OpenCV实战案例——直线检测[C++]
0.前言
本文以实战案例为背景,一步步讲述如何使用计算机图像处理相关知识提取图片中英语填空题答题线。
1.需求背景
某公司打算设计一款英语题目批改APP,要求学生上传英语填空题图片,然后该APP自动标注答题线位置(使用红线标注),方便后续定位和批改答案。下图(图1-1)为某一学生上传的英语填空题图片。
该APP的预期效果如下图(图1-2)所示。
2.解决思路
- 首先应该将图片转换为灰度图再经过阈值处理转换为二值图。
- 由于图片中存在较多英文字符,如果直接使用霍夫直线检测效果并不显著。
- 寻找一种方法能够将英文等字符去除,仅留下直线特征。本文采取形态学操作实现该步骤。
- 将大多干扰因素去除后使用霍夫变换提取直线效果更佳。
- 最后在原图像中标注即可。
3.代码解释
点击查看代码
//读取图像
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);
程序显示结果如下图(图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);
从图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);
至此结束。
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);