opencv 圖像仿射變換 計算仿射變換後對應特徵點的新坐標 圖像旋轉、縮放、平移
常常需要最圖像進行仿射變換,仿射變換後,我們可能需要將原來圖像中的特徵點坐標進行重新計算,獲得原來圖像中例如眼睛瞳孔坐標的新的位置,用於在新得到圖像中繼續利用瞳孔位置坐標。
仿射變換在:http://blog.csdn.net/xiaowei_cqu/article/details/7616044這位大牛的博客中已經介紹的非常清楚。
關於仿射變換的詳細介紹,請見上面鏈接的博客。
我這裡主要介紹如何在已經知道原圖像中若干特徵點的坐標之後,計算這些特徵點進行放射變換之後的坐標,然後做一些補充。
** 在原文中,很多功能函數都是使用的cvXXX,例如cv2DRotationMatrix(center,degree,1,&M); 這些都是老版本的函數,在opencv2以後,應該盡量的使用全新的函數,所以在我的代碼中,都是使用的最新的函數,不再使用 cvMat, 而是全部使用 Mat 類型。 **
1. 特徵點對應的新的坐標計算假設已經有一個原圖像中的特徵點的坐標 CvPoint point; 那麼計算這個point的對應的仿射變換之後在新的圖像中的坐標位置,使用的方法如下函數:
[cpp] view plain copy- //獲取指定像素點放射變換後的新的坐標位置
- CvPointgetPointAffinedPos(constCvPoint&src,constCvPoint¢er,doubleangle)
- {
- CvPointdst;
- intx=src.x-center.x;
- inty=src.y-center.y;
- dst.x=cvRound(x*cos(angle)+y*sin(angle)+center.x);
- dst.y=cvRound(-x*sin(angle)+y*cos(angle)+center.y);
- returndst;
- }
要特別注意的是,在對一個原圖像中的像素的坐標進行計算仿射變換之後的坐標的時候,一定要按照仿射變換的基本原理,將原來的坐標減去仿射變換的旋轉中心的坐標,這樣仿射變換之後得到的坐標再加上仿射變換旋轉中心坐標纔是原坐標在新的仿射變換之後的圖像中的正確坐標。
下面給出計算對應瞳孔坐標旋轉之後的坐標位置的示例代碼:
[cpp] view plain copy
- //AffineTransformation.cpp:Definestheentrypointfortheconsoleapplication.
- //
- #include"stdafx.h"
- #include"stdio.h"
- #include"iostream"
- #include"opencv2/opencv.hpp"
- usingnamespacestd;
- usingnamespacecv;
- //獲取指定像素點放射變換後的新的坐標位置
- CvPointgetPointAffinedPos(constCvPoint&src,constCvPoint¢er,doubleangle);
- MatImageRotate(Mat&src,constCvPoint&_center,doubleangle);
- MatImageRotate2NewSize(Mat&src,constCvPoint&_center,doubleangle);
- int_tmain(intargc,_TCHAR*argv[])
- {
- stringimage_path="D:/lena.jpg";
- Matimg=imread(image_path);
- cvtColor(img,img,CV_BGR2GRAY);
- Matsrc;
- img.copyTo(src);
- CvPointLeye;
- Leye.x=265;
- Leye.y=265;
- CvPointReye;
- Reye.x=328;
- Reye.y=265;
- //drawpupil
- src.at<unsignedchar>(Leye.y,Leye.x)=255;
- src.at<unsignedchar>(Reye.y,Reye.x)=255;
- imshow("src",src);
- //
- CvPointcenter;
- center.x=img.cols/2;
- center.y=img.rows/2;
- doubleangle=15L;
- Matdst=ImageRotate(img,center,angle);
- //計算原特徵點在旋轉後圖像中的對應的坐標
- CvPointl2=getPointAffinedPos(Leye,center,angle*CV_PI/180);
- CvPointr2=getPointAffinedPos(Reye,center,angle*CV_PI/180);
- //drawpupil
- dst.at<unsignedchar>(l2.y,l2.x)=255;
- dst.at<unsignedchar>(r2.y,r2.x)=255;
- //Matdst=ImageRotate2NewSize(img,center,angle);
- imshow("dst",dst);
- waitKey(0);
- return0;
- }
- MatImageRotate(Mat&src,constCvPoint&_center,doubleangle)
- {
- CvPoint2D32fcenter;
- center.x=float(_center.x);
- center.y=float(_center.y);
- //計算二維旋轉的仿射變換矩陣
- MatM=getRotationMatrix2D(center,angle,1);
- //rotate
- Matdst;
- warpAffine(src,dst,M,cvSize(src.cols,src.rows),CV_INTER_LINEAR);
- returndst;
- }
- //獲取指定像素點放射變換後的新的坐標位置
- CvPointgetPointAffinedPos(constCvPoint&src,constCvPoint¢er,doubleangle)
- {
- CvPointdst;
- intx=src.x-center.x;
- inty=src.y-center.y;
- dst.x=cvRound(x*cos(angle)+y*sin(angle)+center.x);
- dst.y=cvRound(-x*sin(angle)+y*cos(angle)+center.y);
- returndst;
- }
這裡,我們先通過手工找到瞳孔坐標,然後計算在圖像旋轉之後瞳孔的坐標。
運行結果如圖:
原圖像
旋轉之後的圖像:
2. 旋轉中心對於旋轉的影響
然後我們看看仿射變換旋轉點的選擇對於旋轉之後的圖像的影響,一般情況下,我們選擇圖像的中心點作為仿射變換的旋轉中心,獲得的旋轉之後的圖像與原圖像大小一樣。
計算代碼:
[cpp] view plain copy
- int_tmain(intargc,_TCHAR*argv[])
- {
- stringimage_path="D:/lena.jpg";
- Matimg=imread(image_path);
- cvtColor(img,img,CV_BGR2GRAY);
- Matsrc;
- img.copyTo(src);
- CvPointLeye;
- Leye.x=265;
- Leye.y=265;
- CvPointReye;
- Reye.x=328;
- Reye.y=265;
- //drawpupil
- src.at<unsignedchar>(Leye.y,Leye.x)=255;
- src.at<unsignedchar>(Reye.y,Reye.x)=255;
- imshow("src",src);
- //
- /*CvPointcenter;
- center.x=img.cols/2;
- center.y=img.rows/2;*/
- CvPointcenter;
- center.x=0;
- center.y=0;
- doubleangle=15L;
- Matdst=ImageRotate(img,center,angle);
- //計算原特徵點在旋轉後圖像中的對應的坐標
- CvPointl2=getPointAffinedPos(Leye,center,angle*CV_PI/180);
- CvPointr2=getPointAffinedPos(Reye,center,angle*CV_PI/180);
- //drawpupil
- dst.at<unsignedchar>(l2.y,l2.x)=255;
- dst.at<unsignedchar>(r2.y,r2.x)=255;
- //Matdst=ImageRotate2NewSize(img,center,angle);
- imshow("dst",dst);
- waitKey(0);
- return0;
- }
這裡繞著(0,0)點進行旋轉,旋轉之後的圖像:
繞著左下角旋轉:
[cpp] view plain copy- CvPointcenter;
- center.x=0;
- center.y=img.rows;
旋轉之後的圖像:
3. 縮放因子對於旋轉圖像的影響
上面我們的代碼都沒有添加縮放信息,現在對上面的代碼進行稍加修改,添加縮放參數,然後看一下如何計算對應的新的坐標。
[cpp] view plain copy- #include"stdafx.h"
- #include"stdio.h"
- #include"iostream"
- #include"opencv2/opencv.hpp"
- usingnamespacestd;
- usingnamespacecv;
- //獲取指定像素點放射變換後的新的坐標位置
- CvPointgetPointAffinedPos(constCvPoint&src,constCvPoint¢er,doubleangle,doublescale);
- MatImageRotate(Mat&src,constCvPoint&_center,doubleangle,doublescale);
- MatImageRotate2NewSize(Mat&src,constCvPoint&_center,doubleangle,doublescale);
- int_tmain(intargc,_TCHAR*argv[])
- {
- stringimage_path="D:/lena.jpg";
- Matimg=imread(image_path);
- cvtColor(img,img,CV_BGR2GRAY);
- doublescale=0.5;
- Matsrc;
- img.copyTo(src);
- CvPointLeye;
- Leye.x=265;
- Leye.y=265;
- CvPointReye;
- Reye.x=328;
- Reye.y=265;
- //drawpupil
- src.at<unsignedchar>(Leye.y,Leye.x)=255;
- src.at<unsignedchar>(Reye.y,Reye.x)=255;
- imshow("src",src);
- //
- CvPointcenter;
- center.x=img.cols/2;
- center.y=img.rows/2;
- doubleangle=15L;
- Matdst=ImageRotate(img,center,angle,scale);
- //計算原特徵點在旋轉後圖像中的對應的坐標
- CvPointl2=getPointAffinedPos(Leye,center,angle*CV_PI/180,scale);
- CvPointr2=getPointAffinedPos(Reye,center,angle*CV_PI/180,scale);
- //drawpupil
- dst.at<unsignedchar>(l2.y,l2.x)=255;
- dst.at<unsignedchar>(r2.y,r2.x)=255;
- //Matdst=ImageRotate2NewSize(img,center,angle);
- imshow("dst",dst);
- waitKey(0);
- return0;
- }
- MatImageRotate(Mat&src,constCvPoint&_center,doubleangle,doublescale)
- {
- CvPoint2D32fcenter;
- center.x=float(_center.x);
- center.y=float(_center.y);
- //計算二維旋轉的仿射變換矩陣
- MatM=getRotationMatrix2D(center,angle,scale);
- //rotate
- Matdst;
- warpAffine(src,dst,M,cvSize(src.cols,src.rows),CV_INTER_LINEAR);
- returndst;
- }
- //獲取指定像素點放射變換後的新的坐標位置
- CvPointgetPointAffinedPos(constCvPoint&src,constCvPoint¢er,doubleangle,doublescale)
- {
- CvPointdst;
- intx=src.x-center.x;
- inty=src.y-center.y;
- dst.x=cvRound(x*cos(angle)*scale+y*sin(angle)*scale+center.x);
- dst.y=cvRound(-x*sin(angle)*scale+y*cos(angle)*scale+center.y);
- returndst;
- }
當縮放尺度為0.5的時候,程序的運行結果如圖:
4. 根據旋轉與縮放尺度獲得與原始圖像大小不同的圖像大小(新的合適的大小)
上面的計算中,一直都是放射變換之後計算得到的圖像和原始圖像一樣大,但是因為旋轉、縮放之後圖像可能會變大或者變小,我們再次對上面的代碼進行修改,這樣在獲得仿射變換之後的圖像前,需要重新計算生成的圖像的大小。
計算方法:
[cpp] view plain copy- doubleangle2=angle*CV_PI/180;
- intwidth_=src.cols;
- intheight=src.rows;
- doublealpha=cos(angle2)*scale;
- doublebeta=sin(angle2)*scale;
- intnew_width_=(int)(width*fabs(alpha)+height*fabs(beta));
- intnew_height=(int)(width*fabs(beta)+height*fabs(alpha));
另外,因為我們的圖像旋轉是按照原圖像的中心,所以當獲取到圖像的仿射變換矩陣之後,我們需要根據新生成的圖像的大小,給仿射變換矩陣添加平移信息。
或者可以這麼說,我們新計算得到的圖像的大小,讓原始圖像繞著新的圖像大小的中心進行旋轉。
[cpp] view plain copy
- //計算二維旋轉的仿射變換矩陣
- MatM=getRotationMatrix2D(center,angle,scale);
- //給計算得到的旋轉矩陣添加平移
- M.at<double>(0,2)+=(int)((new_width-width)/2);
- M.at<double>(1,2)+=(int)((new_height-height)/2);
然後另外需要注意的是,如果你在原始圖像中有一些特徵點的坐標,這些特徵點的坐標映射到新的圖像上的時候,需要在以前的方法的基礎上增加平移信息。[cpp] view plain copy
- //獲取指定像素點放射變換後的新的坐標位置
- CvPointgetPointAffinedPos(Mat&src,Mat&dst,constCvPoint&src_p,constCvPoint¢er,doubleangle,doublescale)
- {
- doublealpha=cos(angle)*scale;
- doublebeta=sin(angle)*scale;
- intwidth_=src.cols;
- intheight=src.rows;
- CvPointdst_p;
- intx=src_p.x-center.x;
- inty=src_p.y-center.y;
- dst_p.x=cvRound(x*alpha+y*beta+center.x);
- dst_p.y=cvRound(-x*beta+y*alpha+center.y);
- intnew_width_=dst.cols;
- intnew_height=dst.rows;
- intmovx=(int)((new_width-width)/2);
- intmovy=(int)((new_height-height)/2);
- dst_p.x+=movx;
- dst_p.y+=movy;
- returndst_p;
- }
我們仿射變換函數代碼:
[cpp] view plain copy- MatImageRotate2NewSize(Mat&src,constCvPoint&_center,doubleangle,doublescale)
- {
- doubleangle2=angle*CV_PI/180;
- intwidth_=src.cols;
- intheight=src.rows;
- doublealpha=cos(angle2)*scale;
- doublebeta=sin(angle2)*scale;
- intnew_width_=(int)(width*fabs(alpha)+height*fabs(beta));
- intnew_height=(int)(width*fabs(beta)+height*fabs(alpha));
- CvPoint2D32fcenter;
- center.x=float(width/2);
- center.y=float(height/2);
- //計算二維旋轉的仿射變換矩陣
- MatM=getRotationMatrix2D(center,angle,scale);
- //給計算得到的旋轉矩陣添加平移
- M.at<double>(0,2)+=(int)((new_width-width)/2);
- M.at<double>(1,2)+=(int)((new_height-height)/2);
- //rotate
- Matdst;
- warpAffine(src,dst,M,cvSize(new_width,new_height),CV_INTER_LINEAR);
- returndst;
- }
主函數:[cpp] view plain copy
- int_tmain(intargc,_TCHAR*argv[])
- {
- stringimage_path="D:/lena.jpg";
- Matimg=imread(image_path);
- cvtColor(img,img,CV_BGR2GRAY);
- doublescale=0.5;
- Matsrc;
- img.copyTo(src);
- CvPointLeye;
- Leye.x=265;
- Leye.y=265;
- CvPointReye;
- Reye.x=328;
- Reye.y=265;
- //drawpupil
- src.at<unsignedchar>(Leye.y,Leye.x)=255;
- src.at<unsignedchar>(Reye.y,Reye.x)=255;
- imshow("src",src);
- //
- CvPointcenter;
- center.x=img.cols/2;
- center.y=img.rows/2;
- doubleangle=15L;
- //Matdst=ImageRotate(img,center,angle,scale);
- Matdst=ImageRotate2NewSize(img,center,angle,scale);
- //計算原特徵點在旋轉後圖像中的對應的坐標
- CvPointl2=getPointAffinedPos(src,dst,Leye,center,angle*CV_PI/180,scale);
- CvPointr2=getPointAffinedPos(src,dst,Reye,center,angle*CV_PI/180,scale);
- //drawpupil
- dst.at<unsignedchar>(l2.y,l2.x)=255;
- dst.at<unsignedchar>(r2.y,r2.x)=255;
- imshow("dst",dst);
- waitKey(0);
- return0;
- }
仿射變換結果以及瞳孔重新坐標計算結果:
5. 根據三個點進行仿射變換根據給點的三個點,由這三個點之前的坐標以及變換之後的坐標,對原圖像進行仿射變換,不過需要事先知道三個點仿射變換的坐標位置。[cpp] view plain copy
- int_tmain(intargc,_TCHAR*argv[])
- {
- stringimage_path="D:/lena.jpg";
- Matimg=imread(image_path);
- Point2fsrc_points[3];
- src_points[0]=Point2f(100,100);
- src_points[1]=Point2f(400,100);
- src_points[2]=Point2f(250,300);
- Point2fdst_points[3];
- dst_points[0]=Point2f(100,100);
- dst_points[1]=Point2f(400,300);
- dst_points[2]=Point2f(100,300);
- MatM1=getAffineTransform(src_points,dst_points);
- Matdst;
- warpAffine(img,dst,M1,cvSize(img.cols,img.rows),INTER_LINEAR);
- imshow("dst",dst);
- //cvtColor(img,img,CV_BGR2GRAY);
- //doublescale=1.5;
- //Matsrc;
- //img.copyTo(src);
- //CvPointLeye;
- //Leye.x=265;
- //Leye.y=265;
- //CvPointReye;
- //Reye.x=328;
- //Reye.y=265;
- ////drawpupil
- //src.at<unsignedchar>(Leye.y,Leye.x)=255;
- //src.at<unsignedchar>(Reye.y,Reye.x)=255;
- //imshow("src",src);
- ////
- //CvPointcenter;
- //center.x=img.cols/2;
- //center.y=img.rows/2;
- //doubleangle=15L;
- ////Matdst=ImageRotate(img,center,angle,scale);
- //Matdst=ImageRotate2NewSize(img,center,angle,scale);
- ////計算原特徵點在旋轉後圖像中的對應的坐標
- //CvPointl2=getPointAffinedPos(src,dst,Leye,center,angle*CV_PI/180,scale);
- //CvPointr2=getPointAffinedPos(src,dst,Reye,center,angle*CV_PI/180,scale);
- ////drawpupil
- //dst.at<unsignedchar>(l2.y,l2.x)=255;
- //dst.at<unsignedchar>(r2.y,r2.x)=255;
- //imshow("dst",dst);
- waitKey(0);
- return0;
- }
結果:
推薦閱讀: