深入探讨opencv图像矫正算法实战


Posted in Python onMay 21, 2021

摘要

在机器视觉中,对于图像的处理有时候因为放置的原因导致ROI区域倾斜,这个时候我们会想办法把它纠正为正确的角度视角来,方便下一步的布局分析与文字识别,这个时候通过透视变换就可以取得比较好的裁剪效果。

本次实战,对于图像的矫正使用了两种矫正思路:

  • 针对边缘比较明显的图像,使用基于轮廓提取的矫正算法。
  • 针对边缘不明显,但是排列整齐的文本图像,使用了基于霍夫直线探测的矫正算法。

基于轮廓提取的矫正算法

整体思路:

  • 图片灰度化,二值化
  • 检测轮廓,并筛选出目标轮廓(通过横纵比或面积去除干扰轮廓)
  • 获取目标轮廓的最小外接矩形
  • 获取最小外接矩形的四顶点,并定义矫正图像后的四顶点
  • 透视变换(四点变换)

opencv实现(分解步骤):

(一)图片灰度化,二值化(开运算,消除噪点)

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);

深入探讨opencv图像矫正算法实战

深入探讨opencv图像矫正算法实战

注意:由于原图像背景是白色,因此二值化时候要用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图像矫正算法实战

深入探讨opencv图像矫正算法实战

基于霍夫直线探测的矫正算法

对于文本图像(如图),它没有明显的轮廓边缘去求四顶点。但是经过深入分析,可以发现:文本的每一行文字都是呈一条直线,而且这些直线都是平行的!

深入探讨opencv图像矫正算法实战

利用这个特征就可以实现基于霍夫直线探测的矫正算法:

用霍夫线变换探测出图像中的所有直线计算出每条直线的倾斜角,求他们的平均值根据倾斜角旋转矫正

?先来看看什么是霍夫变换:

霍夫变换在检测各种形状的的技术中非常流行,如果你要检测的形状可以用数学表达式写出,你就可以是使用霍夫变换检测它。

霍夫变换的直线检测简单来说就是在空间坐标系和映射到另外一个参数空间,将空间坐标系中的每一个点映射到另外一个参数空间中的线,通过该参数空间中所有线的交叉次数得到实际空间坐标系中的直线。

在OpenCV中,使用Hough变换的直线检测在函数HoughLines和HoughLinesP中实现。

HoughLines函数(标准霍夫变换)

从平面坐标转换到霍夫空间,最终输出是找到直线的极坐标(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函数(霍夫变换直线概率)

从平面坐标转换到霍夫空间,最终输出是找到直线的起点和终点(直角坐标系)

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

opencv实现(分解步骤):

(一)图片灰度化,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);

深入探讨opencv图像矫正算法实战

深入探讨opencv图像矫正算法实战

(二) 霍夫直线检测(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;
    }

深入探讨opencv图像矫正算法实战

?核心代码分析:

由于需要求解直线的倾斜角度,因此这里使用了HoughLines函数,返回的是直线的步长和弧度(极坐标系下)

通过极坐标系下的步长和弧度,可以转换到直接坐标系下的两点坐标,然后显示。(原理如图)

深入探讨opencv图像矫正算法实战

(三)根据倾斜角度,进行放射变换(逆时针旋转矫正)

//旋转中心为图像中心    
    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);

深入探讨opencv图像矫正算法实战

到此这篇关于深入探讨opencv图像矫正算法实战的文章就介绍到这了,更多相关opencv图像矫正内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
Python动态加载模块的3种方法
Nov 22 Python
Python调用C语言开发的共享库方法实例
Mar 18 Python
利用python编写一个图片主色转换的脚本
Dec 07 Python
Python解析并读取PDF文件内容的方法
May 08 Python
Python实现爬虫爬取NBA数据功能示例
May 28 Python
python3.4控制用户输入与输出的方法
Oct 17 Python
Python 数值区间处理_对interval 库的快速入门详解
Nov 16 Python
Python虚拟环境的原理及使用详解
Jul 02 Python
python定位xpath 节点位置的方法
Aug 27 Python
Python 求数组局部最大值的实例
Nov 26 Python
浅谈python的elementtree模块处理中文注意事项
Mar 06 Python
使用python把xmind转换成excel测试用例的实现代码
Oct 12 Python
python正则表达式re.search()的基本使用教程
pandas:get_dummies()与pd.factorize()的用法及区别说明
python spilt()分隔字符串的实现示例
教你用python实现一个无界面的小型图书管理系统
一篇文章带你搞懂Python类的相关知识
Python深度学习之Pytorch初步使用
我对PyTorch dataloader里的shuffle=True的理解
You might like
php 数据库字段复用的基本原理与示例
2011/07/22 PHP
奉献出一个封装的curl函数 便于调用(抓数据专用)
2013/07/22 PHP
php+mysqli实现批量替换数据库表前缀的方法
2014/12/29 PHP
CI框架出现mysql数据库连接资源无法释放的解决方法
2016/05/17 PHP
PHP实现的最大正向匹配算法示例
2017/12/19 PHP
js的正则test,match,exec详细解析
2014/01/29 Javascript
Js获取下拉框选定项的值和文本的实现代码
2014/02/26 Javascript
在css加载完毕后自动判断页面是否加入css或js文件
2014/09/10 Javascript
javascript抽象工厂模式详细说明
2014/12/16 Javascript
javascript去除字符串左右两端的空格
2015/02/05 Javascript
PHP获取当前页面完整URL的方法
2016/12/02 Javascript
Vue数据驱动模拟实现4
2017/01/12 Javascript
jquery PrintArea 实现票据的套打功能(代码)
2017/03/17 Javascript
JS实现小球的弹性碰撞效果
2017/11/11 Javascript
Node.js实现注册邮箱激活功能的方法示例
2018/03/23 Javascript
JavaScript中var、let、const区别浅析
2018/06/24 Javascript
微信小程序实现无限滚动列表
2020/05/29 Javascript
浅谈Vue.js 中的 v-on 事件指令的使用
2018/11/25 Javascript
详解在React-Native中持久化redux数据
2019/05/22 Javascript
在layui下对元素进行事件绑定的实例
2019/09/06 Javascript
vue-simple-uploader上传成功之后的response获取代码
2020/09/07 Javascript
JavaScript封装单向链表的示例代码
2020/09/17 Javascript
python写xml文件的操作实例
2014/10/05 Python
Python全局变量操作详解
2015/04/14 Python
Python修改MP3文件的方法
2015/06/15 Python
Django中处理出错页面的方法
2015/07/15 Python
Python叠加两幅栅格图像的实现方法
2019/07/05 Python
python的reverse函数翻转结果为None的问题
2020/05/11 Python
Python新手学习函数默认参数设置
2020/06/03 Python
python+opencv实现车道线检测
2021/02/19 Python
技校毕业生个人学习的自我评价
2014/02/21 职场文书
优秀团员事迹材料1500字
2014/08/31 职场文书
教师群众路线心得体会
2014/11/04 职场文书
2015年暑期社会实践活动总结
2015/03/27 职场文书
魂断蓝桥观后感
2015/06/10 职场文书
端午节将至,用Python爬取粽子数据并可视化,看看网友喜欢哪种粽子吧!
2021/06/11 Python