前言:
大家都知道,现在在英语考试中已普遍实现了机器阅卷,所以从试卷图像中提取答题区域就显的很重要了。为了实现对答题区域的切图和识别,现在我们有一个这样的需求,那就是我们要寻找英语试卷填空题的下划线。这种问题有两种思路:一是对图像进行二值化后直接进行霍夫直线检测;二是对图像进行二值化后经过开运算再进行霍夫直线检测。接下我们编程来演示。
一、图像二值化+霍夫直线检测
#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;
}
运行程序,如下所示:
结果很完美!