【OpenCV3.3】通过透视变换矫正变形图像
2017-08-18 17:13
597 查看
在平面图像处理中,因为镜头角度等原因,容易导致图像出现倾斜、变形等情况,为了方便后续处理我们常常需要进行图像矫正,其中主要技术原理是两种变换类型--仿射变换(Affine Transformation)和透视变换(Perspective Transformation)。
仿射变换是二维坐标间的线性变换, 故而变换后的图像仍然具有原图的一些性质,包括“平直性”以及“平行性”,常用于图像翻转(Flip)、旋转(Rotations)、平移(Translations)、缩放(Scale operations)等,具体到代码应用可以参见OpenCV官方介绍。说句题外话,如果没记错的话,仿射变换在高中数学的选修部分就出现过,它可以解决一些初等几何问题,比如部分椭圆题,使用仿射变换往往能得到优雅解法。
但是仿射变换不能矫正一些变形,如矩形区域的部分发生变化最终变成梯形,这时候矫正就需要用到透视变换。透视变换(Perspective Transformation),又称投影映射(Projective Mapping)、投射变换等,是三维空间上的非线性变换,可看作是仿射变换的更一般形式,简单讲即通过一个3x3的变换矩阵将原图投影到一个新的视平面(Viewing
Plane),在视觉上的直观表现就是产生或消除了远近感。落实到OpenCV,图像的透视变换由以下函数完成(该函数是针对图像的包装,其本质调用cv::perspectiveTransform进行向量坐标的变换):
当WARP_INVERSE_MAP被指定时,warpPerspective使用输入的矩阵M对图像src进行如下变换(图片来自文档截图):
否则,方法先计算矩阵M的倒置矩阵T,然后将T应用到上述形式的变换。该变换不能在原地进行(须分配额外空间)。
关于变换矩阵M,OpenCV提供了两种方法计算,getPerspectiveTransform和findHomography,前者虽然有2个重载函数,但其实都是一样的形式,通过原图和变换后图像的4个对应点(即对应的四边形)计算出透视变换矩阵;后者则相对比较复杂,属于calib3d模块的内容,概括而言即通过变换前、后两个平面的点寻找出一个单应性变换矩阵H,满足
,使得反向投影误差
最小;这些在计算机视觉相关课程里应有详细介绍。
下面来看一个具体例子:
仿射变换是二维坐标间的线性变换, 故而变换后的图像仍然具有原图的一些性质,包括“平直性”以及“平行性”,常用于图像翻转(Flip)、旋转(Rotations)、平移(Translations)、缩放(Scale operations)等,具体到代码应用可以参见OpenCV官方介绍。说句题外话,如果没记错的话,仿射变换在高中数学的选修部分就出现过,它可以解决一些初等几何问题,比如部分椭圆题,使用仿射变换往往能得到优雅解法。
但是仿射变换不能矫正一些变形,如矩形区域的部分发生变化最终变成梯形,这时候矫正就需要用到透视变换。透视变换(Perspective Transformation),又称投影映射(Projective Mapping)、投射变换等,是三维空间上的非线性变换,可看作是仿射变换的更一般形式,简单讲即通过一个3x3的变换矩阵将原图投影到一个新的视平面(Viewing
Plane),在视觉上的直观表现就是产生或消除了远近感。落实到OpenCV,图像的透视变换由以下函数完成(该函数是针对图像的包装,其本质调用cv::perspectiveTransform进行向量坐标的变换):
void cv::warpPerspective ( InputArray src, OutputArray dst, InputArray M, Size dsize, int flags = INTER_LINEAR, int borderMode = BORDER_CONSTANT, const Scalar &borderValue = Scalar(0))其中,src是输入图像,dst是输出图像,M是3x3变换矩阵,dsize是输出图像的大小,flags指定像素插补方法以及矩阵倒置标志cv::WARP_INVERSE_MAP,borderMode指定边沿像素的推算模式,其中BORDER_CONSTANT指示边沿像素用borderValue替换,因为默认是0,所以我们变换后的图像边界可能会出现黑边,此时可以指定BORDER_REPLICATE对边界像素进行复制,即`aaaaaa|abcdefgh|hhhhhhh`(a-h代表像素)。
当WARP_INVERSE_MAP被指定时,warpPerspective使用输入的矩阵M对图像src进行如下变换(图片来自文档截图):
否则,方法先计算矩阵M的倒置矩阵T,然后将T应用到上述形式的变换。该变换不能在原地进行(须分配额外空间)。
关于变换矩阵M,OpenCV提供了两种方法计算,getPerspectiveTransform和findHomography,前者虽然有2个重载函数,但其实都是一样的形式,通过原图和变换后图像的4个对应点(即对应的四边形)计算出透视变换矩阵;后者则相对比较复杂,属于calib3d模块的内容,概括而言即通过变换前、后两个平面的点寻找出一个单应性变换矩阵H,满足
,使得反向投影误差
最小;这些在计算机视觉相关课程里应有详细介绍。
下面来看一个具体例子:
static void testImageRectification(cv::Mat &image_original) { CV_SHOW(image_original); // CV_SHOW是cv::imshow的一个自定义宏,忽略即可 cv::Mat &&image = image_original.clone(); cv::Mat image_gray; cv::cvtColor(image, image_gray, cv::COLOR_BGR2GRAY); cv::threshold(image_gray, image_gray, g_threshVal, g_threshMax, cv::THRESH_BINARY); std::vector< std::vector<cv::Point> > contours_list; { std::vector<cv::Vec4i> hierarchy; // Since opencv 3.2 source image is not modified by this function cv::findContours(image_gray, contours_list, hierarchy, cv::RetrievalModes::RETR_EXTERNAL, cv::ContourApproximationModes::CHAIN_APPROX_NONE); } for (uint32_t index = 0; index < contours_list.size(); ++index) { cv::RotatedRect &&rect = cv::minAreaRect(contours_list[index]); if (rect.size.area() > 1000) { if (rect.angle != 0.) { // 此处可通过cv::warpAffine进行旋转矫正,本例不需要 } //if cv::Mat &mask = image_gray; cv::drawContours(mask, contours_list, static_cast<int>(index), cv::Scalar(255), cv::FILLED); cv::Mat extracted(image_gray.rows, image_gray.cols, CV_8UC1, cv::Scalar(0)); image.copyTo(extracted, mask); CV_SHOW(extracted); std::vector<cv::Point2f> poly; cv::approxPolyDP(contours_list[index], poly, 30, true); // 多边形逼近,精度(即最小边长)设为30是为了得到4个角点 cv::Point2f pts_src[] = { // 此处顺序调整是为了和后面配对,仅作为示例 poly[1], poly[0], poly[3], poly[2] }; cv::Rect &&r = rect.boundingRect(); // 注意坐标可能超出图像范围 cv::Point2f pts_dst[] = { cv::Point(r.x, r.y), cv::Point(r.x + r.width, r.y), cv::Point(r.x + r.width, r.y + r.height) , cv::Point(r.x, r.y + r.height) }; cv::Mat &&M = cv::getPerspectiveTransform(pts_dst, pts_src); // 我这里交换了输入,因为后面指定了cv::WARP_INVERSE_MAP,你可以试试不交换的效果是什么 cv::Mat warp;cv::warpPerspective(image, warp, M, image.size(), cv::INTER_LINEAR + cv::WARP_INVERSE_MAP, cv::BORDER_REPLICATE); CV_SHOW(warp); } //if } }效果截图(原图变形不明显,将就吧,人眼还是很强大的):
相关文章推荐
- 用opencv计算棋盘格内角点坐标(通过多个内角点获得转换矩阵),并同时用halcon和opnecv对图像进行透视变换
- 用opencv计算棋盘格内角点坐标(通过4个内角点获得转换矩阵),并同时用halcon和opnecv对图像进行透视变换
- case5 opencv中透视变换结合直线检测对图像进行校正
- Python3与OpenCV3.3 图像处理(十三)--反射投影
- opencv 通过指针访问图像像素值,输出为空的问题
- Python3与OpenCV3.3 图像处理(十七)--图像梯度
- Python3与OpenCV3.3 图像处理(二十一)--轮廓发现
- Atitit 图像处理 调用opencv 通过java api attilax总结
- OpenCV的+安卓+号牌识别(OpenCV + Android + 图像水平矫正)
- Python3与OpenCV3.3 图像处理(十一)--图像直方图
- OpenCV 通过 MFC 的 Picture Control 控件操作图像
- 在MFC中通过保存对话框保存opencv处理后图像
- Python3与OpenCV3.3 图像处理(二)--图像基本操作
- Atitit 图像处理 调用opencv 通过java api attilax总结
- OpenCV3.3深度学习模块(DNN)应用-图像分类
- 【OpenCV3.3】检测图像中的身份证区域
- Opencv中使用Surf特征实现图像配准及对透视变换矩阵H的平移修正
- Python3与OpenCV3.3 图像处理(八)--模糊
- opencv-ios开发笔记9 使用透视变换矫正扭曲的图片