日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【OpenCV】图像几何变换:旋转,缩放,斜切

發(fā)布時間:2024/1/17 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【OpenCV】图像几何变换:旋转,缩放,斜切 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

2019獨角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>

幾何變換

幾何變換可以看成圖像中物體(或像素)空間位置改變,或者說是像素的移動。

幾何運算需要空間變換和灰度級差值兩個步驟的算法,像素通過變換映射到新的坐標(biāo)位置,新的位置可能是在幾個像素之間,即不一定為整數(shù)坐標(biāo)。這時就需要灰度級差值將映射的新坐標(biāo)匹配到輸出像素之間。最簡單的插值方法是最近鄰插值,就是令輸出像素的灰度值等于映射最近的位置像素,該方法可能會產(chǎn)生鋸齒。這種方法也叫零階插值,相應(yīng)比較復(fù)雜的還有一階和高階插值。

插值算法感覺只要了解就可以了,圖像處理中比較需要理解的還是空間變換。

空間變換

空間變換對應(yīng)矩陣的仿射變換。一個坐標(biāo)通過函數(shù)變換的新的坐標(biāo)位置:

所以在程序中我們可以使用一個2*3的數(shù)組結(jié)構(gòu)來存儲變換矩陣:

以最簡單的平移變換為例,平移(b1,b2)坐標(biāo)可以表示為:

因此,平移變換的變換矩陣及逆矩陣記為:

縮放變換:將圖像橫坐標(biāo)放大(或縮小)sx倍,縱坐標(biāo)放大(或縮小)sy倍,變換矩陣及逆矩陣為:

選擇變換:圖像繞原點逆時針旋轉(zhuǎn)a角,其變換矩陣及逆矩陣(順時針選擇)為:

OpenCV中的圖像變換函數(shù)

基本的放射變換函數(shù):

void cvWarpAffine( const CvArr* src,//輸入圖像CvArr* dst, //輸出圖像const CvMat* map_matrix, //2*3的變換矩陣int flags=CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS, //插值方法的組合CvScalar fillval=cvScalarAll(0) //用來填充邊界外的值 );

另外一個比較類似的函數(shù)是cvGetQuadrangleSubPix:

void cvGetQuadrangleSubPix( const CvArr* src, //輸入圖像 CvArr* dst, // 提取的四邊形const CvMat* map_matrix //2*3的變換矩陣 );

這個函數(shù)用以提取輸入圖像中的四邊形,并通過map_matrix變換存儲到dst中,與WarpAffine變換意義相同,

?

即對應(yīng)每個點的變換:

WarpAffine與 GetQuadrangleSubPix 不同的在于cvWarpAffine 要求輸入和輸出圖像具有同樣的數(shù)據(jù)類型,有更大的資源開銷(因此對小圖像不太合適)而且輸出圖像的部分可以保留不變。而 cvGetQuadrangleSubPix 可以精確地從8位圖像中提取四邊形到浮點數(shù)緩存區(qū)中,具有比較小的系統(tǒng)開銷,而且總是全部改變輸出圖像的內(nèi)容。

?

實踐:圖像旋轉(zhuǎn)變換(原尺寸)

首先用cvWarpAffine實驗將圖像逆時針旋轉(zhuǎn)degree角度。

//逆時針旋轉(zhuǎn)圖像degree角度(原尺寸) void rotateImage(IplImage* img, IplImage *img_rotate,int degree) {//旋轉(zhuǎn)中心為圖像中心CvPoint2D32f center; center.x=float (img->width/2.0+0.5);center.y=float (img->height/2.0+0.5);//計算二維旋轉(zhuǎn)的仿射變換矩陣float m[6]; CvMat M = cvMat( 2, 3, CV_32F, m );cv2DRotationMatrix( center, degree,1, &M);//變換圖像,并用黑色填充其余值cvWarpAffine(img,img_rotate, &M,CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS,cvScalarAll(0) ); }

逆時針旋轉(zhuǎn)30度結(jié)果:

這里我們將新的圖像還保留原來的圖像尺寸。這樣的效果顯然不太好,我們通過計算相應(yīng)放大圖像尺寸。

?

實踐:圖像旋轉(zhuǎn)變換(保留原圖內(nèi)容,放大尺寸)

需要計算新圖的尺寸,示意圖如下:

所以新圖size為(width*cos(a)+height*sin(a), height*cos(a)+width*sin(a))

//旋轉(zhuǎn)圖像內(nèi)容不變,尺寸相應(yīng)變大 IplImage* rotateImage1(IplImage* img,int degree){double angle = degree * CV_PI / 180.; // 弧度 double a = sin(angle), b = cos(angle); int width = img->width; int height = img->height; int width_rotate= int(height * fabs(a) + width * fabs(b)); int height_rotate=int(width * fabs(a) + height * fabs(b)); //旋轉(zhuǎn)數(shù)組map// [ m0 m1 m2 ] ===> [ A11 A12 b1 ]// [ m3 m4 m5 ] ===> [ A21 A22 b2 ]float map[6];CvMat map_matrix = cvMat(2, 3, CV_32F, map); // 旋轉(zhuǎn)中心CvPoint2D32f center = cvPoint2D32f(width / 2, height / 2); cv2DRotationMatrix(center, degree, 1.0, &map_matrix); map[2] += (width_rotate - width) / 2; map[5] += (height_rotate - height) / 2; IplImage* img_rotate = cvCreateImage(cvSize(width_rotate, height_rotate), 8, 3); //對圖像做仿射變換//CV_WARP_FILL_OUTLIERS - 填充所有輸出圖像的象素。//如果部分象素落在輸入圖像的邊界外,那么它們的值設(shè)定為 fillval.//CV_WARP_INVERSE_MAP - 指定 map_matrix 是輸出圖像到輸入圖像的反變換,cvWarpAffine( img,img_rotate, &map_matrix, CV_INTER_LINEAR | CV_WARP_FILL_OUTLIERS, cvScalarAll(0)); return img_rotate; }

實踐:圖像旋轉(zhuǎn)變換(保留原圖內(nèi)容,放大尺寸)-2

試一下用cvGetQuadrangleSubPix函數(shù):

//旋轉(zhuǎn)圖像內(nèi)容不變,尺寸相應(yīng)變大 IplImage* rotateImage2(IplImage* img, int degree) { double angle = degree * CV_PI / 180.; double a = sin(angle), b = cos(angle); int width=img->width, height=img->height;//旋轉(zhuǎn)后的新圖尺寸int width_rotate= int(height * fabs(a) + width * fabs(b)); int height_rotate=int(width * fabs(a) + height * fabs(b)); IplImage* img_rotate = cvCreateImage(cvSize(width_rotate, height_rotate), img->depth, img->nChannels); cvZero(img_rotate); //保證原圖可以任意角度旋轉(zhuǎn)的最小尺寸int tempLength = sqrt((double)width * width + (double)height *height) + 10; int tempX = (tempLength + 1) / 2 - width / 2; int tempY = (tempLength + 1) / 2 - height / 2; IplImage* temp = cvCreateImage(cvSize(tempLength, tempLength), img->depth, img->nChannels); cvZero(temp); //將原圖復(fù)制到臨時圖像tmp中心cvSetImageROI(temp, cvRect(tempX, tempY, width, height)); cvCopy(img, temp, NULL); cvResetImageROI(temp); //旋轉(zhuǎn)數(shù)組map// [ m0 m1 m2 ] ===> [ A11 A12 b1 ]// [ m3 m4 m5 ] ===> [ A21 A22 b2 ]float m[6]; int w = temp->width; int h = temp->height; m[0] = b; m[1] = a; m[3] = -m[1]; m[4] = m[0]; // 將旋轉(zhuǎn)中心移至圖像中間 m[2] = w * 0.5f; m[5] = h * 0.5f; CvMat M = cvMat(2, 3, CV_32F, m); cvGetQuadrangleSubPix(temp, img_rotate, &M); cvReleaseImage(&temp); return img_rotate; } //旋轉(zhuǎn)圖像內(nèi)容不變,尺寸相應(yīng)變大 IplImage* rotateImage2(IplImage* img, int degree) { double angle = degree * CV_PI / 180.; double a = sin(angle), b = cos(angle); int width=img->width, height=img->height;//旋轉(zhuǎn)后的新圖尺寸int width_rotate= int(height * fabs(a) + width * fabs(b)); int height_rotate=int(width * fabs(a) + height * fabs(b)); IplImage* img_rotate = cvCreateImage(cvSize(width_rotate, height_rotate), img->depth, img->nChannels); cvZero(img_rotate); //保證原圖可以任意角度旋轉(zhuǎn)的最小尺寸int tempLength = sqrt((double)width * width + (double)height *height) + 10; int tempX = (tempLength + 1) / 2 - width / 2; int tempY = (tempLength + 1) / 2 - height / 2; IplImage* temp = cvCreateImage(cvSize(tempLength, tempLength), img->depth, img->nChannels); cvZero(temp); //將原圖復(fù)制到臨時圖像tmp中心cvSetImageROI(temp, cvRect(tempX, tempY, width, height)); cvCopy(img, temp, NULL); cvResetImageROI(temp); //旋轉(zhuǎn)數(shù)組map// [ m0 m1 m2 ] ===> [ A11 A12 b1 ]// [ m3 m4 m5 ] ===> [ A21 A22 b2 ]float m[6]; int w = temp->width; int h = temp->height; m[0] = b; m[1] = a; m[3] = -m[1]; m[4] = m[0]; // 將旋轉(zhuǎn)中心移至圖像中間 m[2] = w * 0.5f; m[5] = h * 0.5f; CvMat M = cvMat(2, 3, CV_32F, m); cvGetQuadrangleSubPix(temp, img_rotate, &M); cvReleaseImage(&temp); return img_rotate; }

?

?

實踐:圖像放射變換(通過三點確定變換矩陣)

在OpenCV 2.3的參考手冊中《opencv_tutorials》介紹了另一種確定變換矩陣的方法,通過三個點變換的幾何關(guān)系映射實現(xiàn)變換。

變換示意圖如下:

即通過三個點就可以確定一個變換矩陣。(矩形變換后一定為平行四邊形)

以下是基于OpenCV 2.3的代碼(需至少2.0以上版本的支持)

int main( ) {Point2f srcTri[3];Point2f dstTri[3];Mat rot_mat( 2, 3, CV_32FC1 );Mat warp_mat( 2, 3, CV_32FC1 );Mat src, warp_dst, warp_rotate_dst;//讀入圖像src = imread( "baboon.jpg", 1 );warp_dst = Mat::zeros( src.rows, src.cols, src.type() );// 用3個點確定A仿射變換srcTri[0] = Point2f( 0,0 );srcTri[1] = Point2f( src.cols - 1, 0 );srcTri[2] = Point2f( 0, src.rows - 1 );dstTri[0] = Point2f( src.cols*0.0, src.rows*0.33 );dstTri[1] = Point2f( src.cols*0.85, src.rows*0.25 );dstTri[2] = Point2f( src.cols*0.15, src.rows*0.7 );warp_mat = getAffineTransform( srcTri, dstTri );warpAffine( src, warp_dst, warp_mat, warp_dst.size() );/// 旋轉(zhuǎn)矩陣Point center = Point( warp_dst.cols/2, warp_dst.rows/2 );double angle = -50.0;double scale = 0.6;rot_mat = getRotationMatrix2D( center, angle, scale );warpAffine( warp_dst, warp_rotate_dst, rot_mat, warp_dst.size() );OpenCV 1.0的形式//IplImage * img=cvLoadImage("baboon.jpg");//IplImage *img_rotate=cvCloneImage(img);//CvMat M =warp_mat;//cvWarpAffine(img,img_rotate, &M,CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS,cvScalarAll(0) );//cvShowImage("Wrap2",img_rotate);namedWindow( "Source", CV_WINDOW_AUTOSIZE );imshow( "Source", src );namedWindow( "Wrap", CV_WINDOW_AUTOSIZE );imshow( "Wrap", warp_dst );namedWindow("Wrap+Rotate", CV_WINDOW_AUTOSIZE );imshow( "Wrap+Rotate", warp_rotate_dst );waitKey(0);return 0; } int main( ) {Point2f srcTri[3];Point2f dstTri[3];Mat rot_mat( 2, 3, CV_32FC1 );Mat warp_mat( 2, 3, CV_32FC1 );Mat src, warp_dst, warp_rotate_dst;//讀入圖像src = imread( "baboon.jpg", 1 );warp_dst = Mat::zeros( src.rows, src.cols, src.type() );// 用3個點確定A仿射變換srcTri[0] = Point2f( 0,0 );srcTri[1] = Point2f( src.cols - 1, 0 );srcTri[2] = Point2f( 0, src.rows - 1 );dstTri[0] = Point2f( src.cols*0.0, src.rows*0.33 );dstTri[1] = Point2f( src.cols*0.85, src.rows*0.25 );dstTri[2] = Point2f( src.cols*0.15, src.rows*0.7 );warp_mat = getAffineTransform( srcTri, dstTri );warpAffine( src, warp_dst, warp_mat, warp_dst.size() );/// 旋轉(zhuǎn)矩陣Point center = Point( warp_dst.cols/2, warp_dst.rows/2 );double angle = -50.0;double scale = 0.6;rot_mat = getRotationMatrix2D( center, angle, scale );warpAffine( warp_dst, warp_rotate_dst, rot_mat, warp_dst.size() );OpenCV 1.0的形式//IplImage * img=cvLoadImage("baboon.jpg");//IplImage *img_rotate=cvCloneImage(img);//CvMat M =warp_mat;//cvWarpAffine(img,img_rotate, &M,CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS,cvScalarAll(0) );//cvShowImage("Wrap2",img_rotate);namedWindow( "Source", CV_WINDOW_AUTOSIZE );imshow( "Source", src );namedWindow( "Wrap", CV_WINDOW_AUTOSIZE );imshow( "Wrap", warp_dst );namedWindow("Wrap+Rotate", CV_WINDOW_AUTOSIZE );imshow( "Wrap+Rotate", warp_rotate_dst );waitKey(0);return 0; }

變換結(jié)果:

?

轉(zhuǎn)載請注明出處:http://blog.csdn.net/xiaowei_cqu/article/details/7616044
實驗代碼下載:http://download.csdn.net/detail/xiaowei_cqu/4339856

寫在最后的一點點閑話

之前一直用的2.1的版本,后來裝了2.3,只是聽說2.3很強大,但我剛開始學(xué),用的也基礎(chǔ),完全沒感覺出不同。直到今天忽然看到了2.3的手冊,才發(fā)現(xiàn)從2.0開始函數(shù)和基本結(jié)構(gòu)都有了很大的改變,而我一直還是用的1.0風(fēng)格的函數(shù)(比如cvMat,cvLoadImage)。我的兩個學(xué)習(xí)工具《Learnning OpenCV》和《OpenCV中文參考手冊》都是基于1.0的,這也是我到今天才看到Mat,然后直接被驚艷到了。

別人總結(jié)出來的東西能幫助我們在一開始迅速入門,但要學(xué)深,學(xué)精,終歸還是要自己去努力挖的。

?

?

?

轉(zhuǎn)載于:https://my.oschina.net/u/1426828/blog/678657

總結(jié)

以上是生活随笔為你收集整理的【OpenCV】图像几何变换:旋转,缩放,斜切的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。