主要還是考慮面試的時候會不會用到斤富,剛才好好看了下旋轉(zhuǎn)的這個思路淤袜,其實和圖像縮放的思路差不多的,主要的問題是要找到坐標的映射方式持偏。
因為還是包含了一部分的公式砍的,所以我再word里寫好然后截圖上來吧痹筛。
(我錯了,簡書已經(jīng)支持輸入公式了挨约,latex語法就好味混,這是寫完之后才發(fā)現(xiàn)的!!)
圖像旋轉(zhuǎn)的變換公式。
這個實際上很簡單诫惭,主要是一些三角函數(shù)方面的推導:
假設(x1,y1)
旋轉(zhuǎn)a
到達(x2,y2)
,那么根據(jù)上圖可以寫出下面的式子:
展開:
替換蔓挖,得到后向映射公式:
由這個式子也很容易解出前向映射的公式:
這便是全部的公式了夕土,圖像旋轉(zhuǎn)是可以用矩陣來表示的,我們把后向映射表示出來:
圖像旋轉(zhuǎn)實現(xiàn)的思路。
其實只要是圖像變換怨绣,無論是旋轉(zhuǎn)還是放大縮小角溃,思路都是基本相同的,就是要尋找一種映射關系篮撑,前向映射是由原圖映射到目標圖减细,后向映射是從目標圖映射到原圖,就寫程序和復雜度來說赢笨,后向映射的復雜度更低未蝌,寫起來也更容易理解。
后向映射的主要思路:
- 建立目標圖茧妒。
- 對于目標圖中的每一點萧吠,映射到原圖中的一個點,這個點可能是不存在的(比如小數(shù)坐標)桐筏,所以需要進行插值處理纸型。
- 插值重建目標圖。
對于旋轉(zhuǎn)來說梅忌,一般我們習慣繞著中心點進行旋轉(zhuǎn)狰腌,所以還要進行坐標變換。
C++實現(xiàn)牧氮。
借助了opencv的Mat數(shù)據(jù)類型琼腔,主要的還是希望用到其索引的方式,把重點放在旋轉(zhuǎn)本身蹋笼,如果給定的是數(shù)組類型的圖像展姐,那么只需要根據(jù)行,列剖毯,通道這三參數(shù)進行變換即可圾笨。
- 讀入圖像,并且進行坐標變換逊谋。
Mat img = imread("2.jpg");
cout << "原圖尺寸:" << img.size() << endl;
imshow("source_img", img);
double angle =-10/180.0f*PI; //這里就是除的180少寫個f擂达,以至于兩個int相除肯定是不對的
int height = img.rows;
int width = img.cols;
//四個定點在旋轉(zhuǎn)坐標系中的位置
// 1 2
// 3 4
int SrcX1 = -width / 2;
int SrcY1 = height / 2;
int SrcX2 = width / 2;
int SrcY2 = height / 2;
int SrcX3 = -width / 2;
int SrcY3 = -height / 2;
int SrcX4 = width / 2;
int SrcY4 = -height / 2;
double cosAn = std::cos(angle);
double sinAn = std::sin(angle);
這個坐標變換是為了計算目標圖像的最小尺寸,這里使用前向映射,由原圖映射到目標圖:
- 計算目標尺寸并開辟存儲空間胶滋。
int DstX1 = (int)(SrcX1*cosAn - SrcY1*sinAn +0.5);
int DstY1 = (int)(SrcX1*sinAn + SrcY1*cosAn +0.5);
int DstX2 = (int)(SrcX2*cosAn - SrcY2*sinAn +0.5);
int DstY2 = (int)(SrcX2*sinAn + SrcY2*cosAn +0.5);
int DstX3 = (int)(SrcX3*cosAn - SrcY3*sin(angle)+0.5);
int DstY3 = (int)(SrcX3*sinAn + SrcY3*cosAn +0.5);
int DstX4 = (int)(SrcX4*cosAn - SrcY4*sinAn + 0.5);
int DstY4 = (int)(SrcX4*sinAn + SrcY4*cosAn + 0.5);
int DstWidth = max(abs(DstX1 - DstX4), abs(DstX2 - DstX3))+1;
int DstHeight = max(abs(DstY1 - DstY4), abs(DstY2 - DstY3))+1;
//旋轉(zhuǎn)后的最合適的寬度和高度
Mat Dst=Mat::zeros(DstHeight,DstWidth, img.type());
//Dst.create(DstHeight, DstWidth, img.type()); //創(chuàng)建DST圖像
加0.5的目的是四舍五入板鬓。
- 后向映射,插值處理究恤,為了簡單這里直接用的最鄰近插值俭令。
坐標變換是重點:
D2S_x = round(double(i - DstWidth / 2.0f)* cosAn + double(j - DstHeight / 2.0f)*sinAn + width / 2.0f);
D2S_y = round(-double(i - DstWidth / 2.0f)*sinAn + double(j - DstHeight / 2.0f)* cosAn + height / 2.0f);
四步走:
- 變換原點到中心。
- 后向映射找對應原圖中的坐標(可能是小數(shù)部宿,也是原點在中心)
- 映射后的點變換原點到原圖左上角抄腔。
- 插值瓢湃,這里直接用的round取四舍五入,就是最鄰近插值了赫蛇。
//映射回去的原圖中的坐標
int D2S_x=0;
int D2S_y=0;
for (int i = 0; i < DstWidth; i++)
{
for (int j = 0; j < DstHeight; j++)
{
D2S_x = round(double(i - DstWidth / 2.0f)* cosAn + double(j - DstHeight / 2.0f)*sinAn + width / 2.0f);
D2S_y = round(-double(i - DstWidth / 2.0f)*sinAn + double(j - DstHeight / 2.0f)* cosAn + height / 2.0f);
if (Dst.channels() == 1)
{
if (D2S_x < 0 || D2S_x >= width || D2S_y < 0 || D2S_y >= height)
Dst.at<uchar>(j, i) = 0;
else
Dst.at<uchar>(j, i) = img.at<uchar>(D2S_y,D2S_x);
}
else if (Dst.channels() == 3)
{
if (D2S_x < 0 || D2S_x >= width || D2S_y < 0 || D2S_y >= height)
Dst.at<Vec3b>(j, i) = Vec3b(0, 0, 0);
else
Dst.at<Vec3b>(j, i) = img.at<Vec3b>(D2S_y,D2S_x);
}
}
}
遇見的坑绵患。
- 整型除整型得到的還是整型。
這種錯誤按理說不該犯悟耘,但是matlab寫多了就是會犯這種錯誤落蝙。如果需得到double型的數(shù)據(jù),記得加上.f
暂幼。 - Mat數(shù)據(jù)坐標系統(tǒng)筏勒。
at
操作符里面的是先行后列而不是坐標
Mat的坐標系統(tǒng)是橫軸為x,縱軸為y,分別對應列和行粟誓。
這些東西不確定的時候一定要查一下定義奏寨,要不還是很容易出錯。