仿射變換是一種二維變換,它可以將一個(gè)二維圖形映射到另一個(gè)二維圖形上,保持了圖形的“形狀”和“大小”不變,但可能會(huì)改變圖形的方向和位置。仿射變換可以用一個(gè)線性變換矩陣來(lái)表示,該矩陣包含了六個(gè)參數(shù),可以進(jìn)行平移、縮放、旋轉(zhuǎn)等操作。通過(guò)原理、函數(shù)和示例進(jìn)行解析,幫助大家理解和使用。
下面我們將依次實(shí)現(xiàn)平移、旋轉(zhuǎn)、縮放和仿射變換等功能,使用C++語(yǔ)言和OpenCV庫(kù)。
原理和函數(shù)
原理
由于矩陣A的最后一行為(0,0,1),所以認(rèn)為A是仿射變換矩陣,變換類型主要包括平移、縮放和旋轉(zhuǎn)。
warpAffine()函數(shù)詳解
warpAffine() 是 OpenCV 庫(kù)中的一個(gè)函數(shù),用于進(jìn)行二維仿射變換。該函數(shù)將輸入圖像映射到輸出圖像,應(yīng)用仿射變換。
函數(shù)原型如下:
void cv::warpAffine(InputArray src, OutputArray dst, InputArray mat, Size dsize = Size(), int flags = INTER_LINEAR, int borderMode = BORDER_CONSTANT, Scalar borderValue = Scalar());
參數(shù)詳解:
src:輸入圖像,必須是單通道或三通道的8位或32位浮點(diǎn)型圖像。
dst:輸出圖像,其大小和類型與輸入圖像相同。
mat:2x3的變換矩陣。
dsize:輸出圖像的大小,如果這個(gè)參數(shù)為 Size() ,則輸出圖像的大小將與輸入圖像相同。
flags:用于指定插值的方法,默認(rèn)為線性插值。可用的選項(xiàng)有 INTER_NEAREST, INTER_LINEAR, INTER_CUBIC 等。
borderMode:用于指定如何處理輸出圖像邊緣的像素,默認(rèn)為常量填充模式??捎玫倪x項(xiàng)有 BORDER_CONSTANT, BORDER_REPLICATE, BORDER_WRAP 等。
borderValue:用于指定填充的邊界值,默認(rèn)為0。
這個(gè)函數(shù)使用仿射變換來(lái)將輸入圖像映射到輸出圖像。仿射變換包括旋轉(zhuǎn)、縮放、平移等操作,但不包括扭曲和剪切。這個(gè)函數(shù)非常有用,特別是在需要將圖像映射到另一個(gè)大小或以特定方式旋轉(zhuǎn)或傾斜圖像時(shí)。
示例
平移
原理
平移變換可以用一個(gè)3x3的變換矩陣來(lái)表示,其中第一行和第二行表示原始圖像的行向量和列向量,第三行表示變換后的行向量和列向量,和原理部分類似,但有些版本只需要設(shè)置一個(gè)2x3的變換矩陣即可,可以省略第三行。在本例中,即定義的是2x3的變換矩陣,我們將原始圖像向右移動(dòng)100個(gè)像素,向下移動(dòng)300個(gè)像素。其中,數(shù)值為正,則向正方向移,數(shù)值為負(fù),則向反向相移。
如上述矩陣轉(zhuǎn)換矩陣,只需要設(shè)置第一行的第三個(gè)元素tx和第二行第三列的ty即可,體現(xiàn)在2行3列的矩陣中如下面運(yùn)行示例中的100和300所示。
運(yùn)行示例
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main() {
Mat src = imread("ceshi.jpg");
if (src.empty()) {
cout << "Could not read the source image" << endl;
return -1;
}
Mat trans_mat = (Mat_<double>(2, 3) << 1, 0, 100, 0, 1, 300);
Mat dst;
warpAffine(src, dst, trans_mat, src.size());
imshow("Source Image", src);
imshow("Affine Transformed Image", dst);
imwrite("pingyi.jpg", dst);
waitKey(0);
return 0;
}
在代碼中,Mat trans_mat = (Mat_<double>(2, 3) << 1, 0, 100, 0, 1, 300);
是定義的一個(gè)2行3列的轉(zhuǎn)換矩陣,第一行是右移100,第二行是下移300。最后,我們使用warpAffine()函數(shù)進(jìn)行仿射變換,將原始圖像映射到輸出圖像中,并顯示原始圖像和變換后的圖像。運(yùn)行可以看到相應(yīng)的效果,如下圖所示。
上面為原圖,下面為平移后的圖像。
縮放
原理
仿射變換中的縮放指的是對(duì)圖像進(jìn)行等比例的放大或縮小。在仿射變換矩陣中,可以通過(guò)設(shè)置第一行和第二行的元素來(lái)控制縮放。
由上面的平移可知,第一行第三列和第二行第三列的數(shù)值是控制平移的,在此處可知,第一行第一列和第二行第二列是用于控制x軸和y軸的縮放比例的,在下面的運(yùn)行示例中也可以看出縮放因子在的位置與此相對(duì)應(yīng)。
縮小示例
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main() {
Mat src = imread("ceshi.jpg");
if (src.empty()) {
cout << "Could not read the source image" << endl;
return -1;
}
// 設(shè)置縮放比例
double scale = 0.5;
// 計(jì)算縮放矩陣
Mat scale_mat = (Mat_<double>(2, 3) << scale, 0, 0, 0, scale, 0);
// 進(jìn)行仿射變換
Mat dst;
warpAffine(src, dst, scale_mat, src.size());
// 顯示原始圖像和變換后的圖像
imshow("Source Image", src);
imshow("Affine Transformed Image", dst);
imwrite("suoxiao.jpg", dst);
waitKey(0);
return 0;
}
在上面的代碼中,我們首先讀取一張名為“ceshi.jpg”的圖像,然后設(shè)置縮放比例為0.5。接著,我們計(jì)算縮放矩陣,其中第一行和第二行的前兩個(gè)元素分別表示水平方向和垂直方向的縮放比例,第三行和第四行的前兩個(gè)元素為0,表示不進(jìn)行平移操作。最后,我們使用warpAffine()函數(shù)進(jìn)行仿射變換,將原始圖像映射到輸出圖像中,并顯示原始圖像和變換后的圖像。變換后的效果如下圖所示。
放大示例
此處,我們把縮放因子設(shè)置為1.5,即放大,
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main() {
Mat src = imread("ceshi.jpg");
if (src.empty()) {
cout << "Could not read the source image" << endl;
return -1;
}
// 設(shè)置縮放比例
double scale = 1.5;
// 計(jì)算縮放矩陣
Mat scale_mat = (Mat_<double>(2, 3) << scale, 0, 0, 0, scale, 0);
// 進(jìn)行仿射變換
Mat dst;
warpAffine(src, dst, scale_mat, src.size());
// 顯示原始圖像和變換后的圖像
imshow("Source Image", src);
imshow("Affine Transformed Image", dst);
imwrite("fangda.jpg", dst);
waitKey(0);
return 0;
}
放大的效果如下圖所示。
相當(dāng)于把圖像一部分放大,但顯示的內(nèi)容比原圖就少了很多,因?yàn)閳D像的總體大小沒(méi)有改變。
旋轉(zhuǎn)
原理
此處,以原點(diǎn)(0,0)坐標(biāo)為中心進(jìn)行,α 是旋轉(zhuǎn)角度。
旋轉(zhuǎn)矩陣的元素是由旋轉(zhuǎn)角度和旋轉(zhuǎn)中心計(jì)算得出的。假設(shè)我們有一個(gè)二維坐標(biāo)系,其中原點(diǎn)是旋轉(zhuǎn)中心,x軸正方向是右方,y軸正方向是上方?,F(xiàn)在我們要將點(diǎn) (x, y) 繞原點(diǎn)逆時(shí)針旋轉(zhuǎn)θ角度,那么旋轉(zhuǎn)矩陣可以表示為:
[ cosα, -sinα, 0 ]
[ sinα, cosα, 0 ]
其中,cosθ 和 sinθ 是使用弧度值計(jì)算得出的。
也可以是如下公式:
若為上述A變換矩陣,則當(dāng)α 為正時(shí),旋轉(zhuǎn)方向?yàn)轫槙r(shí)針;當(dāng) α 為負(fù)時(shí),旋轉(zhuǎn)方向?yàn)槟鏁r(shí)針。
同時(shí),另外一種情況與此相反,如變換矩陣為:
[ cosα, sinα, 0 ]
[ -sinα, cosα, 0 ]
則當(dāng)α 為正時(shí),旋轉(zhuǎn)方向?yàn)槟鏁r(shí)針;當(dāng) α 為負(fù)時(shí),旋轉(zhuǎn)方向?yàn)轫槙r(shí)針。
此外,也可以控制α 的值,如α 為45和為-45逆順情況是相反的。
下面進(jìn)行兩個(gè)示例,分別進(jìn)行順時(shí)針和逆時(shí)針旋轉(zhuǎn)。
順時(shí)針示例
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main() {
Mat src = imread("ceshi.jpg");
if (src.empty()) {
cout << "Could not read the source image" << endl;
return -1;
}
// 設(shè)置旋轉(zhuǎn)角度
double angle = 45;
// 計(jì)算旋轉(zhuǎn)矩陣
Mat rot_mat = (Mat_<double>(2, 3) << cos(angle), -sin(angle), sin(angle), cos(angle), 1, 0);
cout<< cos(angle) <<endl;
cout << -sin(angle) << endl;
cout << sin(angle) << endl;
//rot_mat = rot_mat * 100; // 設(shè)置旋轉(zhuǎn)中心點(diǎn),這里將圖像中心設(shè)置為旋轉(zhuǎn)中心
// 進(jìn)行仿射變換
Mat dst;
warpAffine(src, dst, rot_mat, src.size());
// 顯示原始圖像和變換后的圖像
imshow("Source Image", src);
imshow("Affine Transformed Image", dst);
imwrite("shunshizhen.jpg", dst);
waitKey(0);
return 0;
}
上述代碼中設(shè)置的double angle = 45;
為旋轉(zhuǎn)角度,45為正數(shù),所以是逆時(shí)針旋轉(zhuǎn),同時(shí),(Mat_<double>(2, 3) << cos(angle), -sin(angle), sin(angle), cos(angle), 1, 0);
轉(zhuǎn)換矩陣表示當(dāng)α 為正時(shí),旋轉(zhuǎn)方向?yàn)轫槙r(shí)針;當(dāng) α 為負(fù)時(shí),旋轉(zhuǎn)方向?yàn)槟鏁r(shí)針。
旋轉(zhuǎn)結(jié)果為:
逆時(shí)針示例
(1)通過(guò)改變 α的值,如將上面順時(shí)針的45變?yōu)?45.
(2)改變變換矩陣的順序,如使用Mat rot_mat = (Mat_<double>(2, 3) << cos(angle), sin(angle), -sin(angle), cos(angle), 1, 0);
替換上面代碼中的轉(zhuǎn)換矩陣。
以原點(diǎn)(0,0)為中心,逆時(shí)針旋轉(zhuǎn)45度效果圖如下:文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-751793.html
總結(jié)
我們可以看出,在使用warpAffine()
函數(shù)是比較方便的,通過(guò)使用定義2行3列和通過(guò)上面的例子,可以快速高效的實(shí)現(xiàn)平移、縮放和旋轉(zhuǎn)等功能。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-751793.html
到了這里,關(guān)于【C++】【Opencv】cv::warpAffine()仿射變換函數(shù)詳解,實(shí)現(xiàn)平移、縮放和旋轉(zhuǎn)等功能的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!