前言:
大家都知道,现在在英语考试中已普遍实现了机器阅卷,所以从试卷图像中提取答题区域就显的很重要了。为了实现对答题区域的切图和识别,现在我们有一个这样的需求,那就是我们要寻找英语试卷填空题的下划线。这种问题有两种思路:一是对图像进行二值化后直接进行霍夫直线检测;二是对图像进行二值化后经过开运算再进行霍夫直线检测。接下我们编程来演示。
一、图像二值化+霍夫直线检测
#include <opencv2/opencv.hpp> #include <iostream> #include <math.h> using namespace cv; using namespace std; int threshold_value = 100; int max_count = 255; const string output_winName = "Hough Lines"; Mat src_img, roi_img, dst_img; void detectLines(int, void*); //声明函数 void morhpologyLines(int, void*); //声明函数 int main( ) { src_img = imread("English1.jpg", IMREAD_GRAYSCALE); //灰度读取一张图片 if (src_img.empty()) { printf("could not load the image...\n"); return -1; } namedWindow("原图", CV_WINDOW_AUTOSIZE); imshow("原图", src_img); namedWindow(output_winName, CV_WINDOW_AUTOSIZE); Rect roi = Rect(5, 5, src_img.cols - 10, src_img.rows - 10); roi_img = src_img(roi); // 去除边缘空白部分 imshow("兴趣图像", roi_img); createTrackbar("threshold:", output_winName, &threshold_value, max_count, detectLines); detectLines(0, 0); //morhpologyLines(0, 0); waitKey(0); return 0; } // 思路一:canny边缘检测+霍夫检测的方法 void detectLines(int, void*) { Canny(roi_img, dst_img, threshold_value, threshold_value * 2, 3, false); //Canny边缘检测 vector<Vec4i> lines; HoughLinesP(dst_img, lines, 1, CV_PI / 180.0, 30, 30.0, 0); //调用概率霍夫直线检测函数 cvtColor(dst_img, dst_img, COLOR_GRAY2BGR); //灰度度转换成彩色图 for (size_t t = 0; t < lines.size(); ++t) // 遍历检测到的每一条直线 { Vec4i ln = lines[t]; line(dst_img, Point(ln[0], ln[1]), Point(ln[2], ln[3]), Scalar(0, 0, 255), 2, 8, 0); // 绘制出检测到的每条直线 } imshow(output_winName, dst_img); }
运行程序,来看看结果如何:
改变阈值,看看不同阈值下的结果:
显然,这种思路根本行不通!我们看看第二种思路。
二、图像二值化+形态学运算+霍夫直线检测
我们为什么要在中间加一个形态学操作呢?其实呀,我们是想保留图像中的下划线,那我们就可以做一个水平的矩形结构元来对图像做一个开运算,同样的道理,如果我们想保留竖直线,那就可以用一个垂直的矩形结构元来对图像做一个开运算。当图像二值图中只剩下白色下划线的时候,再来进行霍夫直线检测那就不会受到其他的干扰了。让我们编写程序来看看效果:
#include <opencv2/opencv.hpp> #include <iostream> #include <math.h> using namespace cv; using namespace std; int threshold_value = 100; int max_count = 255; const string output_winName = "Hough Lines"; Mat src_img, roi_img, dst_img; void detectLines(int, void*); //声明函数 void morhpologyLines(int, void*); //声明函数 int main( ) { src_img = imread("English1.jpg", IMREAD_GRAYSCALE); //灰度读取一张图片 if (src_img.empty()) { printf("could not load the image...\n"); return -1; } namedWindow("原图", CV_WINDOW_AUTOSIZE); imshow("原图", src_img); namedWindow(output_winName, CV_WINDOW_AUTOSIZE); Rect roi = Rect(5, 5, src_img.cols - 10, src_img.rows - 10); roi_img = src_img(roi); // 去除边缘空白部分 imshow("兴趣图像", roi_img); morhpologyLines(0, 0); waitKey(0); return 0; } // 思路二:二值图化+形态学操作+霍夫检测的方法 void morhpologyLines(int, void*) { //阈值化图像 Mat binary_img, morhp_img; threshold(roi_img, binary_img, 0, 255, THRESH_BINARY_INV | THRESH_OTSU); imshow("二值化结果", binary_img); //进行开运算操作 Mat kernel = getStructuringElement(MORPH_RECT, Size(30, 1), Point(-1, -1)); //定义一个宽20、高1的一个矩形结构元 morphologyEx(binary_img, morhp_img, MORPH_OPEN, kernel, Point(-1, -1)); //作开运算,只保留图像中的直线 imshow("开运算操作结果", morhp_img); // 进行膨胀操作 kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1)); //定义一个宽3、高3的一个矩形结构元 dilate(morhp_img, morhp_img, kernel); //作膨胀操作,图像中的直线增强 imshow("膨胀操作结果", morhp_img); // 霍夫直线检测 vector<Vec4i> lines; HoughLinesP(morhp_img, lines, 1, CV_PI / 180.0, 25, 20.0, 0); Mat result_img = roi_img.clone(); cvtColor(result_img, result_img, COLOR_GRAY2BGR); //灰度图转为彩色图 for (size_t t = 0; t < lines.size(); ++t) { Vec4i ln = lines[t]; line(result_img, Point(ln[0], ln[1]), Point(ln[2], ln[3]), Scalar(0, 0, 255), 2, 8, 0); } imshow(output_winName, result_img); return; }
运行程序,如下所示:
结果很完美!