前言:     

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

一、图像二值化+霍夫直线检测

#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);
}

运行程序,来看看结果如何:

opencv 画区域 opencv划线_直线检测

改变阈值,看看不同阈值下的结果:

opencv 画区域 opencv划线_直线检测_02

opencv 画区域 opencv划线_直线检测_03

显然,这种思路根本行不通!我们看看第二种思路。


二、图像二值化+形态学运算+霍夫直线检测

      我们为什么要在中间加一个形态学操作呢?其实呀,我们是想保留图像中的下划线,那我们就可以做一个水平的矩形结构元来对图像做一个开运算,同样的道理,如果我们想保留竖直线,那就可以用一个垂直的矩形结构元来对图像做一个开运算。当图像二值图中只剩下白色下划线的时候,再来进行霍夫直线检测那就不会受到其他的干扰了。让我们编写程序来看看效果:

#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;
}

运行程序,如下所示:

opencv 画区域 opencv划线_直线检测_04

结果很完美!