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

摘要

在机器视觉中,对于图像的处理有时候因为放置的原因导致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);

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

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

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

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

(0)

相关推荐

  • OpenCV实现图像校正功能

    一. 需求分析 首先是需求: 1.利用 OpenCV 里面的仿射变换函 数实现对图像进行一些基本的变换,如平移.旋转.缩放 2.学习透视变换原理,对一个矩形进行透视变换,并将变换结果绘制出来.先调用 OpenCV 函数实现透视变换,自己编写代码实现透视变换. 3.识别一张倾斜拍摄的纸张,找出轮廓,提取出该纸张的位置 4. 假设你已通过图像处理的算法找到发生形变的纸张的位置,那么对这个倾斜 纸张进行变换,得到纸张的垂直视图,实现文档校准. 然后是分析: 1.首先要调用OpenCV的函数对图像进行平

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

    摘要 在机器视觉中,对于图像的处理有时候因为放置的原因导致ROI区域倾斜,这个时候我们会想办法把它纠正为正确的角度视角来,方便下一步的布局分析与文字识别,这个时候通过透视变换就可以取得比较好的裁剪效果. 本次实战,对于图像的矫正使用了两种矫正思路: 针对边缘比较明显的图像,使用基于轮廓提取的矫正算法. 针对边缘不明显,但是排列整齐的文本图像,使用了基于霍夫直线探测的矫正算法. 基于轮廓提取的矫正算法 整体思路: 图片灰度化,二值化 检测轮廓,并筛选出目标轮廓(通过横纵比或面积去除干扰轮廓) 获取

  • Python OpenCV 图像矫正的原理实现

    目录 题目描述 基本思路 核心代码 题目描述 目录hw1下的图像是一些胶片的照片,请将其进行度量矫正. 推荐流程:采用Canny算子,检测边缘点:采用Hough直线检测,根据边缘点检测胶片边缘对应的4条直线:4条直线在图像平面中的交点为胶片图像的4个顶点.根据4个顶点与真实世界中胶片的位置(假设胶片图像长宽比为4:3),得到两个平面之间的单应变换矩阵,并根据单应变换矩阵实现图像矫正. 基本思路 使用Canny算子,检测边缘点:以边缘点作为输入,采用Hough直线检测,检测出最多点共线的四条直线,

  • w3c编程挑战_初级脚本算法实战篇

    之前偶然看到了w3c上的编程挑战题,就像拿来试试手,先做的是初级脚本算法,总体不难,如果有更好的方法,希望能一起交流! 1.翻转字符串 先把字符串转化成数组,再借助数组的reverse方法翻转数组顺序,最后把数组转化成字符串. function reverseString(str) { var str2 = ''; for(var i=str.length-1;i>=0;i--){ str2 += str[i]; } return str2; } function reverseString(s

  • Opencv Hough算法实现图片中直线检测

    本文实例为大家分享了Opencv Hough算法实现直线检测的具体代码,供大家参考,具体内容如下 (1)载入需检测的图及显示原图 Mat g_srcImage = imread("C:\\Users\\lenovo\\Pictures\\Saved Pictures\\Q.jpg"); //图片所放路径 imshow("[原始图]", g_srcImage); (2)为显示不同的效果图而设置滑动条 namedWindow("[效果图]", 1);

  • Python 深入了解opencv图像分割算法

    使用 OpenCV 函数 cv::filter2D 执行一些拉普拉斯滤波以进行图像锐化 使用 OpenCV 函数 cv::distanceTransform 以获得二值图像的派生(derived)表示,其中每个像素的值被替换为其到最近背景像素的距离 使用 OpenCV 函数 cv::watershed 将图像中的对象与背景隔离 加载源图像并检查它是否加载没有任何问题,然后显示它: # Load the image parser = argparse.ArgumentParser(descript

  • Java算法实战之排一亿个随机数

    目录 前言 一.直接插入排序 1. 图解插排 2. 代码实现 3.性能检测与时空复杂度 二.希尔排序(交换法) 1. 思路图解 2. 代码实现 3. 时间复杂度 4. 关于增量的选择 三.希尔排序(移位法) 1. 思路 2. 代码实现 3. 实验结果 总结 前言 插入排序狭义上指的是简单插入排序(选择集合,比较大小,插入元素),广义上还应该包括希尔排序(分治思想)及其两种实现方式, 最激动人心的是 , 希尔排序(移位法)的效率奇高, 在本地调试中,一亿 个随机数仅需30S即可排完 (不同机器可能

  • OpenCV边缘提取算法流程的实现(附DEMO)

    目录 1. 具体算法流程 2.流程图 3. 涉及到的代码 在传统的计算机视觉领域,经常需要使用一些传统的图像处理算法完成对图像的边缘提取功能,通过对图像的边缘进行提取完成对目标对象的分割,目标分割技术又包括语义分割与实例分割,比较高端的鲁棒性较强的还是需要卷积神经网络算法进行相关的训练,如fcn全连接网络,mask-rcnn实例分割网络.本案例旨在采用传统的图像处理技术完成对图像的边缘检测任务,并通过膨胀腐蚀操作进行连通域的提取,之后通过连通域的填充以及掩膜操作完成目标对象的分割. 具体需要达到

  • Opencv分水岭算法学习

    分水岭算法可以将图像中的边缘转化成"山脉",将均匀区域转化为"山谷",这样有助于分割目标. 分水岭算法是一种基于拓扑理论的数学形态学的分割方法,其基本思想是把图像看作是测地学上的拓扑地貌,图像中的每一点像素的灰度值表示该点的海拔高度,每一个局部极小值及其影响区域称为集水盆,而集水盆的边界则形成分水岭.分水岭的概念和形成可以通过模拟浸入过程来说明:在每一个局部极小值表面,刺穿一个小孔,然后把整个模型慢慢浸入水中,随着浸入的加深,每一个局部极小值的影响区域慢慢向外扩展,

  • OpenCV半小时掌握基本操作之分水岭算法

    [OpenCV]⚠️高手勿入! 半小时学会基本操作 ⚠️ 分水岭算法 概述 OpenCV 是一个跨平台的计算机视觉库, 支持多语言, 功能强大. 今天小白就带大家一起携手走进 OpenCV 的世界. 分水岭算法 分水岭算法 (Watershed Algorithm) 是一种图像区域分割算法. 在分割的过程中, 分水岭算法会把跟临近像素间的相似性作为重要的根据. 分水岭分割流程: 读取图片 转换成灰度图 二值化 距离变换 寻找种子 生成 Marker 分水岭变换 距离变换 距离变换 (Distan

  • Python-OpenCV实战:利用 KNN 算法识别手写数字

    目录 前言 手写数字数据集 MNIST 介绍 基准模型--利用 KNN 算法识别手写数字 改进模型1--参数 K 对识别手写数字精确度的影响 改进模型2--训练数据量对识别手写数字精确度的影响 改进模型3--预处理对识别手写数字精确度的影响 改进模型4--使用高级描述符作为图像特征提高 KNN 算法准确率 完整代码 相关链接 前言 K-最近邻 (k-nearest neighbours, KNN) 是监督学习中最简单的算法之一,KNN 可用于分类和回归问题,在博文<Python OpenCV实战

随机推荐