熱線電話(huà):0755-23712116
郵箱:contact@shuangyi-tech.com
地址:深圳市寶安區(qū)沙井街道后亭茅洲山工業(yè)園工業(yè)大廈全至科技創(chuàng)新園科創(chuàng)大廈2層2A
仿射變換和透視變換更直觀的叫法可以叫做「平面變換」和「空間變換」或者「二維坐標(biāo)變換」和「三維坐標(biāo)變換」。一個(gè)是二維坐標(biāo)(x,y),一個(gè)是三維坐標(biāo)(x,y,z)。也就是:
仿射變換:
透視變換:
??從另一個(gè)角度也能說(shuō)明三維變換和二維變換的意思,仿射變換的方程組有6個(gè)未知數(shù),所以要求解就需要找到3組映射點(diǎn),三個(gè)點(diǎn)剛好確定一個(gè)平面。透視變換的方程組有8個(gè)未知數(shù),所以要求解就需要找到4組映射點(diǎn),四個(gè)點(diǎn)就剛好確定了一個(gè)三維空間。
??仿射變換和透視變換的數(shù)學(xué)原理也不需要深究,其計(jì)算方法為坐標(biāo)向量和變換矩陣的乘積,換言之就是矩陣運(yùn)算。在應(yīng)用層面,放射變換是圖像基于3個(gè)固定頂點(diǎn)的變換,如圖1.1所示:
??圖中紅點(diǎn)即為固定頂點(diǎn),在變換先后固定頂點(diǎn)的像素值不變,圖像整體則根據(jù)變換規(guī)則進(jìn)行變換同理,透視變換是圖像基于4個(gè)固定頂點(diǎn)的變換,如圖1.2所示:
??在OpenCV中,放射變換和透視變換均有封裝好的函數(shù),分別為:
void warpAffine(InputArray src, OutputArray dst, InputArray M, Size dsize, int flags=INTER_LINEAR, int borderMode=BORDER_CONSTANT, const Scalar& borderValue=Scalar())
與
void warpPerspective(InputArray src, OutputArray dst, InputArray M, Size dsize, int flags=INTER_LINEAR, int borderMode=BORDER_CONSTANT, const Scalar& borderValue=Scalar())
??兩種變換函數(shù)形式完全相同,因此以仿射變換為例:
void warpAffine(InputArray src, OutputArray dst, InputArray M, Size dsize, int flags=INTER_LINEAR, int borderMode=BORDER_CONSTANT, const Scalar& borderValue=Scalar()) 參數(shù)InputArray src:輸入變換前的圖像; 參數(shù)OutputArray dst:輸出變換后圖像,需要初始化一個(gè)空矩陣用來(lái)保存結(jié)果,不用設(shè)定矩陣尺寸; 參數(shù)Size dsize:設(shè)置輸出圖像大?。? 參數(shù)int flags=INTER_LINEAR:設(shè)置插值方式,默認(rèn)方式為線性插值; 后兩個(gè)參數(shù)不常用,在此不贅述。
關(guān)于生成變換矩陣InputArray M的函數(shù)getAffineTransform():
Mat getAffineTransform(const Point2f* src, const Point2f* dst) 參數(shù)const Point2f* src:原圖的三個(gè)固定頂點(diǎn) 參數(shù)const Point2f* dst:目標(biāo)圖像的三個(gè)固定頂點(diǎn) 返回值:Mat型變換矩陣,可直接用于warpAffine()函數(shù) 注意,頂點(diǎn)數(shù)組長(zhǎng)度超過(guò)3個(gè),則會(huì)自動(dòng)以前3個(gè)為變換頂點(diǎn);數(shù)組可用Point2f[]或Point2f*表示
??示例代碼如下:
//讀取原圖 Mat I = imread("..//img.jpg"); //設(shè)置空矩陣用于保存目標(biāo)圖像 Mat dst; //設(shè)置原圖變換頂點(diǎn) Point2f AffinePoints0[3] = { Point2f(100, 50), Point2f(100, 390), Point2f(600, 50) }; //設(shè)置目標(biāo)圖像變換頂點(diǎn) Point2f AffinePoints1[3] = { Point2f(200, 100), Point2f(200, 330), Point2f(500, 50) }; //計(jì)算變換矩陣 Mat Trans = getAffineTransform(AffinePoints0, AffinePoints1); //矩陣仿射變換 warpAffine(I, dst, Trans, Size(I.cols, I.rows)); //分別顯示變換先后圖像進(jìn)行對(duì)比 imshow("src", I); imshow("dst", dst); waitKey();
??同理,透視變換與仿射變換函數(shù)類(lèi)似:
void warpPerspective(InputArray src, OutputArray dst, InputArray M, Size dsize, int flags=INTER_LINEAR, int borderMode=BORDER_CONSTANT, const Scalar& borderValue=Scalar())
??生成變換矩陣函數(shù)為:
Mat getPerspectiveTransform(const Point2f* src, const Point2f* dst)
??注意透視變換頂點(diǎn)為4個(gè)。
??兩種變換完整代碼及結(jié)果比較:
#include <iostream> #include <opencv.hpp> using namespace std; using namespace cv; Mat AffineTrans(Mat src, Point2f* scrPoints, Point2f* dstPoints) { Mat dst; Mat Trans = getAffineTransform(scrPoints, dstPoints); warpAffine(src, dst, Trans, Size(src.cols, src.rows), CV_INTER_CUBIC); return dst; } Mat PerspectiveTrans(Mat src, Point2f* scrPoints, Point2f* dstPoints) { Mat dst; Mat Trans = getPerspectiveTransform(scrPoints, dstPoints); warpPerspective(src, dst, Trans, Size(src.cols, src.rows), CV_INTER_CUBIC); return dst; } void main() { Mat I = imread("..//img.jpg"); //700*438 Point2f AffinePoints0[4] = { Point2f(100, 50), Point2f(100, 390), Point2f(600, 50), Point2f(600, 390) }; Point2f AffinePoints1[4] = { Point2f(200, 100), Point2f(200, 330), Point2f(500, 50), Point2f(600, 390) }; Mat dst_affine = AffineTrans(I, AffinePoints0, AffinePoints1); Mat dst_perspective = PerspectiveTrans(I, AffinePoints0, AffinePoints1); for (int i = 0; i < 4; i++) { circle(I, AffinePoints0[i], 2, Scalar(0, 0, 255), 2); circle(dst_affine, AffinePoints1[i], 2, Scalar(0, 0, 255), 2); circle(dst_perspective, AffinePoints1[i], 2, Scalar(0, 0, 255), 2); } imshow("origin", I); imshow("affine", dst_affine); imshow("perspective", dst_perspective); waitKey(); }??可以看出,仿射變換以3個(gè)點(diǎn)為基準(zhǔn)點(diǎn),即使數(shù)組長(zhǎng)度為4也僅取前3個(gè)點(diǎn)作為基準(zhǔn)點(diǎn);透視變換以4個(gè)點(diǎn)為基準(zhǔn)點(diǎn),兩種變換結(jié)果不相同。應(yīng)根據(jù)實(shí)際情況判斷使用哪種變換方式更佳。