opencv图像矫正的思路及实现是什么
Admin 2022-06-29 群英技术资讯 359 次浏览
在机器视觉中,对于图像的处理有时候因为放置的原因导致ROI区域倾斜,这个时候我们会想办法把它纠正为正确的角度视角来,方便下一步的布局分析与文字识别,这个时候通过透视变换就可以取得比较好的裁剪效果。
本次实战,对于图像的矫正使用了两种矫正思路:
(一)图片灰度化,二值化(开运算,消除噪点)
Mat src = imread("D:/opencv练习图片/图片矫正.png"); imshow("原图片", src); // 二值图像 Mat gray, binary; cvtColor(src, gray, COLOR_BGR2GRAY); threshold(gray, binary, 0, 255, THRESH_BINARY_INV| THRESH_OTSU); imshow("二值化", binary); // 定义结构元素 Mat se = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1)); morphologyEx(binary, binary, MORPH_OPEN, se); imshow("开运算", binary);
注意:由于原图像背景是白色,因此二值化时候要用THRESH_BINARY_INV
(二)提取轮廓,筛选轮廓
// 寻找最大轮廓 vector<vector<Point>> contours; findContours(binary, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE); int index = -1; int max = 0; for (size_t i = 0; i < contours.size(); i++) { double area = contourArea(contours[i]); if (area > max) { max = area; index = i; } }
(三)求取最小外接矩形以及四顶点坐标,并定义变换后的四顶点坐标
// 寻找最小外接矩形 RotatedRect rect = minAreaRect(contours[index]); Point2f srcpoint[4];//存放变换前四顶点 Point2f dstpoint[4];//存放变换后四顶点 rect.points(srcpoint);//获取最小外接矩形四顶点坐标 //显示顶点 for (size_t i = 0; i < 4; i++) { circle(src, srcpoint[i], 5, Scalar(0, 0, 255),-1);//-1表示填充 } imshow("顶点坐标", src); //获取外接矩形宽高 float width = rect.size.width; float height = rect.size.height; //定义矫正后四顶点 dstpoint[0]= Point2f(0, height); dstpoint[1] = Point2f(0, 0); dstpoint[2] = Point2f(width, 0); dstpoint[3] = Point2f(width, height);
这里需要注意的是:
RotatedRect 类的矩形返回的是矩形的中心坐标,倾斜角度。
Rect类的矩形返回的是矩形的左上角坐标,宽,高。因此要获取RotatedRect 类的矩形的宽,高就要用:
//获取外接矩形宽高 float width = rect.size.width; float height = rect.size.height;
获取RotatedRect 类四顶点坐标的顺序依次是:左下-左上-右上-右下(可通过显示顶点依次查看)
对应矫正后的四顶点就是:(0,height)-(0,0)-(width,0)-(width,height)
(四)透视变换
// 透视变换 Mat M = getPerspectiveTransform(srcpoint, dstpoint); Mat result = Mat::zeros(Size(width, height), CV_8UC3); warpPerspective(src, result, M, result.size()); imshow("矫正结果", result);
对于文本图像(如图),它没有明显的轮廓边缘去求四顶点。但是经过深入分析,可以发现:文本的每一行文字都是呈一条直线,而且这些直线都是平行的!
利用这个特征就可以实现基于霍夫直线探测的矫正算法:
用霍夫线变换探测出图像中的所有直线计算出每条直线的倾斜角,求他们的平均值根据倾斜角旋转矫正
先来看看什么是霍夫变换:
霍夫变换在检测各种形状的的技术中非常流行,如果你要检测的形状可以用数学表达式写出,你就可以是使用霍夫变换检测它。
霍夫变换的直线检测简单来说就是在空间坐标系和映射到另外一个参数空间,将空间坐标系中的每一个点映射到另外一个参数空间中的线,通过该参数空间中所有线的交叉次数得到实际空间坐标系中的直线。
在OpenCV中,使用Hough变换的直线检测在函数HoughLines和HoughLinesP中实现。
从平面坐标转换到霍夫空间,最终输出是找到直线的极坐标(r,θ)
HoughLines( InputArray src, // 输入图像,必须CV_8U的二值图像(常用canny处理后的二值图像) OutputArray lines, // 输出的极坐标来表示直线 double rho, // 步长(常为1) double theta, //角度,(一般是CV_PI/180) int threshold, // 阈值,只有获得足够交点的极坐标点才被看成是直线 double min_theta=0, // 表示角度扫描范围 0 ~180之间, 默认即可 double max_theta=CV_PI) // 一般情况是有经验的开发者使用,需要自己反变换到平面空间
从平面坐标转换到霍夫空间,最终输出是找到直线的起点和终点(直角坐标系)
HoughLinesP( InputArray src, // 输入图像,必须CV_8U的二值图像 OutputArray lines, // 输出找到直线的两点 double rho, // 步长(半径,常设为1) double theta, //角度,一般取值CV_PI/180 Int threshold, // 阈值,累计次数必须达到的值,一般为150 double minLineLength=0,// 最小直线长度,一般为50 double maxLineGap=0)// 最大间隔,一般为10
(一)图片灰度化,Canny边缘提取
Mat src, src_edge, src_gray,src_rotate; double angle; src = imread("D:/opencv练习图片/文本矫正.png"); imshow("文本图片", src); cvtColor(src, src_gray, COLOR_RGB2GRAY); Canny(src_gray, src_edge, 50, 200, 3); imshow("canny", src_edge);
(二) 霍夫直线检测(HoughLines函数)并显示
//通过霍夫变换检测直线 vector<Vec2f> plines; //第5个参数就是阈值,阈值越大,检测精度越高 HoughLines(src_edge, plines, 1, CV_PI / 180, 200, 0, 0); cout << plines.size() << endl; //由于图像不同,阈值不好设定,因为阈值设定过高导致无法检测直线,阈值过低直线太多,速度很慢 //所以根据阈值由大到小设置了三个阈值,如果经过大量试验后,可以固定一个适合的阈值。 float sum = 0; //依次画出每条线段 for (size_t i = 0; i < plines.size(); i++) { float rho = plines[i][0]; float theta = plines[i][1]; Point pt1, pt2; double a = cos(theta), b = sin(theta); double x0 = a * rho, y0 = b * rho; pt1.x = cvRound(x0 + 1000 * (-b));//cvRound四舍五入 pt1.y = cvRound(y0 + 1000 * (a)); pt2.x = cvRound(x0 - 1000 * (-b)); pt2.y = cvRound(y0 - 1000 * (a)); sum += theta; line(src_gray, pt1, pt2, Scalar(55, 100, 195), 1, LINE_AA);//Scalar函数用于调节线段颜色 imshow("直线探测效果图", src_gray); float average = sum / plines.size(); //对所有角度求平均,这样做旋转效果会更好 angle = DegreeTrans(average) - 90; }
核心代码分析:
由于需要求解直线的倾斜角度,因此这里使用了HoughLines函数,返回的是直线的步长和弧度(极坐标系下)
通过极坐标系下的步长和弧度,可以转换到直接坐标系下的两点坐标,然后显示。(原理如图)
(三)根据倾斜角度,进行放射变换(逆时针旋转矫正)
//旋转中心为图像中心 Point2f center; center.x = float(src.cols / 2.0); center.y = float(src.rows / 2.0); int length = 0; length = sqrt(src.cols*src.cols + src.rows*src.rows); Mat M = getRotationMatrix2D(center, angle, 1); warpAffine(src, src_rotate, M, Size(length, length), 1, 0, Scalar(255, 255, 255));//仿射变换,背景色填充为白色 imshow("矫正后", src_rotate);
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。
猜你喜欢
这篇文章主要为大家介绍了python神经网络使用slim函数进行模型的训练及保存模型示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
反射即想到4个内置函数分别为:getattr、hasattr、setattr、delattr 获取成员、检查成员、设置成员、删除成员classDog(object):def__init__
这篇文章主要介绍了Python中列表索引 A[ : 2 ]与A[ : , 2]的区别说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
在一般情况下,语句是顺序执行的:在一个函数的第一条语句,首先执行,然后是第二条,第三条...等等。可能有一种情况,当你需要执行代码块几次或多数。
当想将照片序列合成延时摄影视频时,可能会发现照片中缺少一张,或者照片序列是跨时间、并不连续的,如图1所示,但PR中只有连续的照片序列才能导入。
成为群英会员,开启智能安全云计算之旅
立即注册Copyright © QY Network Company Ltd. All Rights Reserved. 2003-2020 群英 版权所有
增值电信经营许可证 : B1.B2-20140078 粤ICP备09006778号 域名注册商资质 粤 D3.1-20240008