上节课学习了实现图像旋转的原理,下课后用matlab实现了一下图像旋转的功能,这里做个记录。
图像旋转的本质利用的是向量的旋转。
矩阵乘法的实质是进行线性变换,因此对一个向量进行旋转操作也可以通过矩阵和向量相乘的方式进行。
【ps:线性代数的本质这个视频很直观地解释了各种线性代数运算的实质,链接:https://www.bilibili.com/video/av6731067】
因为图像都是通过二维矩阵存放的(单通道),所以对图像进行旋转时要先设定一个像素作为旋转轴,然后其他的像素位置可以看作从旋转轴出发的向量。
如图中间的红点为旋转轴,则旋转的实质就是将图中的各个向量进行旋转,然后将旋转前的位置的像素值赋值给旋转后的位置的像素值。
假设有二维向量v = [x ; y],若要进行逆时针旋转角度a。则旋转矩阵R为
旋转后的向量v2 = R * v。
在正式处理过程中可以这么表示,原像素位置记为p,中心点记为c,旋转后像素位置记为pp
则有(pp - c) = R*(p - c)
即
pp = R*(p-c) + c
一共写了三份代码,依次改进了旋转图像的效果。
第一次实现代码
第一次实现代码的思路是正向的思路,也就是把原图进行向量的旋转,找到旋转后的向量的位置,然后将原图的像素值赋值过去即可。
代码实现
% 读入图片
im = imread('1.jpg');
% 求出旋转矩阵
a = 30 / 180 * pi;
R = [cos(a), -sin(a); sin(a), cos(a)];
% 求出图片大小 ch为通道数 h为高度 w为宽度
sz = size(im);
h = sz(1);
w = sz(2);
ch = sz(3);
c = [h; w] / 2;
% 初始化结果图像
im2 = uint8(zeros(h, w, 3));
for k = 1:ch
for i = 1:h
for j = 1:w
p = [i; j];
% round为四舍五入
pp = round(R*(p-c)+c);
if (pp(1) >= 1 && pp(1) <= h && pp(2) >= 1 && pp(2) <= w)
im2(pp(1), pp(2), k) = im(i, j, k);
end
end
end
end
% 显示图像
figure;
imshow(im2);
结果显示
分析:可以看到有这么几个瑕疵,首先是图像的大小和原图一样导致边缘被裁剪了,其次是图像中会出现很多噪声很多杂点,出现杂点的原因是从原图旋转后的像素位置在原图可能找不到,解决方法是用逆向思维,从目标图片反向旋转到原图进行像素查找。
第二次实现代码
这次我先算出了旋转后要显示完整图像所需的画布大小,然后像素赋值的顺序反过来,从目标图片反向旋转到原图进行像素查找。此外还要注意,在改变目标画布大小后,图像中心点即旋转轴改变了,要单独进行计算。
% 读入图片
im = imread('1.jpg');
% 求出旋转矩阵
a = 30 / 180 * pi;
R = [cos(a), -sin(a); sin(a), cos(a)];
R = R'; % 求出旋转矩阵的逆矩阵进行逆向查找
% 计算原图大小
sz = size(im);
h = sz(1);
w = sz(2);
ch = sz(3);
c1 = [h; w] / 2;
% 计算显示完整图像需要的画布大小
hh = floor(w*sin(a)+h*cos(a))+1;
ww = floor(w*cos(a)+h*sin(a))+1;
c2 = [hh; ww] / 2;
% 初始化目标画布
im2 = uint8(ones(hh, ww, 3)*128);
for k = 1:ch
for i = 1:hh
for j = 1:ww
p = [i; j];
pp = round(R*(p-c2)+c1);
% 逆向进行像素查找
if (pp(1) >= 1 && pp(1) <= h && pp(2) >= 1 && pp(2) <= w)
im2(i, j, k) = im(pp(1), pp(2), k);
end
end
end
end
% 显示图像
figure;
imshow(im2);
结果显示
分析:可以看到噪声已经消失,同时显示的也是完整的旋转图像。但是这里的插值方式十分简陋,是直接用四舍五入的,还可以再次改进,通过使用线性插值的方法:https://blog.csdn.net/qq_34586921/article/details/84192850
第三次代码实现
和第二次代码实现比起来就是插值方法改变了,其他的都没有改变,插值方法改变后旋转后的图像质量更好了。
% 读入图片
im = imread('1.jpg');
% 求出旋转矩阵
a = 30 / 180 * pi;
R = [cos(a), -sin(a); sin(a), cos(a)];
R = R'; % 求出旋转矩阵的逆矩阵进行逆向查找
% 计算原图大小
sz = size(im);
h = sz(1);
w = sz(2);
ch = sz(3);
c1 = [h; w] / 2;
% 计算显示完整图像需要的画布大小
hh = floor(w*sin(a)+h*cos(a))+1;
ww = floor(w*cos(a)+h*sin(a))+1;
c2 = [hh; ww] / 2;
% 初始化目标画布
im2 = uint8(ones(hh, ww, 3)*128);
for k = 1:ch
for i = 1:hh
for j = 1:ww
p = [i; j];
pp = (R*(p-c2)+c1);
mn = floor(pp);
ab = pp - mn;
a = ab(1);
b = ab(2);
m = mn(1);
n = mn(2);
% 线性插值方法
if (pp(1) >= 2 && pp(1) <= h-1 && pp(2) >= 2 && pp(2) <= w-1)
im2(i, j, k) = (1-a)*(1-b)*im(m, n, k) + a*(1-b)*im(m+1, n, k)...
+ (1-a)*b*im(m, n, k) + a*b*im(m, n, k);
end
end
end
end
% 显示图像
figure;
imshow(im2);
结果显示:
学习不息,继续加油