原理
我们是怎么做到检测到区域的呢?
首先,我们会注意到,文字区域和其他的图片背景很不一样。我们用膨胀处理图片,让文字变成一块块大区域,然后识别整块的轮廓,用矩形去框住这个轮廓。
这个程序分三个子函数,detect(检测),preprocess(图片预处理),findTextRegion(查找和筛选文字区域)。即main函数调用detect函数去实际完成文字区域检测。detect函数又分成preprocess和findTextRegion两个步骤来做。
#include<opencv2/opencv.hpp>
#include<iostream>
#include<opencv2/highgui/highgui_c.h>
#include<vector>
using namespace cv;
using namespace std;
Mat preprocess(Mat gray)
{
//1.Sobel算子,x方向求梯度
Mat sobel;
Sobel(gray, sobel, CV_8U, 1, 0, 3);
//2.二值化
Mat binary;
threshold(sobel, binary, 0, 255, THRESH_OTSU + THRESH_BINARY);
//3.膨胀和腐蚀操作核设定
Mat element1 = getStructuringElement(MORPH_RECT, Size(30, 9));
//控制高度设置可以控制上下行的膨胀程度,例如3比4的区分能力更强,但也会造成漏检
Mat element2 = getStructuringElement(MORPH_RECT, Size(24, 4));
//4.膨胀一次,让轮廓突出
Mat dilate1;
dilate(binary, dilate1, element2);
//5.腐蚀一次,去掉细节,表格线等。这里去掉的是竖直的线
Mat erode1;
erode(dilate1, erode1, element1);
//6.再次膨胀,让轮廓明显一些
Mat dilate2;
dilate(erode1, dilate2, element2);
//7.显示中间图片
//imshow("binary.jpg", binary);
//imshow("dilate1.jpg", dilate1);
//imshow("erode1.jpg", erode1);
//imshow("dilate2.jpg", dilate2);
return dilate2;
}
vector<RotatedRect> findTextRegion(Mat img)
{
vector<RotatedRect> rects;
//1.查找轮廓
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(img, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE, Point(0, 0));
//2.筛选那些面积小的
for (int i = 0; i < contours.size(); i++)
{
//计算当前轮廓的面积
double area = contourArea(contours[i]);
//面积小于1000的全部筛选掉
if (area < 1000)
continue;
//轮廓近似,作用较小,approxPolyDP函数有待研究
double epsilon = 0.001 * arcLength(contours[i], true);
Mat approx;
approxPolyDP(contours[i], approx, epsilon, true);
//找到最小矩形,该矩形可能有方向
RotatedRect rect = minAreaRect(contours[i]);
//计算高和宽
int m_width = rect.boundingRect().width;
int m_height = rect.boundingRect().height;
//筛选那些太细的矩形,留下扁的
if (m_height > m_width * 1.2)
continue;
//符合条件的rect添加到rects集合中
rects.push_back(rect);
}
return rects;
}
void detect(Mat img)
{
//1.转化成灰度图
Mat gray;
cvtColor(img, gray, CV_BGR2GRAY);
//2.形态学变换的预处理,得到可以查找矩形的轮廓
Mat dilation = preprocess(gray);
//3.查找和筛选文字区域
vector<RotatedRect> rects = findTextRegion(dilation);
//4.用绿线画出这些找到的轮廓
for(int i = 0; i<rects.size(); i++)
{
Point2f P[4];
rects[i].points(P);
for (int j = 0; j <= 3; j++)
{
line(img, P[j], P[(j + 1) % 4], Scalar(0, 255, 0), 2);
}
}
//5.显示带轮廓的图像
imshow("img", img);
}
int main(int argc, char* argv[])
{
Mat textImageSrc = imread("D://11.JPG");
detect(textImageSrc);
waitKey(0);
return 0;
}
效果如下: