分类: 【图像处理】 【编程语言】 2014-05-27 09:39 2776人阅读 评论(13) 收藏 举报

透视变换的原理和矩阵求解请参见前一篇《透视变换 Perspective Transformation》。在OpenCV中也实现了透视变换的公式求解和变换函数。

求解变换公式的函数:

  1. Mat getPerspectiveTransform(const Point2f src[], const Point2f dst[])
Mat getPerspectiveTransform(const Point2f src[], const Point2f dst[])

输入原始图像和变换之后的图像的对应4个点,便可以得到变换矩阵。之后用求解得到的矩阵输入perspectiveTransform便可以对一组点进行变换:

  1. void perspectiveTransform(InputArray src, OutputArray dst, InputArray m)
void perspectiveTransform(InputArray src, OutputArray dst, InputArray m)

注意这里src和dst的输入并不是图像,而是图像对应的坐标。应用前一篇的例子,做个相反的变换:

  1. int main( )
  2. {
  3. Mat img=imread("boy.png");
  4. int img_height = img.rows;
  5. int img_width = img.cols;
  6. vector<Point2f> corners(4);
  7. corners[0] = Point2f(0,0);
  8. corners[1] = Point2f(img_width-1,0);
  9. corners[2] = Point2f(0,img_height-1);
  10. corners[3] = Point2f(img_width-1,img_height-1);
  11. vector<Point2f> corners_trans(4);
  12. corners_trans[0] = Point2f(150,250);
  13. corners_trans[1] = Point2f(771,0);
  14. corners_trans[2] = Point2f(0,img_height-1);
  15. corners_trans[3] = Point2f(650,img_height-1);
  16. Mat transform = getPerspectiveTransform(corners,corners_trans);
  17. cout<<transform<<endl;
  18. vector<Point2f> ponits, points_trans;
  19. for(int i=0;i<img_height;i++){
  20. for(int j=0;j<img_width;j++){
  21. ponits.push_back(Point2f(j,i));
  22. }
  23. }
  24. perspectiveTransform( ponits, points_trans, transform);
  25. Mat img_trans = Mat::zeros(img_height,img_width,CV_8UC3);
  26. int count = 0;
  27. for(int i=0;i<img_height;i++){
  28. uchar* p = img.ptr<uchar>(i);
  29. for(int j=0;j<img_width;j++){
  30. int y = points_trans[count].y;
  31. int x = points_trans[count].x;
  32. uchar* t = img_trans.ptr<uchar>(y);
  33. t[x*3]  = p[j*3];
  34. t[x*3+1]  = p[j*3+1];
  35. t[x*3+2]  = p[j*3+2];
  36. count++;
  37. }
  38. }
  39. imwrite("boy_trans.png",img_trans);
  40. return 0;
  41. }
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()的函数,不是用点来找,而是直接用透视平面来找变换公式。这个函数在特征匹配的经典例子中有用到,也非常直观:

  1. int main( int argc, char** argv )
  2. {
  3. Mat img_object = imread( argv[1], IMREAD_GRAYSCALE );
  4. Mat img_scene = imread( argv[2], IMREAD_GRAYSCALE );
  5. if( !img_object.data || !img_scene.data )
  6. { std::cout<< " --(!) Error reading images " << std::endl; return -1; }
  7. //-- Step 1: Detect the keypoints using SURF Detector
  8. int minHessian = 400;
  9. SurfFeatureDetector detector( minHessian );
  10. std::vector<KeyPoint> keypoints_object, keypoints_scene;
  11. detector.detect( img_object, keypoints_object );
  12. detector.detect( img_scene, keypoints_scene );
  13. //-- Step 2: Calculate descriptors (feature vectors)
  14. SurfDescriptorExtractor extractor;
  15. Mat descriptors_object, descriptors_scene;
  16. extractor.compute( img_object, keypoints_object, descriptors_object );
  17. extractor.compute( img_scene, keypoints_scene, descriptors_scene );
  18. //-- Step 3: Matching descriptor vectors using FLANN matcher
  19. FlannBasedMatcher matcher;
  20. std::vector< DMatch > matches;
  21. matcher.match( descriptors_object, descriptors_scene, matches );
  22. double max_dist = 0; double min_dist = 100;
  23. //-- Quick calculation of max and min distances between keypoints
  24. for( int i = 0; i < descriptors_object.rows; i++ )
  25. { double dist = matches[i].distance;
  26. if( dist < min_dist ) min_dist = dist;
  27. if( dist > max_dist ) max_dist = dist;
  28. }
  29. printf("-- Max dist : %f \n", max_dist );
  30. printf("-- Min dist : %f \n", min_dist );
  31. //-- Draw only "good" matches (i.e. whose distance is less than 3*min_dist )
  32. std::vector< DMatch > good_matches;
  33. for( int i = 0; i < descriptors_object.rows; i++ )
  34. { if( matches[i].distance < 3*min_dist )
  35. { good_matches.push_back( matches[i]); }
  36. }
  37. Mat img_matches;
  38. drawMatches( img_object, keypoints_object, img_scene, keypoints_scene,
  39. good_matches, img_matches, Scalar::all(-1), Scalar::all(-1),
  40. vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS );
  41. //-- Localize the object from img_1 in img_2
  42. std::vector<Point2f> obj;
  43. std::vector<Point2f> scene;
  44. for( size_t i = 0; i < good_matches.size(); i++ )
  45. {
  46. //-- Get the keypoints from the good matches
  47. obj.push_back( keypoints_object[ good_matches[i].queryIdx ].pt );
  48. scene.push_back( keypoints_scene[ good_matches[i].trainIdx ].pt );
  49. }
  50. Mat H = findHomography( obj, scene, RANSAC );
  51. //-- Get the corners from the image_1 ( the object to be "detected" )
  52. std::vector<Point2f> obj_corners(4);
  53. obj_corners[0] = Point(0,0); obj_corners[1] = Point( img_object.cols, 0 );
  54. obj_corners[2] = Point( img_object.cols, img_object.rows ); obj_corners[3] = Point( 0, img_object.rows );
  55. std::vector<Point2f> scene_corners(4);
  56. perspectiveTransform( obj_corners, scene_corners, H);
  57. //-- Draw lines between the corners (the mapped object in the scene - image_2 )
  58. Point2f offset( (float)img_object.cols, 0);
  59. line( img_matches, scene_corners[0] + offset, scene_corners[1] + offset, Scalar(0, 255, 0), 4 );
  60. line( img_matches, scene_corners[1] + offset, scene_corners[2] + offset, Scalar( 0, 255, 0), 4 );
  61. line( img_matches, scene_corners[2] + offset, scene_corners[3] + offset, Scalar( 0, 255, 0), 4 );
  62. line( img_matches, scene_corners[3] + offset, scene_corners[0] + offset, Scalar( 0, 255, 0), 4 );
  63. //-- Show detected matches
  64. imshow( "Good Matches & Object detection", img_matches );
  65. waitKey(0);
  66. return 0;
  67. }
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; } //-- Step 1: Detect the keypoints using SURF Detector
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 ); //-- Step 2: Calculate descriptors (feature vectors)
SurfDescriptorExtractor extractor;
Mat descriptors_object, descriptors_scene;
extractor.compute( img_object, keypoints_object, descriptors_object );
extractor.compute( img_scene, keypoints_scene, descriptors_scene ); //-- Step 3: Matching descriptor vectors using FLANN matcher
FlannBasedMatcher matcher;
std::vector< DMatch > matches;
matcher.match( descriptors_object, descriptors_scene, matches );
double max_dist = 0; double min_dist = 100; //-- Quick calculation of max and min distances between keypoints
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 ); //-- Draw only "good" matches (i.e. whose distance is less than 3*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 ); //-- Localize the object from img_1 in img_2
std::vector<Point2f> obj;
std::vector<Point2f> scene; for( size_t i = 0; i < good_matches.size(); i++ )
{
//-- Get the keypoints from the good matches
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 ); //-- Get the corners from the image_1 ( the object to be "detected" )
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);
//-- Draw lines between the corners (the mapped object in the scene - image_2 )
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 ); //-- Show detected matches
imshow( "Good Matches & Object detection", img_matches );
waitKey(0);
return 0;
}

代码运行效果:

findHomography()函数直接通过两个平面上相匹配的特征点求出变换公式,之后代码又对原图的四个边缘点进行变换,在右图上画出对应的矩形。这个图也很好地解释了所谓透视变换的“Viewing
Plane”。

OpenCV】透视变换 Perspective Transformation(续)的更多相关文章

  1. OpenCV Intro - Perspective Transform

    透视变换(Perspective Transformation)是将图片投影到一个新的视平面(Viewing Plane),也称作投影映射(Projective Mapping).通用的变换公式为: ...

  2. 对倾斜的图像进行修正——基于opencv 透视变换

    这篇文章主要解决这样一个问题: 有一张倾斜了的图片(当然是在Z轴上也有倾斜,不然直接旋转得了o(╯□╰)o),如何尽量将它纠正到端正的状态. 而要解决这样一个问题,可以用到透视变换. 关于透视变换的原 ...

  3. OpenCV 透视变换实例

    参考文献: http://www.cnblogs.com/self-control/archive/2013/01/18/2867022.html http://opencv-code.com/tut ...

  4. OpenCV-paper detection & perspective transformation 相关资料

    经过一段时间的搜索,决定把搜过的资料都汇集在此,以免重复劳动,几乎来自stackoverflow OpenCV C++/Obj-C: Detecting a sheet of paper / Squa ...

  5. Java基于opencv—透视变换矫正图像

    很多时候我们拍摄的照片都会产生一点畸变的,就像下面的这张图 虽然不是很明显,但还是有一点畸变的,而我们要做的就是把它变成下面的这张图 效果看起来并不是很好,主要是四个顶点找的不准确,会有一些偏差,而且 ...

  6. opencv透视变换GetPerspectiveTransform的总结

    对于透视变换,必须为map_matrix分配一个3x3数组,除了3x3矩阵和三个控点变为四个控点外,透视变化在其他方面与仿射变换完全类似.具体可以参考:点击打开链接 主要用到两个函数WarpPersp ...

  7. opencv透视变换

    关于透视投影的几何知识,以及求解方法,可以参考 http://media.cs.tsinghua.edu.cn/~ahz/digitalimageprocess/chapter06/chapt06_a ...

  8. cv2.getPerspectiveTransform 透视变换

    简介 透视变换(Perspective Transformation)是将成像投影到一个新的视平面(Viewing Plane),也称作投影映射(Projective Mapping).如图1,通过透 ...

  9. 深入学习OpenCV文档扫描及OCR识别(文档扫描,图像矫正,透视变换,OCR识别)

    如果需要处理的原图及代码,请移步小编的GitHub地址 传送门:请点击我 如果点击有误:https://github.com/LeBron-Jian/ComputerVisionPractice 下面 ...

随机推荐

  1. adb server is out of date killing... 的解决办法

    是adb server端口被占用了 你先执行adb nodaemon server ,查看adb server的端口是多少 1 2 C:\Users\xxxx>adb nodaemon serv ...

  2. html5新特性之音频、视频

    1.视频 标签video video标签的属性 属性 描述 autoplay 视频就绪后自动播放 preload 视频在页面加载时加载 loop 视频播放完成后循环播放 controls 显示控件 s ...

  3. 01:Geoserver发布shapfile,中文字段乱码问题

    软件环境:Geoserver 2.1.0 UDig 1.2.1 shapfile文件结构:FID                  地物名称      变化图斑                     ...

  4. Source Depot 使用总结

    MS使用的Source Depot方案,主要是控制软件版本,类似的软件有SVN等,Source Depot一般使用起来也比较方便,可以灵活的配置,只要有访问权限,就可以下到对应的源代码文件. SD使用 ...

  5. Winform 中的KeyDown

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...

  6. Dynamic CRM 2013学习笔记(二十二)插件里调用WCF服务

      1. 添加service:     2.调用WCF BasicHttpBinding myBinding = new BasicHttpBinding(); myBinding.Name = &q ...

  7. [ACM_模拟] The Willy Memorial Program (poj 1073 ,联通水管注水模拟)

    Description Willy the spider used to live in the chemistry laboratory of Dr. Petro. He used to wande ...

  8. 如何成为Python高手(转载)

    本文是从 How to become a proficient Python programmer 这篇文章翻译而来. 这篇文章主要是对我收集的一些文章的摘要.因为已经有很多比我有才华的人写出了大量关 ...

  9. 【原】《Git教程》学习笔记

    [TOC] 1 创建版本库 1.1 初始化 初始化一个Git仓库,使用 git init 命令. 添加文件到Git仓库,分两步: 第一步,使用命令git add <file> ,注意,可反 ...

  10. 利用nodejs模块缓存机制创建“全局变量”

    在<深入浅出nodejs>有这样一段(有部分增减): 1.nodejs引入模块分四个步骤 路径分析 文件定位 编译执行 加入内存 2.核心模块部分在node源代码的编译过程中就编译成了二级 ...