【opencv解数独】数字提取

提取思路:
1、读取图片 imread();
2、转换成灰度图 cvtColor();
3、高斯滤波去除噪音 GaussianBlur();
4、拉普拉斯锐化增强图像 filter2D();
5、图像二值化 threshold();
6、寻找轮廓 findContours();
7、筛选轮廓并绘制外部矩形边界
8、显示图片 imshow();

原图
在这里插入图片描述

处理后的图片
在这里插入图片描述

可以看到所有数字都被找了出来
下面是全部代码

#include "opencv2/opencv.hpp"
#include "iostream"

using namespace cv;
using namespace std;

int Area = 5000;//数字轮廓的最小值
int main()
{
	Mat src = imread("1.png");   //读取图片 1.png
	//imshow("原图", src);          //显示原图

	Mat dst = src.clone();

	//g_vContours是由一系列点构成的矩形,每个图形中又包括许多轮廓
	//g_vContours[i]:表示第 i 个轮廓,
	//g_vContours[i][i]:表示第 i 个轮廓的第 i 个点
	vector<vector<Point>> g_vContours;
	//g_vHierarchy[i][0] :后一个轮廓序号, 没有则-1
	//g_vHierarchy[i][1] :前一个轮廓序号, 没有则-1
	//g_vHierarchy[i][2] :子轮廓的序号, 没有则-1
	//g_vHierarchy[i][3] :父轮廓的序号,没有则 -1
	vector<Vec4i> g_vHierarchy;

	cvtColor(dst, dst, COLOR_BGR2GRAY);//转换成灰度图
	GaussianBlur(dst, dst, Size(5, 5), 0, 0);//高斯滤波
	//拉普拉斯锐化
	Mat kern = (Mat_<char>(3, 3) <<  0, -1, 0,
									-1,  5,-1,
									 0, -1, 0);
	filter2D(dst, dst, -1, kern);

	threshold(dst, dst, 170, 255, CV_THRESH_BINARY);//图像二值化
	
	//imshow("dst", dst);   //显示经过灰度化、滤波、图像增强、二值化后的图像

	Mat frame = dst.clone();
	//Canny(frame, frame, 3, 9, 3);//运行Canny算子检测边缘
	findContours(frame, g_vContours, g_vHierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);//寻找轮廓

	//drawContours(src, g_vContours, -1, Scalar(0,0,255), 2);//绘制轮廓

	for (size_t i = 0; i < g_vContours.size(); i++)
	{   
		//当前轮廓有子轮廓,并且当前轮廓的面积大于5000,面积按实际大小更改
		if (g_vHierarchy[i][2] != -1 && contourArea(g_vContours[i]) > Area)
		{	
			int childIndex = g_vHierarchy[i][2];   //当前轮廓的子轮廓点的序号,即是数字外围的轮廓
			//Rect recttemp = boundingRect(g_vContours[i]);        //第 i 个轮廓的轮廓
			Rect numberbox = boundingRect(g_vContours[childIndex]);//第 i 个轮廓的子轮廓,即包围数字
			//boundingRect()函数会返回四个值,分别是矩阵的左上角坐标 x , y 和矩阵的 宽 和 高 
			rectangle(src, numberbox, Scalar(0,0,255), 2);
		}
	}
	imshow("【标记出数字】", src);
	//imwrite("1.jpg",src);
	cout << "按任意键结束程序" << endl;
	waitKey(0);

	return 0;
}

其他代码就不多解释了,都是一些简单的函数,程序里面也注释得很清楚
我就说一下怎么在所有轮廓里面找出数字的轮廓
核心程序

int Area = 5000;//数字轮廓的最小值
	
//g_vContours是由一系列点构成的矩形,每个图形中又包括许多轮廓
//g_vContours[i]:表示第 i 个轮廓,
//g_vContours[i][i]:表示第 i 个轮廓的第 i 个点
vector<vector<Point>> g_vContours;
//g_vHierarchy[i][0] :后一个轮廓序号, 没有则-1
//g_vHierarchy[i][1] :前一个轮廓序号, 没有则-1
//g_vHierarchy[i][2] :子轮廓的序号, 没有则-1
//g_vHierarchy[i][3] :父轮廓的序号,没有则 -1
vector<Vec4i> g_vHierarchy;
	
findContours(frame, g_vContours, g_vHierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);//寻找轮廓

for (size_t i = 0; i < g_vContours.size(); i++)
	{   
		//当前轮廓有子轮廓,并且当前轮廓的面积大于5000,面积按实际大小更改
		if (g_vHierarchy[i][2] != -1 && contourArea(g_vContours[i]) > Area)
		{	
			int childIndex = g_vHierarchy[i][2];   //当前轮廓的子轮廓点的序号,即是数字外围的轮廓
			Rect numberbox = boundingRect(g_vContours[childIndex]);//第 i 个轮廓的子轮廓,即包围数字
			//boundingRect()函数会返回四个值,分别是矩阵的左上角坐标 x , y 和矩阵的 宽 和 高 
			rectangle(src, numberbox, Scalar(0,0,255), 2);
		}
	}

Area ,g_vContours,g_vHierarchy这三个变量的作用和用法
都在注释里面了
首先调用findContours()函数将所有轮廓保存在g_vContours中
在for循环中遍历所有轮廓
找到有子轮廓并且面积大于5000的轮廓,这个实际上是那91个小方格
面积是我按照图片的大小试验了几次找到的,可按自己的需求更改
找到小方格就好办了,数字的轮廓就是小方格的子轮廓
最后调用rectangle函数画出小矩形就行了

冰不语大佬是找到小方格的条件是
1、父轮廓为0号轮廓
2、有子轮廓的轮廓

这样实现的代码是

for (size_t i = 0; i < g_vContours.size(); i++)
	{   		
		if (g_vHierarchy[i][3] == 0 && g_vHierarchy[i][2] != -1)
		{	
			int childIndex = g_vHierarchy[i][2];   //当前轮廓的子轮廓点的序号,即是数字外围的轮廓
			Rect numberbox = boundingRect(g_vContours[childIndex]);//第 i 个轮廓的子轮廓,即包围数字
			//boundingRect()函数会返回四个值,分别是矩阵的左上角坐标 x , y 和矩阵的 宽 和 高 
			rectangle(src, numberbox, Scalar(0,0,255), 2);
		}
	}

这样便不用判断和更改面积了,理论上可以在任何数独图片上找出数字

不过由于我找到的所有轮廓是这样的
在这里插入图片描述

因此小方格是没有父轮廓的,只能老老实实的用面积来查找了

下面都是大佬写的值得我们借鉴的好文章
OpenCV求解数独
OpenCV玩九宫格数独(一)——九宫格图片中提取数字
【OpenCV 学习之路】(8)数独提取之一
【OpenCV 学习之路】(9)数独提取之二

发布了12 篇原创文章 · 获赞 10 · 访问量 966

猜你喜欢

转载自blog.csdn.net/weixin_45523734/article/details/102501829
今日推荐