[opencv] 图像几何变换:旋转,缩放,斜切
几何变换
几何变换可以看成图像中物体(或像素)空间位置改变,或者说是像素的移动。
几何运算需要空间变换和灰度级差值两个步骤的算法,像素通过变换映射到新的坐标位置,新的位置可能是在几个像素之间,即不一定为整数坐标。这时就需要灰度级差值将映射的新坐标匹配到输出像素之间。最简单的插值方法是最近邻插值,就是令输出像素的灰度值等于映射最近的位置像素,该方法可能会产生锯齿。这种方法也叫零阶插值,相应比较复杂的还有一阶和高阶插值。
插值算法感觉只要了解就可以了,图像处理中比较需要理解的还是空间变换。
空间变换
空间变换对应矩阵的仿射变换。一个坐标通过函数变换的新的坐标位置:
所以在程序中我们可以使用一个2*3的数组结构来存储变换矩阵:
以最简单的平移变换为例,平移(b1,b2)坐标可以表示为:
因此,平移变换的变换矩阵及逆矩阵记为:
缩放变换:将图像横坐标放大(或缩小)sx倍,纵坐标放大(或缩小)sy倍,变换矩阵及逆矩阵为:
选择变换:图像绕原点逆时针旋转a角,其变换矩阵及逆矩阵(顺时针选择)为:
OpenCV中的图像变换函数
基本的放射变换函数:
- 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() //用来填充边界外的值
- );
另外一个比较类似的函数是cvGetQuadrangleSubPix:
- void cvGetQuadrangleSubPix(
- const CvArr* src, //输入图像
- CvArr* dst, // 提取的四边形
- const CvMat* map_matrix //2*3的变换矩阵
- );
这个函数用以提取输入图像中的四边形,并通过map_matrix变换存储到dst中,与WarpAffine变换意义相同,
即对应每个点的变换:
WarpAffine与 GetQuadrangleSubPix 不同的在于cvWarpAffine 要求输入和输出图像具有同样的数据类型,有更大的资源开销(因此对小图像不太合适)而且输出图像的部分可以保留不变。而 cvGetQuadrangleSubPix 可以精确地从8位图像中提取四边形到浮点数缓存区中,具有比较小的系统开销,而且总是全部改变输出图像的内容。
实践:图像旋转变换(原尺寸)
- //逆时针旋转图像degree角度(原尺寸)
- void rotateImage(IplImage* img, int degree)
- {
- IplImage *img_rotate = cvCloneImage(img);
- cvZero(img_rotate);
- //旋转中心为图像中心
- CvPoint2D32f center;
- center.x=float (img->width/2.0+0.5);
- center.y=float (img->height/2.0+0.5);
- //计算二维旋转的仿射变换矩阵
- float m[];
- CvMat M = cvMat( , , CV_32F, m );
- cv2DRotationMatrix( center, degree,, &M);
- //变换图像,并用黑色填充其余值
- cvWarpAffine(img,img_rotate, &M,CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS,cvScalarAll() );
- cvNamedWindow("原图");
- cvNamedWindow("旋转后的图像");
- cvShowImage("原图",img);
- cvShowImage("旋转后的图像",img_rotate);
- }
逆时针旋转30度结果:

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

- //旋转图像内容不变,尺寸相应变大
- IplImage* rotateImage1(IplImage* img,int degree){ //逆时针旋转
- double angle = degree * CV_PI / .; // 弧度
- 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));
- //旋转数组map
- // [ m0 m1 m2 ] ===> [ A11 A12 b1 ]
- // [ m3 m4 m5 ] ===> [ A21 A22 b2 ]
- float map[];
- CvMat map_matrix = cvMat(, , CV_32F, map);
- // 旋转中心
- CvPoint2D32f center = cvPoint2D32f(width / , height / );
- //函数返回一个指向2X3矩阵的指针
- //PointF center:源图像的旋转中心
- //double angle:源图像旋转的角度,正值表示逆时针旋转(坐标原点假设在图像左上角)
- //double scale:等向比例因子
- //IntPer mapMatrix:用于返回的2X3矩阵
- cv2DRotationMatrix(center, degree, 1.0, &map_matrix);
- map[] += (width_rotate - width) / ;
- map[] += (height_rotate - height) / ;
- IplImage* img_rotate = cvCreateImage(cvSize(width_rotate, height_rotate), , );
- //对图像做仿射变换
- //输入图像
- //输出图像
- //2*3的变换矩阵
- //插值方法的组合 CV_WARP_FILL_OUTLIERS - 填充所有输出图像的象素。
- //如果部分象素落在输入图像的边界外,那么它们的值设定为 fillval.
- //CV_WARP_INVERSE_MAP - 指定 map_matrix 是输出图像到输入图像的反变换,
- //用来填充边界外的值
- cvWarpAffine( img,img_rotate, &map_matrix, CV_INTER_LINEAR | CV_WARP_FILL_OUTLIERS, cvScalarAll());
- cvNamedWindow("原图");
- cvNamedWindow("旋转后的图像");
- cvShowImage("原图",img);
- cvShowImage("旋转后的图像",img_rotate);
- return img_rotate;
- }
实践:图像旋转变换(保留原图内容,放大尺寸)-2
- //旋转图像内容不变,尺寸相应变大
- IplImage* rotateImage2(IplImage* img, int degree) //顺时针旋转
- {
- double angle = degree * CV_PI / .;
- double a = sin(angle), b = cos(angle);
- int width=img->width, height=img->height;
- //旋转后的新图尺寸
- 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);
- //保证原图可以任意角度旋转的最小尺寸
- int tempLength = sqrt((double)width * width + (double)height *height) + ; //sqrt()开平方根
- int tempX = (tempLength + ) / - width / ;
- int tempY = (tempLength + ) / - height / ;
- IplImage* temp = cvCreateImage(cvSize(tempLength, tempLength), img->depth, img->nChannels);
- cvZero(temp);
- //将原图复制到临时图像tmp中心
- cvSetImageROI(temp, cvRect(tempX, tempY, width, height));
- cvCopy(img, temp, NULL);
- cvResetImageROI(temp);
- cvNamedWindow("临时图像");
- cvShowImage("临时图像",temp);
- //旋转数组map
- // [ m0 m1 m2 ] ===> [ A11 A12 b1 ]
- // [ m3 m4 m5 ] ===> [ A21 A22 b2 ]
- float m[];
- int w = temp->width;
- int h = temp->height;
- m[] = b;
- m[] = a;
- m[] = -m[];
- m[] = m[];
- // 将旋转中心移至图像中间
- m[] = w * 0.5f;
- m[] = h * 0.5f;
- CvMat M = cvMat(, , CV_32F, m);
- cvGetQuadrangleSubPix(temp, img_rotate, &M);
- cvReleaseImage(&temp);
- cvNamedWindow("原图");
- cvNamedWindow("旋转后的图像");
- cvShowImage("原图",img);
- cvShowImage("旋转后的图像",img_rotate);
- return img_rotate;
- }

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

- //以下代码理论可参考:http://blog.csdn.net/fengbingchun/article/details/17713429
- Mat rotateImage3(Mat src){ //仿射变换
- Point2f srcTri[];
- Point2f dstTri[];
- Mat rot_mat( , , CV_32FC1 );
- Mat warp_mat( , , CV_32FC1 );
- Mat warp_dst, warp_rotate_dst;
- warp_dst = Mat::zeros( src.rows, src.cols, src.type() );
- // 用3个点确定A仿射变换
- srcTri[] = Point2f( , );
- srcTri[] = Point2f( src.cols - , );
- srcTri[] = Point2f( , src.rows - );
- dstTri[] = Point2f( src.cols*0.0, src.rows*0.33 );
- dstTri[] = Point2f( src.cols*0.85, src.rows*0.25 );
- dstTri[] = Point2f( src.cols*0.15, src.rows*0.7 );
- warp_mat = getAffineTransform( srcTri, dstTri );
- warpAffine( src, warp_dst, warp_mat, warp_dst.size() );
- /// 旋转矩阵
- Point center = Point( warp_dst.cols/, warp_dst.rows/ );
- double angle = -50.0;
- double scale = 0.6;
- ////获取旋转矩阵 scale为缩放因子(x、y方向保持一致),angle为旋转角度(弧长),centerx,centery为旋转中心
- // | a b (1-a)*center.x-b*center.y | a=scale*cos(angle)
- // M= | |
- // | -b a b*center.x+(1-a)*center.y| b=scale*sin(angle)
- 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 );
- return warp_dst;
- }

主函数:
- // ImageRotation2.cpp : 定义控制台应用程序的入口点。
- // 本文代码参考:http://www.cnblogs.com/slysky/archive/2012/03/21/2410743.html
- #include "stdafx.h"
- #include <opencv2/core/core.hpp> //cvGetSize cvCreateImage
- #include <opencv2/highgui/highgui.hpp>
- #include <opencv2/opencv.hpp> //cvResize cvInitMatHeader cvGetMinMaxHistValue cvCvtColor
- #include <opencv2/imgproc/imgproc.hpp>
- #ifdef _DEBUG
- #pragma comment(lib, "opencv_core244d")
- #pragma comment(lib, "opencv_highgui244d")
- #pragma comment(lib, "opencv_imgproc244d") //cvResize
- #else
- #pragma comment(lib, "opencv_core244")
- #pragma comment(lib, "opencv_highgui244")
- #pragma comment(lib, "opencv_imgproc244") //cvResize
- #endif
- using namespace std;
- //隐藏控制台窗口
- #pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
- int main( )
- {
- //读入图像
- //Mat src = imread( "./images/baboon.jpg", 1 );
- //src = rotateImage3(src);
- int degree = ;
- IplImage *src = cvLoadImage("./images/meng3.jpg",CV_LOAD_IMAGE_UNCHANGED);
- rotateImage(src, degree);
- //src = rotateImage1(src, degree);
- //src = rotateImage2(src, degree);
- waitKey();
- return ;
- }
本文转自:http://blog.csdn.net/xiaowei_cqu/article/details/7616044
[opencv] 图像几何变换:旋转,缩放,斜切的更多相关文章
- opencv图像的旋转
#include"stdafx.h"#include"opencv2/opencv.hpp" using namespace cv;// clockwise 为 ...
- opencv 图像仿射变换 计算仿射变换后对应特征点的新坐标 图像旋转、缩放、平移
常常需要最图像进行仿射变换,仿射变换后,我们可能需要将原来图像中的特征点坐标进行重新计算,获得原来图像中例如眼睛瞳孔坐标的新的位置,用于在新得到图像中继续利用瞳孔位置坐标. 仿射变换在:http:// ...
- Numpy和OpenCV中的图像几何变换
介绍 上面的图像使它不言而喻什么是几何变换.它是一种应用广泛的图像处理技术.例如,在计算机图形学中有一个简单的用例,用于在较小或较大的屏幕上显示图形内容时简单地重新缩放图形内容. 它也可以应用于扭曲一 ...
- OpenCV图像金字塔:高斯金字塔、拉普拉斯金字塔与图片尺寸缩放
这篇已经写得很好,真心给作者点个赞.题目都是直接转过来的,直接去看吧. Reference Link : http://blog.csdn.net/poem_qianmo/article/detail ...
- (原)使用opencv的warpAffine函数对图像进行旋转
转载请注明出处: http://www.cnblogs.com/darkknightzh/p/5070576.html 参考网址: http://stackoverflow.com/questions ...
- 【OpenCV新手教程之十三】OpenCV图像金字塔:高斯金字塔、拉普拉斯金字塔与图片尺寸缩放
本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/26157633 作者:毛星云(浅墨) ...
- openCV—Python(5)—— 图像几何变换
一.函数简单介绍 1.warpAffine-图像放射变换(平移.旋转.缩放) 函数原型:warpAffine(src, M, dsize, dst=None, flags=None, borderMo ...
- OpenCL + OpenCV 图像旋转
▶ 使用 OpenCV 从文件读取彩色的 png 图像,旋转一定角度以后写回文件 ● 代码,核函数 // rotate.cl //__constant sampler_t sampler = CLK_ ...
- 学习 opencv---(12)OpenCV 图像金字塔:高斯金字塔,拉普拉斯金字塔与图片尺寸缩放
在这篇文章里,我们一起学习下 图像金字塔 的一些基本概念,如何使用OpenCV函数pyrUp和pyrDown 对图像进行向上和向下采样,以及了解专门用于缩放图像尺寸的resize函数的用法.此博文一共 ...
随机推荐
- Scrum立会报告+燃尽图(Final阶段第一次)
此作业要求参见:https://edu.cnblogs.com/campus/nenu/2018fall/homework/2480 项目地址:https://coding.net/u/wuyy694 ...
- Scrum Meeting 10.28
今天大部分同学仍停留在学习阶段,进度快的同学已经在配置SQLserver. 成员 今日完成任务 明日计划 所用时间 徐越 配置SQLserver,试用java程序连接数据库 学习servlet,htt ...
- 20172329 2018-2019《Java程序设计与数据结构》课程总结
作者:lalalouye(20172329王文彬) 2018-2019年大二Java程序设计与数据结构课程总目录:第一周 第二周 第三周 第四周 第五周 第六周 第七周 第八周 第九周 实验一 实验二 ...
- Chapter 6 面向对象基础
面向对象=对象+类+继承+通信,如果一个软件系统采用这些概念来建立模型并给予实现,那么它就是面向对象的.面向对象的软件工程方法是面向对象方法在软件工程领域的全面运用涉及到从面向对象分析.面向对象设计. ...
- 软工实践-Beta 冲刺 (1/7)
队名:起床一起肝活队 组长博客:博客链接 作业博客:班级博客本次作业的链接 组员情况 组员1(队长):白晨曦 过去两天完成了哪些任务 描述: 1.界面的修改与完善 展示GitHub当日代码/文档签入记 ...
- 图论 Kruskal算法 并查集
#include<iostream> #include<cstring> #include<string> #include<cstdio> #incl ...
- 团队作业之404 Note Found Team
如果记忆是一个罐头的话,我希望这一罐罐头不会过期----<重庆森林> 404 Note Found Team 如果记忆是一个备忘录的话,别说了,它不会过期----<404 Note ...
- 一致性Hash算法(KetamaHash)的c#实现
Consistent Hashing最大限度地抑制了hash键的重新分布.另外要取得比较好的负载均衡的效果,往往在服务器数量比较少的时候需要增加虚拟节点来保证服务器能均匀的分布在圆环上.因为使用一般的 ...
- 对JAVA RMI的认识
RMI的定义 RPC (Remote Procedure Call):远程方法调用,用于一个进程调用另一个进程中的过程,从而提供了过程的分布能力. RMI(Remote Method Invocati ...
- [BUAA_SE_2017]个人作业-Week1
个人作业-Week1 疑问 教材中说,PM在衡量需求时需要方方面面的能力与研究.可是,当下许多互联网IT公司只承担外包业务,即客户给什么需求就实现什么需求,甚至可能不要求其它先进的功能.此时,开发团队 ...