图像的几何运算

原图:
奥特曼
读取原图(这里我的图片名字是atm.png):

% 先读入图像
I = imread('atm.png');
% imshow(I);

1. 旋转 (imrotate)

我们先说原理,图像旋转的本质是向量的旋转。

矩阵乘法的实质是进行线性变换,因此对一个向量进行旋转操作也可以通过矩阵和向量相乘的方式进行。

因为图像都是通过二维矩阵存放的(单通道),所以对图像进行旋转时要先设定一个像素作为旋转轴,然后其他的像素位置可以看作从旋转轴出发的向量。

假设有二维向量 v = [ x ; y ] v = [x ; y] v=[x;y] ,若要进行逆时针旋转角度 a a a 。则旋转矩阵R为 [ c o s ( a ) − s i n ( a ) s i n ( a ) c o s ( a ) ] \begin{bmatrix} cos(a)& -sin(a)\\ sin(a) & cos(a) \end{bmatrix} [cos(a)sin(a)sin(a)cos(a)]旋转后的向量 v 2 = R ∗ v v2 = R * v v2=Rv 。在正式处理过程中可以这么表示,原像素位置记为 p p p ,中心点记为 c c c ,旋转后像素位置记为 p p pp pp
则有 ( p p − c ) = R ∗ ( p − c ) (pp - c) = R*(p - c) (ppc)=R(pc)

如果通过原图的点来计算新图的点,那么新图的点可能会出现漏算的。

这里我们进行逆向求点,通过新图的点,来找旧图的点,这样就不会漏算了。

这里我设函数名为imrotate_test.m

% 进行旋转
% I是图像,angle是角度
function J = imrotate_test(I,angle)
% 首先获得高和宽,色彩的层数
[height, width, color] = size(I);
angle = angle/180*pi;
R=[cos(angle),-sin(angle);sin(angle),cos(angle)];%旋转矩阵
R=R';%求出旋转矩阵的逆矩阵

c1=[height;width]/2;%原图中心
%计算显示完整图像需要的画布大小
height2=floor(width*sin(angle)+height*cos(angle))+1;
width2=floor(width*cos(angle)+height*sin(angle))+1;
c2=[height2,width2]/2;%求新画布中点

%初始化目标画布
J=uint8(ones(height2,width2,3)*128);
for k = 1:color
    for i = 1:height2
        for j = 1:width2
            p=[i;j];%遍历新图像像素点
            pp=round(R*(p-c2)+c1);%计算在原来图像中的位置
            %逆向进行像素查找
            if(pp(1)>=1&&pp(1)<=height&&pp(2)>=1&&pp(2)<=width)
                J(i,j,k)=I(pp(1),pp(2),k);
            end
        end
    end
end
%显示图像
figure;
imshow(J);

代码:

% 自带的函数
% J1 = imrotate(I,45);
J1 = imrotate_test(I,45); % 进行旋转

运行结果:

旋转后的代码

2. 缩放(imresize)

这个很好理解就不解释了。

这里我用到逆向求点,通过新图找旧图的点

这里我设函数名为imresize_test.m

% 进行放大缩小
% I:图像 ,r:比例
function J = imresize_test(I,r)
% 首先获得高和宽,色彩的层数
[height, width, color] = size(I);
% 新的图层
height2=floor(height*r)+1;
width2=floor(width*r)+1;

%初始化目标画布
J=uint8(ones(height2,width2,3)*128);
for k = 1:color
    for i = 1:height2
        for j = 1:width2
            p=[i;j];%遍历旧图像像素点
            pp=round(p/r);%计算在原来图像中的位置
            %这里仍然使用round函数,但结果会比方案一好得多
            %逆向进行像素查找
            if(pp(1)>=1&&pp(1)<=height&&pp(2)>=1&&pp(2)<=width)
                J(i,j,k)=I(pp(1),pp(2),k);
            end
        end
    end
end

%显示图像
figure;
imshow(J);

代码:

% 自带函数
% J2 = imresize(I,1/3);
J2 = imresize_test(I,1/3);

运行结果:

缩小

3. 裁剪 (imcrop)

直接求点,直接上代码!!!(参数与自带的函数不同)

这里我设函数名为imcrop_test.m

% 进行裁剪
% w:水平方向的两个值,h:竖直方向的两个值
function J = imcrop_test(I,w,h)

% 首先获得高和宽,色彩的层数
[height, width, color] = size(I);
if w(1)>w(2)
    a = w(2);
    w(2) = w(1);
    w(1) = a;
end
if h(1)>h(2)
    a = h(2);
    h(2) = h(1);
    h(1) = a;
end
if w(2)>width||h(2)>height||w(1)<=0||h(1)<=0
    return
end
% 新的图层
height2=h(2)-h(1);
width2=w(2)-w(1);

%初始化目标画布
J=uint8(ones(height2,width2,3)*128);
for k = 1:color
    for i = 1:height2
        for j = 1:width2
            pp=[i+h(1);j+w(1)];
            J(i,j,k)=I(pp(1),pp(2),k);
        end
    end
end

%显示图像
figure;
imshow(J);

代码:

% 自带函数
% J3 = imcrop(I,[95,80,300,300]); % 略有不同,参数:[左上角坐标,两个截取长度]
J3 = imcrop_test(I,[95,398],[80,379]);

运行结果:
裁剪

4. 镜像变换(flip,flipdim也可以)

水平变换,就是左右点关于中线交换

这里我设函数名为flip_test.m

% I是图像
% a是选择的变化 1是水平变换 2是垂直变换 3是对角变换
function J = flip_test(I,a)
% 首先获得高和宽,色彩的层数
[height, width, color] = size(I);
%初始化目标画布
J=uint8(ones(height,width,color)*128);
if a==1 % 水平
    for k = 1:color
        for i = 1:height
            for j = 1:width
                pp=round(p/r);%计算在原来图像中的位置
                %这里仍然使用round函数,但结果会比方案一好得多
                %逆向进行像素查找
                if(pp(1)>=1&&pp(1)<=height&&pp(2)>=1&&pp(2)<=width)
                    J(i,j,k)=I(pp(1),pp(2),k);
            end
            end
        end
    end
elseif a==2 % 垂直
    for k = 1:color
        for i = 1:height
            for j = 1:width
            p=[i;j];%遍历新图像像素点
            J(height-i+1,j,k)=I(p(1),p(2),k);
            end
        end
    end
elseif a==3 % 对角
    for k = 1:color
        for i = 1:height
            for j = 1:width
            p=[i;j];%遍历新图像像素点
            J(height-i+1,width-j+1,k)=I(p(1),p(2),k);
            end
        end
    end
else
    disp('你的输入错误');
    return
end

%显示图像
figure;
imshow(J);
end

代码:

% 自带的函数
% J4 = flip(I,1);% 水平变换
% J5 = flip(I,2);% 垂直变换
% 对角变换我没有找到,但是直接变换两次不就是对角变换了嘛
J4 = flip_test(I,1);
title('水平变换');
J5 = flip_test(I,2);
title('垂直变换');
J6 = flip_test(I,3);
title('对角变换');

运行结果:

水平变换
垂直变换
对角变换

5. 平移

这里我设函数名为translation_test.m

% I为处理图像,x是一个数组,第一个数为右移的长度,第二个为下移的长度
function J = translation_test(I,x)
% 首先获得高和宽,色彩的层数
[height, width, color] = size(I);
%初始化目标画布
J=uint8(ones(height,width,color)*128);
for k = 1:color
    for i = 1:height
        for j = 1:width
            p=[i;j];%遍历旧图像像素点
            pp=[i-x(1);j-x(2)]; %计算在原来图像中的位置
            %这里仍然使用round函数,但结果会比方案一好得多
            %逆向进行像素查找
            if(pp(1)>=1&&pp(1)<=height&&pp(2)>=1&&pp(2)<=width)
                J(i,j,k)=I(pp(1),pp(2),k);
            end
        end
    end
end

%显示图像
figure;
imshow(J);

代码:

% 通过一些自带的函数,可以实现平移
%translate(SE, [y x])在结构元素SE上进行y和x方向的位移 正数对应右移和下移
% se = translate(strel(1),[100 80]);
% J7 = imdilate(I,se);%利用膨胀函数平移图像
J7 = translation_test(I,[-50,-100]);

运行结果:

平移


求赞
创作不易,如果对你有帮助,求求你给我个赞!!!
点赞 + 收藏 + 关注!!!
如有错误与建议,望告知!!!(将于下篇文章更正)
请多多关注我!!!谢谢!!!

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐