iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >怎么用C++ OpenCV实现文档矫正功能
  • 674
分享到

怎么用C++ OpenCV实现文档矫正功能

2023-06-29 12:06:34 674人浏览 薄情痞子
摘要

这篇文章主要介绍了怎么用c++ OpenCV实现文档矫正功能的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇怎么用C++ OpenCV实现文档矫正功能文章都会有所收获,下面我们一起来看看吧。需

这篇文章主要介绍了怎么用c++ OpenCV实现文档矫正功能的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇怎么用C++ OpenCV实现文档矫正功能文章都会有所收获,下面我们一起来看看吧。

需求

将一个斜着拍摄的文档矫正成正的,如图所示:

怎么用C++ OpenCV实现文档矫正功能

怎么用C++ OpenCV实现文档矫正功能

思路

读取原始图像,若图像太大可以先进行缩放处理,并获取原始图像的宽和高

对图像进行预处理得到边缘,依次进行灰度处理、高斯模糊、边缘检测、膨胀、腐蚀。

找到最大的轮廓,并提取角点

  • 进行降噪处理:检测轮廓面积,只保留大于阈值面积的轮廓

  • 计算每个轮廓的周长,使用DP算法计算出轮廓点的个数,规则为周长*0.02

  • 找到图像中面积最大的,且角点为4的轮廓

将找到的四个角点排列成一个固定的顺序,排列后的顺序为:左上角-右上角-左下角-右下角

  • 将每个点的xy坐标值相加(x+y),左上角的点的坐标和应该是最小的,右下角的点的坐标和应该是最大的

  • 将每个点的xy坐标值相减(x-y),左下角的点的坐标差应该是最小的,右上角的点的坐标差应该是最大的

  • 重新排列四个角点

进行透视变换

  • 根据变换前及变换后的四个角点,创建变换矩阵

  • 根据变换矩阵对图像进行透视变换

若透视变换后有一些毛边,按需要进行裁剪,裁剪后重新调整比例

  • 创建一个矩形用来裁剪,并设定四周裁剪5像素

  • 裁剪后重新调整图像宽高

显示变换后图像

代码

代码中均有详细注释,请仔细阅读

#include <iOStream>#include<opencv2/opencv.hpp>#include <opencv2/highgui.hpp>#include <opencv2/imgproc.hpp>using namespace cv;using namespace std;// 一些定义Mat image_origin,     // 原始图像image_gray,       // 灰度处理后的图像image_blur,       // 高斯模糊处理后的图像image_canny,      // 边缘检测后的图像image_dilate,     // 膨胀后的图像image_erode,      // 腐蚀后的图像image_preprocess, // 预处理后的图像image_trans,      // 透视变换后的图像image_crop;      // 裁剪后的图像vector<Point> origin_points,  // 重新排列前的角点  reorder_points; // 重新排列后的角点    int origin_width = 0, origin_height = 0;Mat PreProcess(const Mat& image, int display){// 灰度处理cvtColor(image, image_gray, COLOR_BGR2GRAY);// 高斯模糊GaussianBlur(image_gray, image_blur, Size(3, 3), 3, 0);// 边缘检测(边缘检测前对图像进行一次高斯模糊)Canny(image_blur, image_canny, 50, 150);// 膨胀和腐蚀(有时进行边缘检测的时候,没有被完全填充,或者无法正确检测,可以用膨胀和腐蚀)// 创建一个用于膨胀和腐蚀的内核,后面的数字越大膨胀的越多(数字要用奇数)Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));// 膨胀dilate(image_canny, image_dilate, kernel);// 腐蚀//erode(image_dilate, image_erode, kernel);// 显示预处理效果if(display == 1){imshow("灰度处理后的图像", image_gray);imshow("高斯模糊后的图像", image_blur);imshow("边缘检测后的图像", image_canny);imshow("膨胀后的图像", image_dilate);//imshow("腐蚀后的图像", image_erode);}else if(display == 2){imshow("预处理后的图像", image_dilate);}return image_dilate;}vector<Point> GetMaxContour(const Mat& img_input){vector<vector<Point>> contours;vector<Vec4i> hierarchy;findContours(img_input, contours, hierarchy, RETR_EXTERNAL, CHaiN_APPROX_SIMPLE);//// 不全输出,在下文只输出角点//drawContours(image, contours, -1, Scalar(255, 0, 255), 2);// 定义轮廓,大小与contours相同,但内层向量中只有角点(例如三角形就是3,四边形就是4,圆形可能七八个)vector<vector<Point>> corners_contours(contours.size());// 定义边界框,大小与contours相同vector<Rect> bounding_box(contours.size());vector<Point> biggest_contours;double max_area = 0;for (int i = 0; i < contours.size(); i++){// 检测轮廓面积double contour_area = contourArea(contours[i]);//cout << area << endl;// 假设图像中有噪声,需要将其过滤,只保留面积大于1000的轮廓if (contour_area > 1000){// 计算每个轮廓的周长double contour_perimeter = arcLength(contours[i], true);// 使用DP算法计算出轮廓点的个数,规则为周长*0.02approxPolyDP(contours[i], corners_contours[i], 0.02 * contour_perimeter, true);// 找到图像中面积最大的,且角点为4的轮廓if (contour_area > max_area && corners_contours[i].size() == 4 ) {//drawContours(image_origin, conPoly, i, Scalar(255, 0, 255), 5);biggest_contours = { corners_contours[i][0],corners_contours[i][1] ,corners_contours[i][2] ,corners_contours[i][3] };max_area = contour_area;}//// 只绘制角点之间的边框线,Debug用,取消注释可以看到检测出的所有边界框//drawContours(image_origin, corners_contours, i, Scalar(255, 0, 255), 2);//rectangle(image_origin, bounding_box[i].tl(), bounding_box[i].br(), Scalar(0, 255, 0), 5);}}// 返回最大的轮廓return biggest_contours;}void DrawPoints(vector<Point> points, const Scalar& color){for (int i = 0; i < points.size(); i++){circle(image_origin, points[i], 10, color, FILLED);putText(image_origin, to_string(i), points[i], FONT_HERSHEY_PLAIN, 4, color, 4);}}vector<Point> ReorderPoints(vector<Point> points){vector<Point> newPoints;vector<int>  sumPoints, subPoints;// OpenCV中左上顶点为(0,0),右为x轴正向,下为y轴正向。for (int i = 0; i < 4; i++){// 将每个点的xy坐标值相加(x+y),左上角的点的坐标和应该是最小的,右下角的点的坐标和应该是最大的sumPoints.push_back(points[i].x + points[i].y);// 将每个点的xy坐标值相减(x-y),左下角的点的坐标差应该是最小的,右上角的点的坐标差应该是最大的subPoints.push_back(points[i].x - points[i].y);}// 重新排列newPoints.push_back(points[min_element(sumPoints.begin(), sumPoints.end()) - sumPoints.begin()]); // 0 和的最小值newPoints.push_back(points[max_element(subPoints.begin(), subPoints.end()) - subPoints.begin()]); // 1 差的最大值newPoints.push_back(points[min_element(subPoints.begin(), subPoints.end()) - subPoints.begin()]); // 2 差的最小值newPoints.push_back(points[max_element(sumPoints.begin(), sumPoints.end()) - sumPoints.begin()]); // 3 和的最大值return newPoints;}Mat PerspectiveTrans(const Mat& img, vector<Point> points, float width, float height ){// 前面经过重新排列,四个角点的顺序为:左上角-右上角-左下角-右下角Point2f src[4] = { points[0],points[1],points[2],points[3] };// 变换后的四个角点Point2f dst[4] = { {0.0f,0.0f},{width,0.0f},{0.0f,height},{width,height} };// 创建变换矩阵Mat matrix = getPerspectiveTransfORM(src, dst);// 透视变换warpPerspective(img, image_trans, matrix, Point(width, height));return image_trans;}int main(){// 1.读取原始图像string path = "res/image_origin.jpg";image_origin = imread(path);//// 若图像太大可以先进行缩放处理//resize(image_origin, image_origin, Size(), 0.5, 0.5);// 获取原始图像的宽和高origin_width  = image_origin.size().width;origin_height = image_origin.size().height;// 2.对图像进行预处理得到边缘,依次进行灰度处理、高斯模糊、边缘检测、膨胀、腐蚀。image_preprocess = PreProcess(image_origin, 0);// 3.找到最大的轮廓,并提取角点origin_points = GetMaxContour(image_preprocess);//DrawPoints(origin_points, Scalar(0, 0, 255)); // 红色// 此时发现,角点的顺序不固定,为了后面进行透视变换时与代码中变换后点集的顺序相同,需要将其排列成一个固定的顺序,排列后的顺序为:左上角-右上角-左下角-右下角reorder_points = ReorderPoints(origin_points);//DrawPoints(reorder_points, Scalar(0, 255, 0)); //绿色// 4.透视变换image_trans = PerspectiveTrans(image_origin, reorder_points, origin_width, origin_height);// 透视变换后有一些毛边,若需要可以进行裁剪// 四周裁剪5像素int cropVal= 5;// 创建一个矩形用来裁剪Rect roi(cropVal, cropVal, origin_width - (2 * cropVal), origin_height - (2 * cropVal));image_crop = image_trans(roi);// 裁剪后重新调整比例resize(image_crop, image_crop, Size(origin_width, origin_height));// 5.显示并输出变换后图像imshow("源图像", image_origin);imshow("最终图像", image_crop);    imwrite("res/image_output.jpg", image_crop);waiTKEy(0);}

效果

怎么用C++ OpenCV实现文档矫正功能

关于“怎么用C++ OpenCV实现文档矫正功能”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“怎么用C++ OpenCV实现文档矫正功能”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注编程网其他教程频道。

--结束END--

本文标题: 怎么用C++ OpenCV实现文档矫正功能

本文链接: https://www.lsjlt.com/news/324854.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 怎么用C++ OpenCV实现文档矫正功能
    这篇文章主要介绍了怎么用C++ OpenCV实现文档矫正功能的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇怎么用C++ OpenCV实现文档矫正功能文章都会有所收获,下面我们一起来看看吧。需...
    99+
    2023-06-29
  • opencv实现文档矫正
    本文实例为大家分享了opencv实现文档矫正的具体代码,供大家参考,具体内容如下 原始文档 矫正后文档 思路: 只要获得倾斜文档的倾斜角度,然后通过仿射变化旋转一下就可以实现矫正...
    99+
    2022-11-11
  • OpenCV怎么通过透视变换实现矫正图像
    这篇“OpenCV怎么通过透视变换实现矫正图像”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“OpenCV怎么通过透视变换实现...
    99+
    2023-07-05
  • 怎么用C#实现合并Word文档功能
    本文小编为大家详细介绍“怎么用C#实现合并Word文档功能”,内容详细,步骤清晰,细节处理妥当,希望这篇“怎么用C#实现合并Word文档功能”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。程序环境本次测试时,在程序...
    99+
    2023-07-04
  • C#怎么实现完整文档打印功能
    这篇文章主要讲解了“C#怎么实现完整文档打印功能”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C#怎么实现完整文档打印功能”吧!C#打印程序实现完整文档打印功能,我们会介绍打印操作具体包括的...
    99+
    2023-06-17
  • 利用C#实现合并Word文档功能
    目录程序环境通过插入完整文件来合并文档 完整代码效果图通过克隆内容合并文档 完整代码效果图合并Word文档可以快速地将多份编辑好的文档合在一起,避免复制粘贴时遗漏...
    99+
    2022-12-08
    C#合并Word文档 C#合并Word C# 合并 文档
  • C++ OpenCV怎么实现形状识别功能
    本篇内容主要讲解“C++ OpenCV怎么实现形状识别功能”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“C++ OpenCV怎么实现形状识别功能”吧!一、图像预处理原图如图所...
    99+
    2023-07-02
  • 怎么用QT+OpenCV实现录屏功能
    这篇“怎么用QT+OpenCV实现录屏功能”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“怎么用QT+OpenCV实现录屏功能...
    99+
    2023-06-26
  • 手把手教你利用opencv实现人脸识别功能(附源码+文档)
    目录一、环境二、使用Haar级联进行人脸检测三、Haar级联结合摄像头四、使用SSD的人脸检测五、 SSD结合摄像头人脸检测六、结语一、环境 pip install opencv...
    99+
    2022-11-12
  • spring boot怎么实现自动输出word文档功能
    这篇文章主要介绍了spring boot怎么实现自动输出word文档功能,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。springboot是什么springboot一种全新的...
    99+
    2023-06-14
  • C#中怎么实现自动化文档
    这篇文章将为大家详细讲解有关C#中怎么实现自动化文档,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。C#文档自动化初步认识:在C#中文档注释对应的符号是:///。但光使用它还是不能为我们产生代...
    99+
    2023-06-17
  • 怎么在C++中使用opencv实现一个车道线识别功能
    本篇文章为大家展示了怎么在C++中使用opencv实现一个车道线识别功能,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。(一)目前国内外广泛使用的车道线检测方法主要分为两大类:(1) 基于道路特征的车...
    99+
    2023-06-06
  • Winform怎么用分页控件实现导出PDF文档功能
    本篇内容主要讲解“Winform怎么用分页控件实现导出PDF文档功能”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Winform怎么用分页控件实现导出PDF文档功能”吧!1、PDF的导出插件使用...
    99+
    2023-07-05
  • 怎么使用vue-pdf插件实现pdf文档预览功能
    这篇文章主要介绍了怎么使用vue-pdf插件实现pdf文档预览功能的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇怎么使用vue-pdf插件实现pdf文档预览功能文章都会有所收获,下面我们一起来看看吧。vue-p...
    99+
    2023-07-05
  • OpenCV中怎么使用GrabCut实现抠图功能
    这篇文章主要讲解了“OpenCV中怎么使用GrabCut实现抠图功能”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“OpenCV中怎么使用GrabCut实现抠图功能”吧!1、概述grabCut...
    99+
    2023-07-05
  • C#怎么实现文本转语音功能
    这篇文章主要介绍“C#怎么实现文本转语音功能”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“C#怎么实现文本转语音功能”文章能帮助大家解决问题。这种方式的优点在于不会被浏览器限制,在js的文本转语音功...
    99+
    2023-06-29
  • C++怎么实现list功能
    这篇文章主要介绍“C++怎么实现list功能”,在日常操作中,相信很多人在C++怎么实现list功能问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C++怎么实现list功能”的疑惑有所帮助!接下来,请跟着小编...
    99+
    2023-06-20
  • 怎么用C++ OpenCV实现像素画
    这篇文章主要介绍“怎么用C++ OpenCV实现像素画”,在日常操作中,相信很多人在怎么用C++ OpenCV实现像素画问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”怎么用C++ ...
    99+
    2023-06-28
  • 怎么用python opencv实现目标区域裁剪功能
    这篇文章主要介绍“怎么用python opencv实现目标区域裁剪功能”,在日常操作中,相信很多人在怎么用python opencv实现目标区域裁剪功能问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”怎么用py...
    99+
    2023-06-20
  • 怎么使用pdf文档中的橡皮功能
    这篇文章将为大家详细讲解有关怎么使用pdf文档中的橡皮功能,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。使用方法:首先打开pdf软件,点击“Open Pdf”,打开pdf文件;然后选择页面顶部的“smal...
    99+
    2023-06-15
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作