前言
上節課學習了實現圖像旋轉的原理,下課後用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);
結果顯示:
原圖: 旋轉後:
總結
學習不息,繼續加油