ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

机器视觉Open CV 项目二文档扫描

2021-10-06 20:01:57  阅读:216  来源: 互联网

标签:int conPoly vector 文档 contours 轮廓 Open CV points


目录

一、什么是文档扫描
文档扫描步骤
二、使用的函数、变量介绍
变量介绍
函数介绍
三、实操
1 图像预处理
2 获取图像轮廓
3 提取并标记文档边缘
4 重新排序边缘
5 裁剪修饰边缘


一、什么是文档扫描

文档扫描即对采用不同视角所拍摄到的文本图像,以正视的形式将文本呈现。如下图:
文档扫描前的原始图片 所希望得到的是图片中的A4文档信息,经文档扫描后得到如下:
扫描后的图片

文档扫描步骤

1 将导入的图片经过转灰度、模糊降噪、canny边缘检测、膨胀等图像预处理
2 定义,轮廓变量,获取预处理后的图像轮廓
3 提取轮廓中的文本框部分,通过找最大外矩形轮廓函数
4 使用透视矩阵,使图像位于正视图,修剪边缘

二、使用的函数、变量介绍

变量介绍

vector 说明
vector是向量类型,可以容纳许多类型的数据,因此也被称为容器(可以理解为动态数组,是封装好了的类)
进行vector操作前应添加头文件#include 具体可以查看
vector< Point >
vector容器里面放二维坐标点
vector<vector< Point >>
vector容器里面放了一个vector容器,子容器里放二维坐标点
vector< Vec4i > vector< int >
vector容器中放4维int向量
Point2f 二维坐标点
Rect 像素width * height from 位置(x*y)
在这里插入图片描述RotateRect [angle,center[0,0],size[width*height]]
在这里插入图片描述

函数介绍

findContours(image, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
对findContours函数的具体内容可以查看findContours函数参数详解
简单说明如下:
第一个image :为输入图像,可为灰度图,二值图常用,一般为经过Canny等边缘检测
第二个countors 定义为“vector<vector> 型向量,并且是一个双重向量,第一重向量对应轮廓元素,例如countours[i] 为第i个轮廓元素。且轮廓元素也为向量,保存的数据类型为Point类型。例如countours[i][j]为第i个轮廓的第J个特征点。
第三个 hierarchy 定义为vector < Vec4i >类型,即向量内每一个元素包括4个int型变量。vector分别表示第 i个轮廓的后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号。
第四个为int型的mode 定义轮廓检索模式
取值一:CV_RETR_EXTERNAL只检测最外围轮廓,包含在外围轮廓内的内围轮廓被忽略
取值二:CV_RETR_LIST 检测所有的轮廓,包括内围、外围轮廓,但是检测到的轮廓不建立等级关 系,彼此之间独立,没有等级关系,这就意味着这个检索模式下不存在父轮廓或内嵌轮廓, 所以hierarchy向量内所有元素的第3、第4个分量都会被置为-1。
取值三:CV_RETR_CCOMP 检测所有的轮廓,但所有轮廓只建立两个等级关系,外围为顶层,若外围 内的内围轮廓还包含了其他的轮廓信息,则内围内的所有轮廓均归属于顶层
取值四:CV_RETR_TREE, 检测所有轮廓,所有轮廓建立一个等级树结构。外层轮廓包含内层轮廓,内层轮廓还可以继续包含内嵌轮廓。
第五个参数:int型的method,定义轮廓的近似方法:
取值一:CV_CHAIN_APPROX_NONE 保存物体边界上所有连续的轮廓点到contours向量内
取值二:CV_CHAIN_APPROX_SIMPLE 仅保存轮廓的拐点信息,把所有轮廓拐点处的点保存入contours 向量内,拐点与拐点之间直线段上的信息点不予保留。
取值三和四: CV_CHAIN_APPROX_TC89_L1CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法
第六个参数: Point偏移量 所有点相对移动一段距离,本项目未使用


approxPolyDP(contours[i], conPoly[i], 0.02 * peri, true);
void approxPolyDP(InputArray curve, OutputArray approxCurve, double epsilon, bool closed);//找出轮廓的多边形拟合曲线
第一个参数 InputArray curve:输入的点集
第二个参数OutputArray approxCurve:输出的点集,当前点集是能最小包容指定点集的。画出来即是一个多边形。
第三个参数double epsilon:指定的精度,也即是原始曲线与近似曲线之间的最大距离,根据周长来计算。
第四个参数bool closed:若为true,则说明近似曲线是闭合的;反之,若为false,则断开。


double arcLength( InputArray curve, bool closed );计算轮廓周长
InputArray类型的curve,输入的向量,二维点(轮廓顶点),可以为std::vector或Mat类型。
bool类型的closed,用于指示曲线是否封闭的标识符,一般设置为true

三、实操

1 图像预处理

Mat preProcessing(Mat img)
{
	cvtColor(img, imgGray, COLOR_BGR2GRAY);
	GaussianBlur(imgGray, imgBlur, Size(3, 3), 3, 0);
	Canny(imgBlur, imgCanny, 25, 75);
	Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
	dilate(imgCanny, imgDil, kernel);
	//erode(imgDil, imgErode, kernel);
	return imgDil;
}

2 获取图像轮廓

vector<vector<Point>> contours;
	vector<Vec4i> hierarchy;

	findContours(image, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);

3 提取并标记文档边缘

	vector<vector<Point>> conPoly(contours.size());
	vector<Rect> boundRect(contours.size());
	vector<Point> biggest;
	int maxArea = 0;
	for (int i = 0; i < contours.size(); i++)
	{
		int area = contourArea(contours[i]);
		string objectType;
		if (area > 1000)
		{
			float peri = arcLength(contours[i], true);
			approxPolyDP(contours[i], conPoly[i], 0.02 * peri, true);
			if (area > maxArea && conPoly[i].size() == 4) {
				biggest = { conPoly[i][0],conPoly[i][1] ,conPoly[i][2] ,conPoly[i][3] };
				maxArea = area;
			}
		}
	}
	return biggest;
}

4 重新排序边缘

vector<Point> reorder(vector<Point> points)
{
	vector<Point> newPoints;
	vector<int> sumPoints, subPoints;
	for (int i = 0; i < 4; i++)
	{
		sumPoints.push_back(points[i].x + points[i].y);
		subPoints.push_back(points[i].x - points[i].y);
	}
	//cout << sumPoints.begin() << endl;
	newPoints.push_back(points[min_element(sumPoints.begin(), sumPoints.end())-sumPoints.begin()]); // 0
	//cout << newPoints[0].x << endl;
	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
	cout << sizeof(newPoints) << endl;
	return newPoints;
}

其中push_back为在容器后添加一个元素,min_elment和max_element分别为查找最大最小值。

5 绘制裁剪修饰边缘

得到透视投影后的图像

Mat getWarp(Mat img, vector<Point> points, float w, float h)
{
	Point2f src[4] = { points[0],points[1],points[2],points[3] }; //初始变化四周
	Point2f dst[4] = { {0.0f,0.0f},{w,0.0f},{0.0f,h},{w,h} }; //变化后的四周
	Mat matrix = getPerspectiveTransform(src, dst); //得到变化矩阵
	warpPerspective(img, imgWarp, matrix, Point(w, h));   //我,h为变化后的像素大小
	return imgWarp;
} 

得到图像结果如下:
在这里插入图片描述可知四周仍有瑕疵,故对其修剪得到如下:

	int cropVal = 5;
	Rect roi(cropVal, cropVal, w-(2 * cropVal), h-(2 * cropVal));
	imgCrop = imgWarp(roi);

最终得到运行结果如下:
在这里插入图片描述最后代码如下:

#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>

using namespace cv;
using namespace std;

/// Project 2 – Document Scanner //

Mat imgOriginal, imgGray, imgBlur, imgCanny, imgThre, imgDil, imgErode, imgWarp, imgCrop;
vector<Point> initialPoints, docPoints;
float w = 420, h = 596;

Mat preProcessing(Mat img)
{
	cvtColor(img, imgGray, COLOR_BGR2GRAY);
	GaussianBlur(imgGray, imgBlur, Size(3, 3), 3, 0);
	Canny(imgBlur, imgCanny, 25, 75);
	Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
	dilate(imgCanny, imgDil, kernel);
	//erode(imgDil, imgErode, kernel);
	return imgDil;
}

vector<Point> getContours(Mat image) {

	vector<vector<Point>> contours;
	vector<Vec4i> hierarchy;

	findContours(image, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
	//drawContours(img, contours, -1, Scalar(255, 0, 255), 2);
	vector<vector<Point>> conPoly(contours.size());
	vector<Rect> boundRect(contours.size());

	vector<Point> biggest;
	int maxArea = 0;

	for (int i = 0; i < contours.size(); i++)
	{
		int area = contourArea(contours[i]);
		//cout << area << endl;

		string objectType;

		if (area > 1000)
		{
			float peri = arcLength(contours[i], true);
			approxPolyDP(contours[i], conPoly[i], 0.02 * peri, true);

			if (area > maxArea && conPoly[i].size() == 4) {

				//drawContours(imgOriginal, conPoly, i, Scalar(255, 0, 255), 5);
				biggest = { conPoly[i][0],conPoly[i][1] ,conPoly[i][2] ,conPoly[i][3] };
				maxArea = area;
			}
			//drawContours(imgOriginal, conPoly, i, Scalar(255, 0, 255), 2);
			//rectangle(imgOriginal, boundRect[i].tl(), boundRect[i].br(), Scalar(0, 255, 0), 5);
		}
	}
	return biggest;
}

void drawPoints(vector<Point> points, Scalar color)
{
	for (int i = 0; i < points.size(); i++)
	{
		circle(imgOriginal, points[i], 10, color, FILLED);
		putText(imgOriginal, to_string(i), points[i], FONT_HERSHEY_PLAIN, 4, color, 4);
	}
}

vector<Point> reorder(vector<Point> points)
{
	vector<Point> newPoints;
	vector<int> sumPoints, subPoints;

	for (int i = 0; i < 4; i++)
	{
		sumPoints.push_back(points[i].x + points[i].y);
		subPoints.push_back(points[i].x - points[i].y);
	}
	//cout << sumPoints.begin() << endl;
	newPoints.push_back(points[min_element(sumPoints.begin(), sumPoints.end())-sumPoints.begin()]); // 0
	//cout << newPoints[0].x << endl;
	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
	cout << sizeof(newPoints) << endl;
	return newPoints;
}

Mat getWarp(Mat img, vector<Point> points, float w, float h)
{
	Point2f src[4] = { points[0],points[1],points[2],points[3] };
	Point2f dst[4] = { {0.0f,0.0f},{w,0.0f},{0.0f,h},{w,h} };

	Mat matrix = getPerspectiveTransform(src, dst);
	warpPerspective(img, imgWarp, matrix, Point(w, h));

	return imgWarp;
}

void main() {

	string path = "resources/paper.jpg";
	imgOriginal = imread(path);
	resize(imgOriginal, imgOriginal, Size(), 0.5, 0.5);

	// Preprpcessing – Step 1
	imgThre = preProcessing(imgOriginal);

	// Get Contours – Biggest – Step 2
	initialPoints = getContours(imgThre);
	//drawPoints(initialPoints, Scalar(0, 0, 255));
	docPoints = reorder(initialPoints);
	//drawPoints(docPoints, Scalar(0, 255, 0));

	// Warp – Step 3
	imgWarp = getWarp(imgOriginal, docPoints, w, h);

	//Crop – Step 4
	int cropVal = 5;
	Rect roi(cropVal, cropVal, w-(2 * cropVal), h-(2 * cropVal));
	imgCrop = imgWarp(roi);

	imshow("Image", imgOriginal);
	//imshow("Image Dilation", imgThre);
	imshow("Image Warp", imgWarp);
	imshow("Image Crop", imgCrop);
	waitKey(0);

}

标签:int,conPoly,vector,文档,contours,轮廓,Open,CV,points
来源: https://blog.csdn.net/hfutchenyu/article/details/120624876

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有