————致力于用代码改变世界

OpenCV实战案例——直线检测[C++]

0.前言
本文以实战案例为背景,一步步讲述如何使用计算机图像处理相关知识提取图片中英语填空题答题线。

1.需求背景
某公司打算设计一款英语题目批改APP,要求学生上传英语填空题图片,然后该APP自动标注答题线位置(使用红线标注),方便后续定位和批改答案。下图(图1-1)为某一学生上传的英语填空题图片。

该APP的预期效果如下图(图1-2)所示。

2.解决思路

  • 首先应该将图片转换为灰度图再经过阈值处理转换为二值图。
  • 由于图片中存在较多英文字符,如果直接使用霍夫直线检测效果并不显著。
  • 寻找一种方法能够将英文等字符去除,仅留下直线特征。本文采取形态学操作实现该步骤。
  • 将大多干扰因素去除后使用霍夫变换提取直线效果更佳。
  • 最后在原图像中标注即可。

3.代码解释

点击查看代码
//读取图像
Mat mSrc = imread(path1, ImreadModes::IMREAD_COLOR);
imshow("源图像", mSrc);
上述代码读取了学生上传的图片,显示结果如下图(图3-1)所示。

点击查看代码
//灰度
Mat mGray;
cvtColor(mSrc, mGray, ColorConversionCodes::COLOR_BGR2GRAY);
imshow("灰度", mGray);
上述代码将原图转换为灰度图片,显示结果如下图(图3-2)所示。

点击查看代码
//二值
Mat mBin;
threshold(mGray, mBin, 114, 255, ThresholdTypes::THRESH_BINARY_INV);
imshow("二值", mBin);
上述代码讲灰度图像经过阈值化处理转换为二值图像,图像阈值为114,像素点灰度高于114的像素点值为0,像素点小于等于114的像素点值变为255,ThresholdTypes为二值反转类型,其实现原理如下图(图3-3)所示。


程序显示结果如下图(图3-4)所示。

点击查看代码
//形态学操作 开+膨胀
Mat mMorph;
Mat mOpenKernal= getStructuringElement(MorphShapes::MORPH_RECT, Size(10, 1));
morphologyEx(mBin, mMorph, MorphTypes::MORPH_OPEN, mOpenKernal, Point(-1, -1), 2);
Mat mDilateKernal = getStructuringElement(MorphShapes::MORPH_RECT, Size(3, 3));
dilate(mMorph, mMorph, mDilateKernal);
imshow("形态学", mMorph);
上述代码设置卷积核大小为10*1(宽度为10,高度为1),对二值图像执行开操作运算,执行后的效果是去除了英文等字符,保留图像中的直线特征,再将图像执行膨胀操作,其目的是增强二值图像中直线的纹理特征,使得图像中直线“变粗”更容易提取。最终显示结果如下图(图3-5)所示。

从图3-5可以很清晰地看到原图像中英语答题线被提取出来了,其余次要特征被过滤掉了。这里得出一个重要的实用结论:进行形态学开操作时,卷积核为横向或纵向时分别能够保留二值图中的横向纹理或纵向纹理,感兴趣的读者可以私下设计实验进行探究。

点击查看代码
//霍夫直线
vector<Vec4f> vLines;
HoughLinesP(mMorph, vLines, 1, CV_PI / 180.0, 1);
for (Vec4f& v : vLines)
{
	line(mSrc, Point(v[0], v[1]), Point(v[2], v[3]), Scalar(0, 0, 255), 2);
}
imshow("结果", mSrc);
上述代码使用霍夫直线算法提取图3-5中的直线并将提取到的直线绘制到原图像上。vLines中的每个元素类型是Vec4f,每一条线段可以被两个点表示,分别为(x1,y1)、(x2,y2),代码中v[0]表示x1、v[1]表示y1、v[2]表示x2、v[3]表示y2。代码显示结果如下图(图3-6)所示。

至此结束。

4.完整代码

点击查看代码
	//读取图像
	Mat mSrc = imread(path1, ImreadModes::IMREAD_COLOR);
	imshow("源图像", mSrc);

	//灰度
	Mat mGray;
	cvtColor(mSrc, mGray, ColorConversionCodes::COLOR_BGR2GRAY);
	imshow("灰度", mGray);

	//二值
	Mat mBin;
	threshold(mGray, mBin, 114, 255, ThresholdTypes::THRESH_BINARY_INV);
	imshow("二值", mBin);

	//形态学操作 开+膨胀
	Mat mMorph;
	Mat mOpenKernal= getStructuringElement(MorphShapes::MORPH_RECT, Size(10, 1));
	morphologyEx(mBin, mMorph, MorphTypes::MORPH_OPEN, mOpenKernal, Point(-1, -1), 2);
	Mat mDilateKernal = getStructuringElement(MorphShapes::MORPH_RECT, Size(3, 3));
	dilate(mMorph, mMorph, mDilateKernal);
	imshow("形态学", mMorph);

	//霍夫直线
	vector<Vec4f> vLines;
	HoughLinesP(mMorph, vLines, 1, CV_PI / 180.0, 1);
	for (Vec4f& v : vLines)
	{
		line(mSrc, Point(v[0], v[1]), Point(v[2], v[3]), Scalar(0, 0, 255), 2);
	}
	imshow("结果", mSrc);
posted @ 2024-06-11 15:56  hello_nullptr  阅读(95)  评论(0编辑  收藏  举报