1.图像旋转的原理
1.1.旋转矩阵
旋转一幅图像(假设这幅图像大小是矩形的),当然应该从像素点(pixels)开始,在直角座标系中,对点逆时针旋转角度到的变换公式为
那么对图像上的每个点调用这个旋转公式,将旧图像像素点的RGB值搬移到新图像像素点,就可以将图像旋转到任意位置。
但是问题来了,显示屏的像素点是有限的,这意味着显示在显示屏上的像素点座标必须是整数,旋转过后的图像的每个像素点座标难免有非整数的情况,那么这种情况下我们怎么处理呢?
我们不妨假设逆时针旋转旋转后的图形上所有的像素点都是整点,对于旋转后的图形的每个像素点,求旋转前图形的对应像素点的座标,取为逆旋转角度,则旋转后的像素点和旋转前的像素点的对应关系为:
此时不一定为整点,的像素值需要做一定的近似。近似的方法有最近邻插值、双线性插值等等,在这里我们就介绍比较实用且不是很复杂的双线性插值,该插值方法不会产生明显失真现象。
对于像素点的旋转座标函数编写如下:
①座标旋转变换函数:rot.m
function y=rot(p,angle)
%p=[x,y]为角度制
angle=angle*pi/180;%角度制输入进行计算
y=[cos(angle) -sin(angle);sin(angle) cos(angle)]*p';
end
1.2.双线性插值
对于x和y座标非整数的非整点,假设它周围的四个整点座标分别为(构成一个矩形)。假设第一维度是x座标,第二维度是y座标。则
显然,即使为整点,仍然存在这样的四个点使得上式成立。
在下图中,P为非整点,存在4个整点将P点包围在其中,设纵向比例系数,横向比例系数颜色函数在四个整点处的值分比为,则P点的函数值
写成矩阵的形式即为:
对于灰度图像,是一维函数,对于RGB图像,
由于该公式较为复杂,可以单独编写双线性插值函数,输入为一个任意点座标,和图像每个点的像素;输出为该点进行双线性插值后的颜色函数值。但需要主要的是,若给采集的4个周围整点,有其中一个超出了图像边界,考虑到图像边界一般为白色,则以白色为替代。检测点是否在画布内只需要条件判断语句就够了。
②检测是否在画布内的判断程序:isinrect.m
function y=isinrect(plt,rect)
if plt(1)>=rect(1) && plt(1)<=rect(2) && plt(2)>=rect(3) && plt(2)<=rect(4)
y=true;
else
y=false;
end
③双线性插值函数:linear_interp.m
function y=linear_interp(p,img)
%p为需要双线性插值的点座标(向量)
x=p(1);y=p(2);
m=size(img,1);n=size(img,2);
x1=floor(x);x2=ceil(x);y1=floor(y);y2=ceil(y);%[x,y]四周的四个整点
left=x-x1;%距左边线距离
bottom=y-y1;%距底线距离
plt=[x1,y2;x2,y2;x1,y2;x2,y2];
img_rect=[1,m,1,n];%原图像的矩形框
pix_rect=zeros(1,4,3);
for t=1:4
if isinrect(plt(t,:),img_rect)
for color=1:3
pix_rect(1,t,color)=img(plt(t,1),plt(t,2),color);
end
else
pix_rect(1,t,:)=255;%背景色为白色
end
end
pixels=zeros(1,3);%保存该点的三原色的三个像素
pix_rect=reshape(pix_rect,2,2,3);
for color=1:3
pixels(color)=[bottom,1-bottom]*pix_rect(:,:,color)*[1-left;left];%双线性像素插值
end
y=pixels;
end
1.3.像素点匹配
假设图像的点都是整点的情况下,新图像是一个旋转后的矩形,此时需要给新图像定界使得新图像能够包括在一个旋转角度为0°大矩形中。
filename='足球.bmp';%文件的完整路径名
img=imread(filename);%导入图像
% subplot(121)
% imshow(img)%展示原图像
m=size(img,1);n=size(img,2);%统计图像的长和宽
plt=[0,0;m,0;m,n;0,n];%四个顶点座标
for t=1:4
plt(t,:)=ceil(rot(plt(t,:),rot_angle));%三个顶点进行旋转座标变换
end
%新的四个点座标的x和y边界值
left=min(plt(:,1));right=max(plt(:,1));
bottom=min(plt(:,2));top=max(plt(:,2));
M=right-left;N=top-bottom;%获取新的图像大小
new_img=255*ones(M,N,3);%创建新画布
left=min(plt(:,1));right=max(plt(:,1));
bottom=min(plt(:,2));top=max(plt(:,2));
定界完成后,只需要求出新图像定界后每个像素点对应的原图像像素点座标,并按照双线性插值的方法取得像素值,对应到新图像点中,即可求出每个新图像点的像素值,旋转步骤即完成。要注意的是,如果双线性插值遇到了原图像界限外的点,为保证程序不出错,可以直接将界限外的点置为0(相当于非常弱的边缘虚化效果),或者直接将最外层边界整点进行最近邻插值(这种分段算法实现会略微麻烦)。主函数文件代码如下:
④主函数文件:img_process.m
function img_process(rot_angle)
close all
filename='足球.bmp';%文件的完整路径名
img=imread(filename);%导入图像
figure
imshow(img)%展示原图像
m=size(img,1);n=size(img,2);%统计图像的长和宽
plt=[0,0;m,0;m,n;0,n];%四个顶点座标
for t=1:4
plt(t,:)=ceil(rot(plt(t,:),rot_angle));%三个顶点进行旋转座标变换
end
%新的四个点座标的x和y边界值
left=min(plt(:,1));right=max(plt(:,1));
bottom=min(plt(:,2));top=max(plt(:,2));
M=right-left;N=top-bottom;%获取新的图像大小
new_img=255*ones(M,N,3);%创建新画布
for i=1:M
for j=1:N
init_plt=rot([i-1+left,j-1+bottom],-rot_angle);%新图像对应原图像的座标
init_plt=init_plt+1;%还原到Matlab座标系
new_img(i,j,:)=linear_interp(init_plt,img);
end
end
figure
imshow(uint8(new_img))%展示旋转后的新图像(底色为白色)
2.实现效果与说明
将上述标红的4个M文件放在一个文件夹,并更改MATLAB目录为该文件夹,并在该文件夹添加一张名为“足球.bmp”的位图文件,在命令行输入img_process(30)
即可将该图像旋转30°显示,显示效果如下:
原图像 | 新图像(旋转30°) |
---|---|
本文从原理上用MATLAB代码实现了图像的旋转,如果想直接调用MATLAB的函数,请查看imrotate函数的相关说明:
new_img=imrotate(initial_img,angle,method, ‘crop’)
- angle:逆时针旋转的角度,是角度值
- method:该参数为插值方法,其中’bilinear’为双线性插值,可选
- crop:旋转后增大图像