透視變換的原理和矩陣求解請參見前一篇《透視變換 Perspective Transformation》。在OpenCV中也實現了透視變換的公式求解和變換函數。
求解變換公式的函數:
[cpp]?view plain
?copy ? Mat?getPerspectiveTransform(const?Point2f?src[],?const?Point2f?dst[])??
輸入原始圖像和變換之后的圖像的對應4個點,便可以得到變換矩陣。之后用求解得到的矩陣輸入perspectiveTransform便可以對一組點進行變換:
[cpp]?view plain
?copy ? void?perspectiveTransform(InputArray?src,?OutputArray?dst,?InputArray?m)??
注意這里src和dst的輸入并不是圖像,而是圖像對應的坐標。應用前一篇的例子,做個相反的變換:
[cpp]?view plain
?copy ? int?main(?)?? {?? ????Mat?img=imread("boy.png");?? ????int?img_height?=?img.rows;?? ????int?img_width?=?img.cols;?? ????vector<Point2f>?corners(4);?? ????corners[0]?=?Point2f(0,0);?? ????corners[1]?=?Point2f(img_width-1,0);?? ????corners[2]?=?Point2f(0,img_height-1);?? ????corners[3]?=?Point2f(img_width-1,img_height-1);?? ????vector<Point2f>?corners_trans(4);?? ????corners_trans[0]?=?Point2f(150,250);?? ????corners_trans[1]?=?Point2f(771,0);?? ????corners_trans[2]?=?Point2f(0,img_height-1);?? ????corners_trans[3]?=?Point2f(650,img_height-1);?? ?? ????Mat?transform?=?getPerspectiveTransform(corners,corners_trans);?? ????cout<<transform<<endl;?? ????vector<Point2f>?ponits,?points_trans;?? ????for(int?i=0;i<img_height;i++){?? ????????for(int?j=0;j<img_width;j++){?? ????????????ponits.push_back(Point2f(j,i));?? ????????}?? ????}?? ?? ????perspectiveTransform(?ponits,?points_trans,?transform);?? ????Mat?img_trans?=?Mat::zeros(img_height,img_width,CV_8UC3);?? ????int?count?=?0;?? ????for(int?i=0;i<img_height;i++){?? ????????uchar*?p?=?img.ptr<uchar>(i);?? ????????for(int?j=0;j<img_width;j++){?? ????????????int?y?=?points_trans[count].y;?? ????????????int?x?=?points_trans[count].x;?? ????????????uchar*?t?=?img_trans.ptr<uchar>(y);?? ????????????t[x*3]??=?p[j*3];?? ????????????t[x*3+1]??=?p[j*3+1];?? ????????????t[x*3+2]??=?p[j*3+2];?? ????????????count++;?? ????????}?? ????}?? ????imwrite("boy_trans.png",img_trans);?? ?? ????return?0;?? }??
得到變換之后的圖片:
注意這種將原圖變換到對應圖像上的方式會有一些沒有被填充的點,也就是右圖中黑色的小點。解決這種問題一是用差值的方式,再一種比較簡單就是不用原圖的點變換后對應找新圖的坐標,而是直接在新圖上找反向變換原圖的點。說起來有點繞口,具體見前一篇《透視變換 Perspective Transformation》的代碼應該就能懂啦。
除了getPerspectiveTransform()函數,OpenCV還提供了findHomography()的函數,不是用點來找,而是直接用透視平面來找變換公式。這個函數在特征匹配的經典例子中有用到,也非常直觀:
[cpp]?view plain
?copy ? int?main(?int?argc,?char**?argv?)?? {?? ????Mat?img_object?=?imread(?argv[1],?IMREAD_GRAYSCALE?);?? ????Mat?img_scene?=?imread(?argv[2],?IMREAD_GRAYSCALE?);?? ????if(?!img_object.data?||?!img_scene.data?)?? ????{?std::cout<<?"?--(!)?Error?reading?images?"?<<?std::endl;?return?-1;?}?? ?? ?????? ????int?minHessian?=?400;?? ????SurfFeatureDetector?detector(?minHessian?);?? ????std::vector<KeyPoint>?keypoints_object,?keypoints_scene;?? ????detector.detect(?img_object,?keypoints_object?);?? ????detector.detect(?img_scene,?keypoints_scene?);?? ?? ?????? ????SurfDescriptorExtractor?extractor;?? ????Mat?descriptors_object,?descriptors_scene;?? ????extractor.compute(?img_object,?keypoints_object,?descriptors_object?);?? ????extractor.compute(?img_scene,?keypoints_scene,?descriptors_scene?);?? ?? ?????? ????FlannBasedMatcher?matcher;?? ????std::vector<?DMatch?>?matches;?? ????matcher.match(?descriptors_object,?descriptors_scene,?matches?);?? ????double?max_dist?=?0;?double?min_dist?=?100;?? ?? ?????? ????for(?int?i?=?0;?i?<?descriptors_object.rows;?i++?)?? ????{?double?dist?=?matches[i].distance;?? ????if(?dist?<?min_dist?)?min_dist?=?dist;?? ????if(?dist?>?max_dist?)?max_dist?=?dist;?? ????}?? ?? ????printf("--?Max?dist?:?%f?\n",?max_dist?);?? ????printf("--?Min?dist?:?%f?\n",?min_dist?);?? ?? ?????? ????std::vector<?DMatch?>?good_matches;?? ?? ????for(?int?i?=?0;?i?<?descriptors_object.rows;?i++?)?? ????{?if(?matches[i].distance?<?3*min_dist?)?? ????{?good_matches.push_back(?matches[i]);?}?? ????}?? ?? ????Mat?img_matches;?? ????drawMatches(?img_object,?keypoints_object,?img_scene,?keypoints_scene,?? ????????good_matches,?img_matches,?Scalar::all(-1),?Scalar::all(-1),?? ????????vector<char>(),?DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS?);?? ?? ?????? ????std::vector<Point2f>?obj;?? ????std::vector<Point2f>?scene;?? ?? ????for(?size_t?i?=?0;?i?<?good_matches.size();?i++?)?? ????{?? ?????????? ????????obj.push_back(?keypoints_object[?good_matches[i].queryIdx?].pt?);?? ????????scene.push_back(?keypoints_scene[?good_matches[i].trainIdx?].pt?);?? ????}?? ?? ????Mat?H?=?findHomography(?obj,?scene,?RANSAC?);?? ?? ?????? ????std::vector<Point2f>?obj_corners(4);?? ????obj_corners[0]?=?Point(0,0);?obj_corners[1]?=?Point(?img_object.cols,?0?);?? ????obj_corners[2]?=?Point(?img_object.cols,?img_object.rows?);?obj_corners[3]?=?Point(?0,?img_object.rows?);?? ????std::vector<Point2f>?scene_corners(4);?? ????perspectiveTransform(?obj_corners,?scene_corners,?H);?? ?????? ????Point2f?offset(?(float)img_object.cols,?0);?? ????line(?img_matches,?scene_corners[0]?+?offset,?scene_corners[1]?+?offset,?Scalar(0,?255,?0),?4?);?? ????line(?img_matches,?scene_corners[1]?+?offset,?scene_corners[2]?+?offset,?Scalar(?0,?255,?0),?4?);?? ????line(?img_matches,?scene_corners[2]?+?offset,?scene_corners[3]?+?offset,?Scalar(?0,?255,?0),?4?);?? ????line(?img_matches,?scene_corners[3]?+?offset,?scene_corners[0]?+?offset,?Scalar(?0,?255,?0),?4?);?? ?? ?????? ????imshow(?"Good?Matches?&?Object?detection",?img_matches?);?? ????waitKey(0);?? ????return?0;?? }??
代碼運行效果:
findHomography()函數直接通過兩個平面上相匹配的特征點求出變換公式,之后代碼又對原圖的四個邊緣點進行變換,在右圖上畫出對應的矩形。這個圖也很好地解釋了所謂透視變換的“Viewing Plane”。
(轉載請注明作者和出處:http://blog.csdn.net/xiaowei_cqu?未經允許請勿用于商業用途)
from:?http://blog.csdn.net/xiaowei_cqu/article/details/26478135
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀
總結
以上是生活随笔為你收集整理的【OpenCV】透视变换 Perspective Transformation(续)的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。