几何变换

几何变换可以看成图像中物体(或像素)空间位置改变,或者说是像素的移动。

几何运算需要空间变换和灰度级差值两个步骤的算法,像素通过变换映射到新的坐标位置,新的位置可能是在几个像素之间,即不一定为整数坐标。这时就需要灰度级差值将映射的新坐标匹配到输出像素之间。最简单的插值方法是最近邻插值,就是令输出像素的灰度值等于映射最近的位置像素,该方法可能会产生锯齿。这种方法也叫零阶插值,相应比较复杂的还有一阶和高阶插值。

插值算法感觉只要了解就可以了,图像处理中比较需要理解的还是空间变换。

空间变换

空间变换对应矩阵的仿射变换。一个坐标通过函数变换的新的坐标位置:

所以在程序中我们可以使用一个2*3的数组结构来存储变换矩阵:

以最简单的平移变换为例,平移(b1,b2)坐标可以表示为:

因此,平移变换的变换矩阵及逆矩阵记为:

缩放变换:将图像横坐标放大(或缩小)sx倍,纵坐标放大(或缩小)sy倍,变换矩阵及逆矩阵为:

选择变换:图像绕原点逆时针旋转a角,其变换矩阵及逆矩阵(顺时针选择)为:

OpenCV中的图像变换函数

基本的放射变换函数:

[cpp] view plaincopy
  1. void cvWarpAffine(
  2. const CvArr* src,//输入图像
  3. CvArr* dst, //输出图像
  4. const CvMat* map_matrix,   //2*3的变换矩阵
  5. int flags=CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS,   //插值方法的组合
  6. CvScalar fillval=cvScalarAll(0)   //用来填充边界外的值
  7. );

另外一个比较类似的函数是cvGetQuadrangleSubPix:

[cpp] view plaincopy
  1. void cvGetQuadrangleSubPix(
  2. const CvArr* src,  //输入图像
  3. CvArr* dst,   // 提取的四边形
  4. const CvMat* map_matrix //2*3的变换矩阵
  5. );

这个函数用以提取输入图像中的四边形,并通过map_matrix变换存储到dst中,与WarpAffine变换意义相同,

即对应每个点的变换:

WarpAffine与 GetQuadrangleSubPix 不同的在于cvWarpAffine 要求输入和输出图像具有同样的数据类型,有更大的资源开销(因此对小图像不太合适)而且输出图像的部分可以保留不变。而 cvGetQuadrangleSubPix 可以精确地从8位图像中提取四边形到浮点数缓存区中,具有比较小的系统开销,而且总是全部改变输出图像的内容。

实践:图像旋转变换(原尺寸)

首先用cvWarpAffine实验将图像逆时针旋转degree角度。
[cpp] view plaincopy
  1. //逆时针旋转图像degree角度(原尺寸)
  2. void rotateImage(IplImage* img, IplImage *img_rotate,int degree)
  3. {
  4. //旋转中心为图像中心
  5. CvPoint2D32f center;
  6. center.x=float (img->width/2.0+0.5);
  7. center.y=float (img->height/2.0+0.5);
  8. //计算二维旋转的仿射变换矩阵
  9. float m[6];
  10. CvMat M = cvMat( 2, 3, CV_32F, m );
  11. cv2DRotationMatrix( center, degree,1, &M);
  12. //变换图像,并用黑色填充其余值
  13. cvWarpAffine(img,img_rotate, &M,CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS,cvScalarAll(0) );
  14. }

逆时针旋转30度结果:

这里我们将新的图像还保留原来的图像尺寸。这样的效果显然不太好,我们通过计算相应放大图像尺寸。

实践:图像旋转变换(保留原图内容,放大尺寸)

需要计算新图的尺寸,示意图如下:
所以新图size为(width*cos(a)+height*sin(a), height*cos(a)+width*sin(a))
[cpp] view plaincopy
  1. //旋转图像内容不变,尺寸相应变大
  2. IplImage* rotateImage1(IplImage* img,int degree){
  3. double angle = degree  * CV_PI / 180.; // 弧度
  4. double a = sin(angle), b = cos(angle);
  5. int width = img->width;
  6. int height = img->height;
  7. int width_rotate= int(height * fabs(a) + width * fabs(b));
  8. int height_rotate=int(width * fabs(a) + height * fabs(b));
  9. //旋转数组map
  10. // [ m0  m1  m2 ] ===>  [ A11  A12   b1 ]
  11. // [ m3  m4  m5 ] ===>  [ A21  A22   b2 ]
  12. float map[6];
  13. CvMat map_matrix = cvMat(2, 3, CV_32F, map);
  14. // 旋转中心
  15. CvPoint2D32f center = cvPoint2D32f(width / 2, height / 2);
  16. cv2DRotationMatrix(center, degree, 1.0, &map_matrix);
  17. map[2] += (width_rotate - width) / 2;
  18. map[5] += (height_rotate - height) / 2;
  19. IplImage* img_rotate = cvCreateImage(cvSize(width_rotate, height_rotate), 8, 3);
  20. //对图像做仿射变换
  21. //CV_WARP_FILL_OUTLIERS - 填充所有输出图像的象素。
  22. //如果部分象素落在输入图像的边界外,那么它们的值设定为 fillval.
  23. //CV_WARP_INVERSE_MAP - 指定 map_matrix 是输出图像到输入图像的反变换,
  24. cvWarpAffine( img,img_rotate, &map_matrix, CV_INTER_LINEAR | CV_WARP_FILL_OUTLIERS, cvScalarAll(0));
  25. return img_rotate;
  26. }

实践:图像旋转变换(保留原图内容,放大尺寸)-2

试一下用cvGetQuadrangleSubPix函数:
[cpp] view plaincopy
  1. //旋转图像内容不变,尺寸相应变大
  2. IplImage* rotateImage2(IplImage* img, int degree)
  3. {
  4. double angle = degree  * CV_PI / 180.;
  5. double a = sin(angle), b = cos(angle);
  6. int width=img->width, height=img->height;
  7. //旋转后的新图尺寸
  8. int width_rotate= int(height * fabs(a) + width * fabs(b));
  9. int height_rotate=int(width * fabs(a) + height * fabs(b));
  10. IplImage* img_rotate = cvCreateImage(cvSize(width_rotate, height_rotate), img->depth, img->nChannels);
  11. cvZero(img_rotate);
  12. //保证原图可以任意角度旋转的最小尺寸
  13. int tempLength = sqrt((double)width * width + (double)height *height) + 10;
  14. int tempX = (tempLength + 1) / 2 - width / 2;
  15. int tempY = (tempLength + 1) / 2 - height / 2;
  16. IplImage* temp = cvCreateImage(cvSize(tempLength, tempLength), img->depth, img->nChannels);
  17. cvZero(temp);
  18. //将原图复制到临时图像tmp中心
  19. cvSetImageROI(temp, cvRect(tempX, tempY, width, height));
  20. cvCopy(img, temp, NULL);
  21. cvResetImageROI(temp);
  22. //旋转数组map
  23. // [ m0  m1  m2 ] ===>  [ A11  A12   b1 ]
  24. // [ m3  m4  m5 ] ===>  [ A21  A22   b2 ]
  25. float m[6];
  26. int w = temp->width;
  27. int h = temp->height;
  28. m[0] = b;
  29. m[1] = a;
  30. m[3] = -m[1];
  31. m[4] = m[0];
  32. // 将旋转中心移至图像中间
  33. m[2] = w * 0.5f;
  34. m[5] = h * 0.5f;
  35. CvMat M = cvMat(2, 3, CV_32F, m);
  36. cvGetQuadrangleSubPix(temp, img_rotate, &M);
  37. cvReleaseImage(&temp);
  38. return img_rotate;
  39. }

实践:图像放射变换(通过三点确定变换矩阵)

在OpenCV 2.3的参考手册中《opencv_tutorials》介绍了另一种确定变换矩阵的方法,通过三个点变换的几何关系映射实现变换。

变换示意图如下:
即通过三个点就可以确定一个变换矩阵。(矩形变换后一定为平行四边形)
以下是基于OpenCV 2.3的代码(需至少2.0以上版本的支持)
[cpp] view plaincopy
  1. int main( )
  2. {
  3. Point2f srcTri[3];
  4. Point2f dstTri[3];
  5. Mat rot_mat( 2, 3, CV_32FC1 );
  6. Mat warp_mat( 2, 3, CV_32FC1 );
  7. Mat src, warp_dst, warp_rotate_dst;
  8. //读入图像
  9. src = imread( "baboon.jpg", 1 );
  10. warp_dst = Mat::zeros( src.rows, src.cols, src.type() );
  11. // 用3个点确定A仿射变换
  12. srcTri[0] = Point2f( 0,0 );
  13. srcTri[1] = Point2f( src.cols - 1, 0 );
  14. srcTri[2] = Point2f( 0, src.rows - 1 );
  15. dstTri[0] = Point2f( src.cols*0.0, src.rows*0.33 );
  16. dstTri[1] = Point2f( src.cols*0.85, src.rows*0.25 );
  17. dstTri[2] = Point2f( src.cols*0.15, src.rows*0.7 );
  18. warp_mat = getAffineTransform( srcTri, dstTri );
  19. warpAffine( src, warp_dst, warp_mat, warp_dst.size() );
  20. /// 旋转矩阵
  21. Point center = Point( warp_dst.cols/2, warp_dst.rows/2 );
  22. double angle = -50.0;
  23. double scale = 0.6;
  24. rot_mat = getRotationMatrix2D( center, angle, scale );
  25. warpAffine( warp_dst, warp_rotate_dst, rot_mat, warp_dst.size() );
  26. OpenCV 1.0的形式
  27. //IplImage * img=cvLoadImage("baboon.jpg");
  28. //IplImage *img_rotate=cvCloneImage(img);
  29. //CvMat M =warp_mat;
  30. //cvWarpAffine(img,img_rotate, &M,CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS,cvScalarAll(0) );
  31. //cvShowImage("Wrap2",img_rotate);
  32. namedWindow( "Source", CV_WINDOW_AUTOSIZE );
  33. imshow( "Source", src );
  34. namedWindow( "Wrap", CV_WINDOW_AUTOSIZE );
  35. imshow( "Wrap", warp_dst );
  36. namedWindow("Wrap+Rotate", CV_WINDOW_AUTOSIZE );
  37. imshow( "Wrap+Rotate", warp_rotate_dst );
  38. waitKey(0);
  39. return 0;
  40. }

变换结果:

转载请注明出处:http://blog.csdn.net/xiaowei_cqu/article/details/7616044
实验代码下载:http://download.csdn.net/detail/xiaowei_cqu/4339856

写在最后的一点点闲话
之前一直用的2.1的版本,后来装了2.3,只是听说2.3很强大,但我刚开始学,用的也基础,完全没感觉出不同。直到今天忽然看到了2.3的手册,才发现从2.0开始函数和基本结构都有了很大的改变,而我一直还是用的1.0风格的函数(比如cvMat,cvLoadImage)。我的两个学习工具《Learnning OpenCV》和《OpenCV中文参考手册》都是基于1.0的,这也是我到今天才看到Mat,然后直接被惊艳到了。
别人总结出来的东西能帮助我们在一开始迅速入门,但要学深,学精,终归还是要自己去努力挖的。

图像几何变换:旋转,缩放,斜切相关推荐

  1. OpenCV图像几何变换专题(缩放、翻转、仿射变换及透视)【python-Open_CV系列(五)】

    OpenCV图像几何变换专题(缩放.翻转.仿射变换及透视)(python为工具) [Open_CV系列(五)] 文章目录 准备图片 1. 缩放 cv2.resize()方法 2. 翻转 cv2.fli ...

  2. 用矩阵表示图形旋转缩放变换

    二维图形 1.缩放 假设x和y都缩小到0.5倍 那新的x' = 0.5x 新的y' = 0.5y 我们现在通用化一下,假设x方向缩放为Sx,y方向缩放为Sy,并且用矩阵的方法可表示如下 -1 < ...

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

    2019独角兽企业重金招聘Python工程师标准>>> 几何变换 几何变换可以看成图像中物体(或像素)空间位置改变,或者说是像素的移动. 几何运算需要空间变换和灰度级差值两个步骤的算 ...

  4. C语言实现bmp图像几何变换(移动,旋转,镜像,转置,缩放)

    C语言实现bmp图像几何变换(移动,旋转,镜像,转置,缩放) 移动 旋转 镜像 转置 缩放 自定义结构及函数如下: #define pi 3.1415926 typedef struct {unsig ...

  5. 图像几何变换C++实现--镜像,平移,旋转,错切,缩放

    一.图像几何变换介绍 图像的几何空间变换是图像处理中的最基础的算法,是指对原始图像按需要改变其大小.形状和位置的变化,原始图像与目标函数之间的坐标变换函数为线性函数.二维图像的基本几何变换主要包括镜像 ...

  6. 图像算法二:【图像几何变换】平移、镜像、转置、缩放、旋转、插值

    作为一个强大的科学计算软件,MATLAB广泛运用于较多领域,以其简单的编程风格著称.这篇文章便通过matlab语言来讲述如何进行图像的各种几何变换. 图像几何变换又称为图像空间变换,它是将一幅图像中的 ...

  7. 图像几何变换--缩放、平移、镜像、旋转

    目录 一.图像几何变换基础 二.平移 三.缩放 四.旋转 五.镜像 六.复合变换 七.变换矩阵总结 八.完整代码 一.图像几何变换基础 1.图像的几何变换是指原始图像按照需要产生大小.形状和位置的变化 ...

  8. [Python从零到壹] 三十八.图像处理基础篇之图像几何变换(平移缩放旋转)

    欢迎大家来到"Python从零到壹",在这里我将分享约200篇Python系列文章,带大家一起去学习和玩耍,看看Python这个有趣的世界.所有文章都将结合案例.代码和作者的经验讲 ...

  9. 2020.6.21P72_OpenCV_几何变换_图像平移_缩放_旋转

    描述: //2020.6.21P72_OpenCV_图像平移_缩放_旋转 •完成一张图像的几何变换需要两个独立的算法.首先需要一个算法实现空间坐标变换,用它描述每个像素如何从初始位置移动到终止位置:其 ...

最新文章

  1. Centos7-Mysql 5.6 多主一从 解决方案详细配置
  2. 【周末阅读】人工智能时代基础数据服务大有可为
  3. timeSetEvent
  4. 041_对象的创建和销毁
  5. 计算机硬盘冒烟了,电脑硬盘冒烟损坏了怎么办?
  6. SQLite 语法(http://www.w3cschool.cc/sqlite/sqlite-syntax.html)
  7. HTML5事件——contextmenu 隐藏鼠标右键菜单
  8. 用Pytorch实现逻辑回归分类
  9. java 分割数据_java 分割csv数据的实例详解
  10. html5中可用的颜色,jquery – 如何知道HTML5输入类型颜色是否可用作颜色选择器?...
  11. mysql安装出现中文乱码_MySQL安装以及中文乱码问题
  12. python安装whl_1 Matplotlib在win10下安装
  13. 7. mybatis实战教程(mybatis in action)之八:mybatis 动态sql语句
  14. 10个优秀的JavaScript参考手册
  15. WebSocket开发说明文档
  16. android url带参数传递,android平台通过http post方式向远程URL传递参数并获取返回数据...
  17. 也就整了一万字的「数据指标体系」指南。
  18. 一篇文章说尽,中国互联网的30年(完结篇)
  19. desktop window manager
  20. XMU 1615 刘备闯三国之三顾茅庐(三) 【欧拉函数+快速幂+欧拉定理】

热门文章

  1. 格子大法与换入换出分析
  2. JPOM - AOP+自定义注解实现操作日志记录
  3. Java Review - 并发编程_前置知识二
  4. Java Review - 并发组件ConcurrentHashMap使用时的注意事项及源码分析
  5. Java 8 - 数值流Numberic Stream
  6. CSS基础_Day02
  7. TCP三次握手、四次挥手过程及原理
  8. C++——入门知识点汇总(命名空间、缺省、重载、引用等)
  9. 机器视觉-特征点检测:【2】Harrs角点检测原理
  10. html load方法的区别,jQuery  中的.load()、$get()、$,post()用法和区别