一.问题描述
寻找图像中的一些直线,比如英语试卷填空题的下划线,这个对后期的切图与自动识别都比较重要。
二.解决方法
①对于直线检测,我们首先想到的是霍夫直线检测
这里来看下直接使用霍夫直线检测的效果
#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>
using namespace cv;
using namespace std;
int max_value = 255;
int threshold_value = 100;
Mat srcImage, dstImage, roiImage;
void FindLines(int, void*);
#define WINDOW_NAME1 "【霍夫检测直线图】"
int main(int argc, char* argv)
{
srcImage = imread("E:\\pictures\\43.jpg", 0);
if (srcImage.empty())
{
cout << "图像读取错误,检测路径!" << endl;
return -1;
}
namedWindow("原图", WINDOW_AUTOSIZE);
imshow("原图", srcImage);
namedWindow(WINDOW_NAME1, WINDOW_AUTOSIZE);
//定义ROI区域
Rect roi = Rect(30, 10, srcImage.cols - 50, srcImage.rows - 10);
roiImage = srcImage(roi);
imshow("ROI", roiImage);
//创建轨道条
createTrackbar("阈值:", WINDOW_NAME1, &threshold_value, max_value, FindLines);
FindLines(threshold_value, 0);
waitKey(0);
return 0;
}
void FindLines(int, void*)
{
Canny(roiImage, dstImage, threshold_value, threshold_value * 2, 3, false);
vector<Vec4i> lines;
HoughLinesP(dstImage, lines, 1, CV_PI / 180.0, 30, 30.0, 0);
//参数中30表示寻找到30个连续的像素即判定为直线
cvtColor(dstImage, dstImage, COLOR_GRAY2BGR);
for (size_t t = 0; t < lines.size(); t++)
{
Vec4i In = lines[t];
line(dstImage, Point(In[0], In[1]), Point(In[2], In[3]), Scalar(0, 0, 255), 2, 8, 0);
}
imshow(WINDOW_NAME1, dstImage);
}
运行结果:
结果发现,无论我们怎么调整二值化的阈值,直线检测的要么是漏检、不全等等,达不到我们预期的效果。
②思路:鉴于直接用霍夫变换的效果,效果不佳;需要通过图像形态学操作来寻找直线,霍夫获取位置信息及显示
程序的思路:
先根据实际图像的情况,进行合理的切割,选取恰当的ROI区域;
因为该图像黑白对比明显,进行二值化的操作;
通过图像形态学操作来寻找直线;
再通过膨胀的操作,使得寻找到的直线更加明显;
最后霍夫获取位置信息及显示。
#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>
using namespace cv;
using namespace std;
#define WINDOW_NAME1 "【1】原图"
#define WINDOW_NAME2 "【2】ROI区域"
#define WINDOW_NAME3 "【3】二值化后"
#define WINDOW_NAME4 "【4】形态学开运算寻找直线后"
#define WINDOW_NAME5 "【5】膨胀后"
#define WINDOW_NAME6 "【6】效果图"
Mat srcImage, dstImage, roiImage;
void morhpologyLines(int, void*);
int main(int argc, char* argv)
{
srcImage = imread("E:\\pictures\\43.jpg", 0);
if (srcImage.empty())
{
cout << "图像读取错误,检测路径!" << endl;
return -1;
}
namedWindow(WINDOW_NAME1, WINDOW_AUTOSIZE);
namedWindow(WINDOW_NAME2, WINDOW_AUTOSIZE);
namedWindow(WINDOW_NAME3, WINDOW_AUTOSIZE);
namedWindow(WINDOW_NAME4, WINDOW_AUTOSIZE);
namedWindow(WINDOW_NAME5, WINDOW_AUTOSIZE);
namedWindow(WINDOW_NAME6, WINDOW_AUTOSIZE);
imshow(WINDOW_NAME1, srcImage);
//定义ROI区域:选取的区域以实际情况为准
Rect roi = Rect(30, 10, srcImage.cols - 50, srcImage.rows - 10);
roiImage = srcImage(roi);
imshow(WINDOW_NAME2, roiImage);
morhpologyLines(0, 0);
waitKey(0);
return 0;
}
void morhpologyLines(int, void*)
{
//二值化
Mat binaryImage; //二值化图像
threshold(roiImage, binaryImage, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);
//参数中:THRESH_BINARY_INV和THRESH_BINARY将背景设成白色或黑色,以实际情况为准选择需要的
//参考博客:https://blog.csdn.net/u012566751/article/details/77046445 查看不同阈值类型的效果
imshow(WINDOW_NAME3, binaryImage);
//形态学寻找直线
//设置形态学运算的内核为 30*1像素
Mat kernel1 = getStructuringElement(MORPH_RECT, Size(30, 1), Point(-1, -1));
//开运算
Mat morphologyImage; //形态学操作图像
morphologyEx(binaryImage, morphologyImage, MORPH_OPEN, kernel1);
//morphologyEx(binaryImage, morhpImage, MORPH_OPEN, kernel, Point(-1, 1));
//一开始按照视频教程里面这样写的,就会报错:未加载wkernelbase.pdb,这里不理解是什么原因
imshow(WINDOW_NAME4, morphologyImage);
//进行膨胀操作,使得直线更加明显
Mat kernel2 = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
dilate(morphologyImage, morphologyImage, kernel2);
imshow(WINDOW_NAME5, morphologyImage);
//霍夫直线检测:此时根据形态学处理后寻找到的直线,再进行霍夫检测更准确
vector<Vec4i> lines;
HoughLinesP(morphologyImage, lines, 1, CV_PI / 180.0, 30, 30.0, 0);//参数中30表示寻找到30个连续的像素即判定为直线
dstImage = roiImage.clone(); //定义dstImage与ROI区域一样的类型
cvtColor(dstImage, dstImage, COLOR_GRAY2BGR);//将结果转为BGR格式
for (size_t t = 0; t < lines.size(); t++)
{
Vec4i In = lines[t];
line(dstImage, Point(In[0], In[1]), Point(In[2], In[3]), Scalar(0, 0, 255), 2, 8, 0);
}
imshow(WINDOW_NAME6, dstImage);
}
运行结果: