C++ opencv and Text Region extraction

今天,项目中出现了文字定位的bug,如下图片:

  • Today, there is a bug with text location in the project, as shown in the picture below:

我需要将文字区域1和文字区域2进行分割,之前用的方法是,Rect rect_Title(0, 0,(int)gray.cols/2, rect_y - 5);这种方法必须满足文字区域1的宽度小于一半的输入图片宽度。今天遇到了文字区域2的内容在【0,(int)gray.cols/2】之中。就出现了分割错误。我参考http://www.voidcn.com/article/p-rkwiytbi-dk.html

原文是基于Python编写的,我需要进行移植。

  • Rect rect_Title(0, 0,(int) gray-cols /2, rect_y-5); This method must satisfy the text area 1 width less than half the width of the input image.Today I encountered the text area 2 in [0, (int) gray-cols /2].You get a segmentation fault.I refer to http://www.voidcn.com/article/p-rkwiytbi-dk.html。

  • The text is written in Python and I need to rewrite it.

参考上面的序列图,这个图是原文中有的,我只是拿过来更好的说明介绍。其中,由于Python的代码是根据opencv写的,又因为opencv是支持C++和Python的,所以函数基本上都是差不多的,这就为我的移植提供了很好的便利。 对于函数preprocess和findTextRegion的函数功能是不需要改变的。我直接根据Python代码转C++即可。但是我得需要自己的需求进行修改detect函数。原文中的函数确实能很好的实现文字区域的定位,但是对于本次我使用的过程中分割定位的时候出现了一点小问题。不过这不影响他的整体功能性。在此非常感谢原文作者为社区做出的贡献,也方便了我更快的实现预先的功能。

  • Refer to the sequence diagram above. This diagram is from the original. I just took it for a better illustration.Because Python code is written according to opencv, and because opencv supports C++ and Python, the functions are basically the same, which provides a good convenience for my migration.There is no need to change the functionality of the preprocess and findTextRegion functions.I just go straight to C++ from Python code.
  • But I have to modify the Detect function based on my own requirements.The function in the original text can really well achieve the positioning of the text area, but there is a little problem when I use the process of segmentation and positioning.But that doesn't affect its overall functionality.I would like to thank the original author for his contribution to the community, and also facilitate my faster implementation of the pre-functionality.

那么,一个文件一个文件的写。首先,是预处理部分。先看看源代码:

  • So, file by file.First, the preprocessing part.First look at the source code:
def preprocess(gray):
    # 1. Sobel算子,x方向求梯度
    sobel = cv2.Sobel(gray, cv2.CV_8U, 1, 0, ksize = 3)
    # 2. 二值化
    ret, binary = cv2.threshold(sobel, 0, 255, cv2.THRESH_OTSU+cv2.THRESH_BINARY)

    # 3. 膨胀和腐蚀操作的核函数
    element1 = cv2.getStructuringElement(cv2.MORPH_RECT, (30, 9))
    element2 = cv2.getStructuringElement(cv2.MORPH_RECT, (24, 6))

    # 4. 膨胀一次,让轮廓突出
    dilation = cv2.dilate(binary, element2, iterations = 1)

    # 5. 腐蚀一次,去掉细节,如表格线等。注意这里去掉的是竖直的线
    erosion = cv2.erode(dilation, element1, iterations = 1)

    # 6. 再次膨胀,让轮廓明显一些
    dilation2 = cv2.dilate(erosion, element2, iterations = 3)

    # 7. 存储中间图片 
    cv2.imwrite("binary.png", binary)
    cv2.imwrite("dilation.png", dilation)
    cv2.imwrite("erosion.png", erosion)
    cv2.imwrite("dilation2.png", dilation2)

    return dilation2

我写的C++代码:

  • My C++ code:
Mat Imagepreprocess(Mat gray)
{
	//1.Sobel算子,x方向求梯度
	Mat sobel;
	Sobel(gray, sobel, CV_8U, 1, 0, 3);

	//2.OTSU 二值化
	Mat binary;
	threshold(sobel, binary, 0, 255, THRESH_OTSU + THRESH_BINARY);

	//3.膨胀和腐蚀操作核设定
	Mat element_1 = getStructuringElement(MORPH_RECT, Size(30, 9));
	//控制高度设置可以控制上下行的膨胀程度,例如3比4的区分能力更强,但也会造成漏检
	Mat element_2 = getStructuringElement(MORPH_RECT, Size(24, 4));

	//4.膨胀一次,让轮廓突出
	Mat dilate_1;
	dilate(binary, dilate_1, element_2);

	//5.腐蚀一次,去掉细节,表格线等。这里去掉的是竖直的线
	Mat erode_1;
	erode(dilate_1, erode_1, element_1);

	//6.再次膨胀,让轮廓明显一些
	Mat dilate_2;
	dilate(erode_1, dilate_2, element_2);

	//7.查看输出结果
	imwrite("./title_time/output/binary.jpg", binary);
	imwrite("./title_time/output/dilate1.jpg", dilate_1);
	imwrite("./title_time/output/erode1.jpg", erode_1);
	imwrite("./title_time/output/dilate2.jpg", dilate_2);

	return dilate_2;
}

文字定位源代码:

  • TextRegion code:
def findTextRegion(img):
    region = []

    # 1. 查找轮廓
    contours, hierarchy = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

    # 2. 筛选那些面积小的
    for i in range(len(contours)):
        cnt = contours[i]
        # 计算该轮廓的面积
        area = cv2.contourArea(cnt) 

        # 面积小的都筛选掉
        if(area < 1000):
            continue

        # 轮廓近似,作用很小
        epsilon = 0.001 * cv2.arcLength(cnt, True)
        approx = cv2.approxPolyDP(cnt, epsilon, True)

        # 找到最小的矩形,该矩形可能有方向
        rect = cv2.minAreaRect(cnt)
        print "rect is: "
        print rect

        # box是四个点的座标
        box = cv2.cv.BoxPoints(rect)
        box = np.int0(box)

        # 计算高和宽
        height = abs(box[0][1] - box[2][1])
        width = abs(box[0][0] - box[2][0])

        # 筛选那些太细的矩形,留下扁的
        if(height > width * 1.2):
            continue

        region.append(box)

    return region

C++写的代码:

  • My C++ code:
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 < 100)
			continue;

		//轮廓近似,作用较小
		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;
}

检测代码,源代码:

  • detect resource code:
def detect(img):
    # 1. 转化成灰度图
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # 2. 形态学变换的预处理,得到可以查找矩形的图片
    dilation = preprocess(gray)

    # 3. 查找和筛选文字区域
    region = findTextRegion(dilation)

    # 4. 用绿线画出这些找到的轮廓
    for box in region:
        cv2.drawContours(img, [box], 0, (0, 255, 0), 2)

    cv2.namedWindow("img", cv2.WINDOW_NORMAL)
    cv2.imshow("img", img)

    # 带轮廓的图片
    cv2.imwrite("contours.png", img)

    cv2.waitKey(0)
    cv2.destroyAllWindows()


if __name__ == '__main__':
    # 读取文件
    imagePath = sys.argv[1]
    img = cv2.imread(imagePath)
    detect(img)

C++对应的代码:

  • My C++ code:
void DetectTextRegion(Mat img)
{
	//1.转化成灰度图
	Mat gray;
	cvtColor(img, gray, CV_BGR2GRAY);

	//2.形态学变换的预处理,得到可以查找矩形的轮廓
	Mat dilation = Imagepreprocess(gray);
	imshow("dilation", dilation);

	//3.查找和筛选文字区域
	vector<RotatedRect> rects = findTextRegion(dilation);

	if (rects.size() == 2)
	{

	}
	//4.用绿线画出这些找到的轮廓
	for each (RotatedRect rect in rects)
	{
		Point2f P[4];
		rect.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);
	imwrite("./title_time/imgDrawRect.jpg", img);

	waitKey(0);
}

int _tmain_(int argc, _TCHAR* argv[])
{
	Mat SrcImage = imread(ImagePath);
	DetectTextRegion(SrcImage);

	return 0;
}

接下来就是简单的测试:

  • Next,it is a sample test.

分析结果,并没有按照我自己的需求进行分割,我需要进行修改相应的代码。我最终需要的是两个矩形框,对于大于2个的矩形框进行处理。我还需要进行裁剪分割框选的区域。

  • The analysis results were not segmented according to my own requirements, so I needed to modify the corresponding code.What I finally need is two rectangles, and I can handle rectangles larger than two.I also need to crop the area of the split box.

通过测试发现,出现检测错误的情况,都出现在第二个检测字段中,对于第一个检测字段都是可以很好的检测分割的。那么,我就可以这么干了。首先,我能够用过函数Textregion得到当前检测到的矩形框的数目。首先,判断当前检测到的矩形框的数目,如果数目是2,那就说明当前检测到的字段为2,我需要进行分割处理。首先定义两个 vector<int> Xboundary; vector<int> Yboundary;用来存储当前矩形框的四个定点座标。

  • Through testing, it is found that any detection error occurs in the second detection field, and the first detection field can be well detected and segmented.Then I can do it.First, I was able to get the number of currently detected rectangular boxes using the function Textregion.First, determine the number of rectangular boxes currently detected. If the number is 2, then the field currently detected is 2 and I need to split it.Firstly, two vectors <int> Xboundary are defined.The vector < int > Yboundary;The four fixed point coordinates used to store the current rectangular box.

auto Min_Yboundary = std::min_element(std::begin(Yboundary), std::end(Yboundary));
auto Max_Yboundary = std::max_element(std::begin(Yboundary), std::end(Yboundary));

通过上面的代码段可以实现顶点座标的最大值和最小值。

  • Maximum and minimum vertex coordinates can be achieved through the code snippet above.

                    int boundary_height = rect.boundingRect().height;
                    int boundary_width = rect.boundingRect().width;
                    //cout << boundary_height << "     " << Max_Yboundary[0] - Min_Yboundary[0] << endl;
                    //cout << boundary_width << "     " << Max_Xboundary[0] - Min_Xboundary[0] << endl;

通过上面的代码段,可以获取到当前矩形框的宽度和高度。你可以直接通过内部函数rect.boundingRect().height的方法来获取高度和宽度。我是直接通过座标差值进行获取的。得到顶点座标,和宽度高度就可以使用函数rect进行指定区域的裁剪。但是你需要进行相应的异常判断。也就是rect函数中的参数应该满足要求,不能小于0,不能大于图像的宽度和高度。

  • From the above code block, you can get the width and height of the current rectangular box.You can get the height and width directly by using the internal function rect.boundingRect().height.I got it directly from the coordinate difference.Get the vertex coordinates, width, and height to crop the specified area using the function rect.But you need to make the exception judgment accordingly.That is, the parameters in the rect function should meet the requirements, not less than 0, not greater than the width and height of the image.

我的代码是这样的:

  • My code looks like this:

void DetectTextRegion(Mat img)
{
	//1.转化成灰度图
	Mat gray;
	cvtColor(img, gray, CV_BGR2GRAY);

	//2.形态学变换的预处理,得到可以查找矩形的轮廓
	Mat dilation = Imagepreprocess(gray);
	imshow("dilation", dilation);

	//3.查找和筛选文字区域
	vector<RotatedRect> rects = findTextRegion(dilation);

	if (rects.size() >= 2)
	{
		int count = 0;
		vector<int> Xboundary;
		vector<int> Yboundary;
		//4.用绿线画出这些找到的轮廓
		for each (RotatedRect rect in rects)
		{
			Point2f P[4];
			rect.points(P);
			count = count + 1;
			if (rects.size() == 2)//表示当前检测到了两个rect
			{
				//=====================================================================
				if (count == 1)
				{
					vector<int> Xboundary;
					vector<int> Yboundary;
					//cout << P[0] << P[1] << P[2] << P[3] <<endl;
					Xboundary.push_back(int(P[0].x));
					Xboundary.push_back(int(P[1].x));
					Xboundary.push_back(int(P[2].x));
					Xboundary.push_back(int(P[3].x));

					Yboundary.push_back(int(P[0].y));
					Yboundary.push_back(int(P[1].y));
					Yboundary.push_back(int(P[2].y));
					Yboundary.push_back(int(P[3].y));

					auto Min_Xboundary = std::min_element(std::begin(Xboundary), std::end(Xboundary));
					auto Max_Xboundary = std::max_element(std::begin(Xboundary), std::end(Xboundary));
					auto Min_Yboundary = std::min_element(std::begin(Yboundary), std::end(Yboundary));
					auto Max_Yboundary = std::max_element(std::begin(Yboundary), std::end(Yboundary));

					int boundary_height = rect.boundingRect().height;
					int boundary_width = rect.boundingRect().width;
					//cout << boundary_height << "     " << Max_Yboundary[0] - Min_Yboundary[0] << endl;
					//cout << boundary_width << "     " << Max_Xboundary[0] - Min_Xboundary[0] << endl;
					//=====================================================================
					//---------------------------------------------------------------------
					//加异常判断
					//---------------------------------------------------------------------
					Mat roi;
					if (Min_Xboundary[0]-5 >= 0 && Min_Yboundary[0]-5>=0 && Max_Xboundary[0] - Min_Xboundary[0]+10 >= 0&& Max_Yboundary[0] - Min_Yboundary[0]+10 >= 0
						&& Max_Xboundary[0] + 5 <= img.cols
						&& Max_Yboundary[0] + 5 <= img.rows)
					{
						Rect m_select(
							(Min_Xboundary[0] - 5),
							(Min_Yboundary[0] - 5),
							(Max_Xboundary[0] - Min_Xboundary[0] + 5 + 5),
							(Max_Yboundary[0] - Min_Yboundary[0] + 5 + 5));
						roi = img(m_select);
					}
					else if (Min_Xboundary[0] >= 0 && Min_Yboundary[0] >= 0 && Max_Xboundary[0] - Min_Xboundary[0] >= 0 && Max_Yboundary[0] - Min_Yboundary[0] >= 0
						    && Max_Xboundary[0] <= img.cols
						    && Max_Yboundary[0] <= img.rows)
					{
						Rect m_select(
							(Min_Xboundary[0]),
							(Min_Yboundary[0]),
							(Max_Xboundary[0] - Min_Xboundary[0]),
							(Max_Yboundary[0] - Min_Yboundary[0]));
						roi = img(m_select);
					}
					string Img_Name = ".//title_time/output1" + to_string(1) + ".jpg";
					imwrite(Img_Name, roi);
					imshow("out1", roi);
					vector<int>().swap(Xboundary);
					vector<int>().swap(Yboundary);
				}
				if (count == 2)
				{
					vector<int> Xboundary;
					vector<int> Yboundary;
					//cout << P[0] << P[1] << P[2] << P[3] <<endl;
					Xboundary.push_back(int(P[0].x));
					Xboundary.push_back(int(P[1].x));
					Xboundary.push_back(int(P[2].x));
					Xboundary.push_back(int(P[3].x));

					Yboundary.push_back(int(P[0].y));
					Yboundary.push_back(int(P[1].y));
					Yboundary.push_back(int(P[2].y));
					Yboundary.push_back(int(P[3].y));

					auto Min_Xboundary = std::min_element(std::begin(Xboundary), std::end(Xboundary));
					auto Max_Xboundary = std::max_element(std::begin(Xboundary), std::end(Xboundary));
					auto Min_Yboundary = std::min_element(std::begin(Yboundary), std::end(Yboundary));
					auto Max_Yboundary = std::max_element(std::begin(Yboundary), std::end(Yboundary));

					int boundary_height = rect.boundingRect().height;
					int boundary_width = rect.boundingRect().width;
					//cout << boundary_height << "     " << Max_Yboundary[0] - Min_Yboundary[0] << endl;
					//cout << boundary_width << "     " << Max_Xboundary[0] - Min_Xboundary[0] << endl;
					//=====================================================================
					//---------------------------------------------------------------------
					//加异常判断
					//---------------------------------------------------------------------
					Mat roi;
					if (Min_Xboundary[0] - 5 >= 0 && Min_Yboundary[0] - 5 >= 0 && Max_Xboundary[0] - Min_Xboundary[0] + 10 >= 0 && Max_Yboundary[0] - Min_Yboundary[0] + 10 >= 0
						&& Max_Xboundary[0] + 5 <= img.cols
						&& Max_Yboundary[0] + 5 <= img.rows)
					{
						Rect m_select(
							(Min_Xboundary[0] - 5),
							(Min_Yboundary[0] - 5),
							(Max_Xboundary[0] - Min_Xboundary[0] + 5 + 5),
							(Max_Yboundary[0] - Min_Yboundary[0] + 5 + 5));
						roi = img(m_select);
					}
					else if (Min_Xboundary[0] >= 0 && Min_Yboundary[0] >= 0 && Max_Xboundary[0] - Min_Xboundary[0] >= 0 && Max_Yboundary[0] - Min_Yboundary[0] >= 0
						&& Max_Xboundary[0] <= img.cols
						&& Max_Yboundary[0] <= img.rows)
					{
						Rect m_select(
							(Min_Xboundary[0]),
							(Min_Yboundary[0]),
							(Max_Xboundary[0] - Min_Xboundary[0]),
							(Max_Yboundary[0] - Min_Yboundary[0]));
						roi = img(m_select);
					}
					string Img_Name = ".//title_time/output2" + to_string(1) + ".jpg";
					imwrite(Img_Name, roi);
					imshow("out2", roi);
					vector<int>().swap(Xboundary);
					vector<int>().swap(Yboundary);
				}
			}
			else//表示当前检测到了大于rect
			{
				if (count == 1)
				{
					vector<int> Xboundary;
					vector<int> Yboundary;
					//cout << P[0] << P[1] << P[2] << P[3] <<endl;
					Xboundary.push_back(int(P[0].x));
					Xboundary.push_back(int(P[1].x));
					Xboundary.push_back(int(P[2].x));
					Xboundary.push_back(int(P[3].x));

					Yboundary.push_back(int(P[0].y));
					Yboundary.push_back(int(P[1].y));
					Yboundary.push_back(int(P[2].y));
					Yboundary.push_back(int(P[3].y));

					auto Min_Xboundary = std::min_element(std::begin(Xboundary), std::end(Xboundary));
					auto Max_Xboundary = std::max_element(std::begin(Xboundary), std::end(Xboundary));
					auto Min_Yboundary = std::min_element(std::begin(Yboundary), std::end(Yboundary));
					auto Max_Yboundary = std::max_element(std::begin(Yboundary), std::end(Yboundary));

					int boundary_height = rect.boundingRect().height;
					int boundary_width = rect.boundingRect().width;
					//cout << boundary_height << "     " << Max_Yboundary[0] - Min_Yboundary[0] << endl;
					//cout << boundary_width << "     " << Max_Xboundary[0] - Min_Xboundary[0] << endl;
					//=====================================================================
					//---------------------------------------------------------------------
					//加异常判断
					//---------------------------------------------------------------------
					Mat roi;
					if (Min_Xboundary[0] - 5 >= 0 && Min_Yboundary[0] - 5 >= 0 && Max_Xboundary[0] - Min_Xboundary[0] + 10 >= 0 && Max_Yboundary[0] - Min_Yboundary[0] + 10 >= 0
						&& Max_Xboundary[0] + 5 <= img.cols
						&& Max_Yboundary[0] + 5 <= img.rows)
					{
						Rect m_select(
							(Min_Xboundary[0] - 5),
							(Min_Yboundary[0] - 5),
							(Max_Xboundary[0] - Min_Xboundary[0] + 5 + 5),
							(Max_Yboundary[0] - Min_Yboundary[0] + 5 + 5));
						roi = img(m_select);
					}
					else if (Min_Xboundary[0] >= 0 && Min_Yboundary[0] >= 0 && Max_Xboundary[0] - Min_Xboundary[0] >= 0 && Max_Yboundary[0] - Min_Yboundary[0] >= 0
						&& Max_Xboundary[0] <= img.cols
						&& Max_Yboundary[0] <= img.rows)
					{
						Rect m_select(
							(Min_Xboundary[0]),
							(Min_Yboundary[0]),
							(Max_Xboundary[0] - Min_Xboundary[0]),
							(Max_Yboundary[0] - Min_Yboundary[0]));
						roi = img(m_select);
					}
					string Img_Name = ".//title_time/output1" + to_string(1) + ".jpg";
					imwrite(Img_Name, roi);
					imshow("out1", roi);
					vector<int>().swap(Xboundary);
					vector<int>().swap(Yboundary);
				}
				else
				{
					//cout << P[0] << P[1] << P[2] << P[3] <<endl;
					Xboundary.push_back(int(P[0].x));
					Xboundary.push_back(int(P[1].x));
					Xboundary.push_back(int(P[2].x));
					Xboundary.push_back(int(P[3].x));

					Yboundary.push_back(int(P[0].y));
					Yboundary.push_back(int(P[1].y));
					Yboundary.push_back(int(P[2].y));
					Yboundary.push_back(int(P[3].y));

					auto Min_Xboundary = std::min_element(std::begin(Xboundary), std::end(Xboundary));
					auto Max_Xboundary = std::max_element(std::begin(Xboundary), std::end(Xboundary));
					auto Min_Yboundary = std::min_element(std::begin(Yboundary), std::end(Yboundary));
					auto Max_Yboundary = std::max_element(std::begin(Yboundary), std::end(Yboundary));

					int boundary_height = rect.boundingRect().height;
					int boundary_width = rect.boundingRect().width;
					//cout << boundary_height << "     " << Max_Yboundary[0] - Min_Yboundary[0] << endl;
					//cout << boundary_width << "     " << Max_Xboundary[0] - Min_Xboundary[0] << endl;
					//=====================================================================
					//---------------------------------------------------------------------
					//加异常判断
					//---------------------------------------------------------------------
					Mat roi;
					if (Min_Xboundary[0] - 5 >= 0 && Min_Yboundary[0] - 5 >= 0 && Max_Xboundary[0] - Min_Xboundary[0] + 10 >= 0 && Max_Yboundary[0] - Min_Yboundary[0] + 10 >= 0
						&& Max_Xboundary[0] + 5 <= img.cols
						&& Max_Yboundary[0] + 5 <= img.rows)
					{
						Rect m_select(
							(Min_Xboundary[0] - 5),
							(Min_Yboundary[0] - 5),
							(Max_Xboundary[0] - Min_Xboundary[0] + 5 + 5),
							(Max_Yboundary[0] - Min_Yboundary[0] + 5 + 5));
						roi = img(m_select);
					}
					else if (Min_Xboundary[0] >= 0 && Min_Yboundary[0] >= 0 && Max_Xboundary[0] - Min_Xboundary[0] >= 0 && Max_Yboundary[0] - Min_Yboundary[0] >= 0
						&& Max_Xboundary[0] <= img.cols
						&& Max_Yboundary[0] <= img.rows)
					{
						Rect m_select(
							(Min_Xboundary[0]),
							(Min_Yboundary[0]),
							(Max_Xboundary[0] - Min_Xboundary[0]),
							(Max_Yboundary[0] - Min_Yboundary[0]));
						roi = img(m_select);
					}
					string Img_Name = ".//title_time/output2" + to_string(1) + ".jpg";
					imwrite(Img_Name, roi);
					imshow("out2", roi);
				}
			}
			for (int j = 0; j <= 3; j++)
			{
				line(img, P[j], P[(j + 1) % 4], Scalar(0, 255, 0), 2);
			}
		}
		vector<int>().swap(Xboundary);
		vector<int>().swap(Yboundary);
	}
	else
	{

	}

	//5.显示带轮廓的图像
	imshow("img", img);
	imwrite("./title_time/imgDrawRect.jpg", img);

	waitKey(0);
}

再次进行测试:

  • retest:

暂定按照这个思路来写,我再进行进一步优化处理。

  • I need to improve the detecting,following this code Temporarily.

All of my complete c++ code:

#include "stdafx.h"
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;
const string ImagePath = "./title_time/1.jpg";//图像路径

Mat Imagepreprocess(Mat gray)
{
	//1.Sobel算子,x方向求梯度
	Mat sobel;
	Sobel(gray, sobel, CV_8U, 1, 0, 3);

	//2.OTSU 二值化
	Mat binary;
	threshold(sobel, binary, 0, 255, THRESH_OTSU + THRESH_BINARY);

	//3.膨胀和腐蚀操作核设定
	Mat element_1 = getStructuringElement(MORPH_RECT, Size(30, 9));
	//控制高度设置可以控制上下行的膨胀程度,例如3比4的区分能力更强,但也会造成漏检
	Mat element_2 = getStructuringElement(MORPH_RECT, Size(24, 4));

	//4.膨胀一次,让轮廓突出
	Mat dilate_1;
	dilate(binary, dilate_1, element_2);

	//5.腐蚀一次,去掉细节,表格线等。这里去掉的是竖直的线
	Mat erode_1;
	erode(dilate_1, erode_1, element_1);

	//6.再次膨胀,让轮廓明显一些
	Mat dilate_2;
	dilate(erode_1, dilate_2, element_2);

	//7.查看输出结果
	imwrite("./title_time/output/binary.jpg", binary);
	imwrite("./title_time/output/dilate1.jpg", dilate_1);
	imwrite("./title_time/output/erode1.jpg", erode_1);
	imwrite("./title_time/output/dilate2.jpg", dilate_2);

	return dilate_2;
}

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 < 100)
			continue;

		//轮廓近似,作用较小
		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 DetectTextRegion(Mat img)
{
	//1.转化成灰度图
	Mat gray;
	cvtColor(img, gray, CV_BGR2GRAY);

	//2.形态学变换的预处理,得到可以查找矩形的轮廓
	Mat dilation = Imagepreprocess(gray);
	imshow("dilation", dilation);

	//3.查找和筛选文字区域
	vector<RotatedRect> rects = findTextRegion(dilation);

	if (rects.size() >= 2)
	{
		int count = 0;
		vector<int> Xboundary;
		vector<int> Yboundary;
		//4.用绿线画出这些找到的轮廓
		for each (RotatedRect rect in rects)
		{
			Point2f P[4];
			rect.points(P);
			count = count + 1;
			if (rects.size() == 2)//表示当前检测到了两个rect
			{
				//=====================================================================
				if (count == 1)
				{
					vector<int> Xboundary;
					vector<int> Yboundary;
					//cout << P[0] << P[1] << P[2] << P[3] <<endl;
					Xboundary.push_back(int(P[0].x));
					Xboundary.push_back(int(P[1].x));
					Xboundary.push_back(int(P[2].x));
					Xboundary.push_back(int(P[3].x));

					Yboundary.push_back(int(P[0].y));
					Yboundary.push_back(int(P[1].y));
					Yboundary.push_back(int(P[2].y));
					Yboundary.push_back(int(P[3].y));

					auto Min_Xboundary = std::min_element(std::begin(Xboundary), std::end(Xboundary));
					auto Max_Xboundary = std::max_element(std::begin(Xboundary), std::end(Xboundary));
					auto Min_Yboundary = std::min_element(std::begin(Yboundary), std::end(Yboundary));
					auto Max_Yboundary = std::max_element(std::begin(Yboundary), std::end(Yboundary));

					int boundary_height = rect.boundingRect().height;
					int boundary_width = rect.boundingRect().width;
					//cout << boundary_height << "     " << Max_Yboundary[0] - Min_Yboundary[0] << endl;
					//cout << boundary_width << "     " << Max_Xboundary[0] - Min_Xboundary[0] << endl;
					//=====================================================================
					//---------------------------------------------------------------------
					//加异常判断
					//---------------------------------------------------------------------
					Mat roi;
					if (Min_Xboundary[0]-5 >= 0 && Min_Yboundary[0]-5>=0 && Max_Xboundary[0] - Min_Xboundary[0]+10 >= 0&& Max_Yboundary[0] - Min_Yboundary[0]+10 >= 0
						&& Max_Xboundary[0] + 5 <= img.cols
						&& Max_Yboundary[0] + 5 <= img.rows)
					{
						Rect m_select(
							(Min_Xboundary[0] - 5),
							(Min_Yboundary[0] - 5),
							(Max_Xboundary[0] - Min_Xboundary[0] + 5 + 5),
							(Max_Yboundary[0] - Min_Yboundary[0] + 5 + 5));
						roi = img(m_select);
					}
					else if (Min_Xboundary[0] >= 0 && Min_Yboundary[0] >= 0 && Max_Xboundary[0] - Min_Xboundary[0] >= 0 && Max_Yboundary[0] - Min_Yboundary[0] >= 0
						    && Max_Xboundary[0] <= img.cols
						    && Max_Yboundary[0] <= img.rows)
					{
						Rect m_select(
							(Min_Xboundary[0]),
							(Min_Yboundary[0]),
							(Max_Xboundary[0] - Min_Xboundary[0]),
							(Max_Yboundary[0] - Min_Yboundary[0]));
						roi = img(m_select);
					}
					string Img_Name = ".//title_time/output1" + to_string(1) + ".jpg";
					imwrite(Img_Name, roi);
					imshow("out1", roi);
					vector<int>().swap(Xboundary);
					vector<int>().swap(Yboundary);
				}
				if (count == 2)
				{
					vector<int> Xboundary;
					vector<int> Yboundary;
					//cout << P[0] << P[1] << P[2] << P[3] <<endl;
					Xboundary.push_back(int(P[0].x));
					Xboundary.push_back(int(P[1].x));
					Xboundary.push_back(int(P[2].x));
					Xboundary.push_back(int(P[3].x));

					Yboundary.push_back(int(P[0].y));
					Yboundary.push_back(int(P[1].y));
					Yboundary.push_back(int(P[2].y));
					Yboundary.push_back(int(P[3].y));

					auto Min_Xboundary = std::min_element(std::begin(Xboundary), std::end(Xboundary));
					auto Max_Xboundary = std::max_element(std::begin(Xboundary), std::end(Xboundary));
					auto Min_Yboundary = std::min_element(std::begin(Yboundary), std::end(Yboundary));
					auto Max_Yboundary = std::max_element(std::begin(Yboundary), std::end(Yboundary));

					int boundary_height = rect.boundingRect().height;
					int boundary_width = rect.boundingRect().width;
					//cout << boundary_height << "     " << Max_Yboundary[0] - Min_Yboundary[0] << endl;
					//cout << boundary_width << "     " << Max_Xboundary[0] - Min_Xboundary[0] << endl;
					//=====================================================================
					//---------------------------------------------------------------------
					//加异常判断
					//---------------------------------------------------------------------
					Mat roi;
					if (Min_Xboundary[0] - 5 >= 0 && Min_Yboundary[0] - 5 >= 0 && Max_Xboundary[0] - Min_Xboundary[0] + 10 >= 0 && Max_Yboundary[0] - Min_Yboundary[0] + 10 >= 0
						&& Max_Xboundary[0] + 5 <= img.cols
						&& Max_Yboundary[0] + 5 <= img.rows)
					{
						Rect m_select(
							(Min_Xboundary[0] - 5),
							(Min_Yboundary[0] - 5),
							(Max_Xboundary[0] - Min_Xboundary[0] + 5 + 5),
							(Max_Yboundary[0] - Min_Yboundary[0] + 5 + 5));
						roi = img(m_select);
					}
					else if (Min_Xboundary[0] >= 0 && Min_Yboundary[0] >= 0 && Max_Xboundary[0] - Min_Xboundary[0] >= 0 && Max_Yboundary[0] - Min_Yboundary[0] >= 0
						&& Max_Xboundary[0] <= img.cols
						&& Max_Yboundary[0] <= img.rows)
					{
						Rect m_select(
							(Min_Xboundary[0]),
							(Min_Yboundary[0]),
							(Max_Xboundary[0] - Min_Xboundary[0]),
							(Max_Yboundary[0] - Min_Yboundary[0]));
						roi = img(m_select);
					}
					string Img_Name = ".//title_time/output2" + to_string(1) + ".jpg";
					imwrite(Img_Name, roi);
					imshow("out2", roi);
					vector<int>().swap(Xboundary);
					vector<int>().swap(Yboundary);
				}
			}
			else//表示当前检测到了大于rect
			{
				if (count == 1)
				{
					vector<int> Xboundary;
					vector<int> Yboundary;
					//cout << P[0] << P[1] << P[2] << P[3] <<endl;
					Xboundary.push_back(int(P[0].x));
					Xboundary.push_back(int(P[1].x));
					Xboundary.push_back(int(P[2].x));
					Xboundary.push_back(int(P[3].x));

					Yboundary.push_back(int(P[0].y));
					Yboundary.push_back(int(P[1].y));
					Yboundary.push_back(int(P[2].y));
					Yboundary.push_back(int(P[3].y));

					auto Min_Xboundary = std::min_element(std::begin(Xboundary), std::end(Xboundary));
					auto Max_Xboundary = std::max_element(std::begin(Xboundary), std::end(Xboundary));
					auto Min_Yboundary = std::min_element(std::begin(Yboundary), std::end(Yboundary));
					auto Max_Yboundary = std::max_element(std::begin(Yboundary), std::end(Yboundary));

					int boundary_height = rect.boundingRect().height;
					int boundary_width = rect.boundingRect().width;
					//cout << boundary_height << "     " << Max_Yboundary[0] - Min_Yboundary[0] << endl;
					//cout << boundary_width << "     " << Max_Xboundary[0] - Min_Xboundary[0] << endl;
					//=====================================================================
					//---------------------------------------------------------------------
					//加异常判断
					//---------------------------------------------------------------------
					Mat roi;
					if (Min_Xboundary[0] - 5 >= 0 && Min_Yboundary[0] - 5 >= 0 && Max_Xboundary[0] - Min_Xboundary[0] + 10 >= 0 && Max_Yboundary[0] - Min_Yboundary[0] + 10 >= 0
						&& Max_Xboundary[0] + 5 <= img.cols
						&& Max_Yboundary[0] + 5 <= img.rows)
					{
						Rect m_select(
							(Min_Xboundary[0] - 5),
							(Min_Yboundary[0] - 5),
							(Max_Xboundary[0] - Min_Xboundary[0] + 5 + 5),
							(Max_Yboundary[0] - Min_Yboundary[0] + 5 + 5));
						roi = img(m_select);
					}
					else if (Min_Xboundary[0] >= 0 && Min_Yboundary[0] >= 0 && Max_Xboundary[0] - Min_Xboundary[0] >= 0 && Max_Yboundary[0] - Min_Yboundary[0] >= 0
						&& Max_Xboundary[0] <= img.cols
						&& Max_Yboundary[0] <= img.rows)
					{
						Rect m_select(
							(Min_Xboundary[0]),
							(Min_Yboundary[0]),
							(Max_Xboundary[0] - Min_Xboundary[0]),
							(Max_Yboundary[0] - Min_Yboundary[0]));
						roi = img(m_select);
					}
					string Img_Name = ".//title_time/output1" + to_string(1) + ".jpg";
					imwrite(Img_Name, roi);
					imshow("out1", roi);
					vector<int>().swap(Xboundary);
					vector<int>().swap(Yboundary);
				}
				else
				{
					//cout << P[0] << P[1] << P[2] << P[3] <<endl;
					Xboundary.push_back(int(P[0].x));
					Xboundary.push_back(int(P[1].x));
					Xboundary.push_back(int(P[2].x));
					Xboundary.push_back(int(P[3].x));

					Yboundary.push_back(int(P[0].y));
					Yboundary.push_back(int(P[1].y));
					Yboundary.push_back(int(P[2].y));
					Yboundary.push_back(int(P[3].y));

					auto Min_Xboundary = std::min_element(std::begin(Xboundary), std::end(Xboundary));
					auto Max_Xboundary = std::max_element(std::begin(Xboundary), std::end(Xboundary));
					auto Min_Yboundary = std::min_element(std::begin(Yboundary), std::end(Yboundary));
					auto Max_Yboundary = std::max_element(std::begin(Yboundary), std::end(Yboundary));

					int boundary_height = rect.boundingRect().height;
					int boundary_width = rect.boundingRect().width;
					//cout << boundary_height << "     " << Max_Yboundary[0] - Min_Yboundary[0] << endl;
					//cout << boundary_width << "     " << Max_Xboundary[0] - Min_Xboundary[0] << endl;
					//=====================================================================
					//---------------------------------------------------------------------
					//加异常判断
					//---------------------------------------------------------------------
					Mat roi;
					if (Min_Xboundary[0] - 5 >= 0 && Min_Yboundary[0] - 5 >= 0 && Max_Xboundary[0] - Min_Xboundary[0] + 10 >= 0 && Max_Yboundary[0] - Min_Yboundary[0] + 10 >= 0
						&& Max_Xboundary[0] + 5 <= img.cols
						&& Max_Yboundary[0] + 5 <= img.rows)
					{
						Rect m_select(
							(Min_Xboundary[0] - 5),
							(Min_Yboundary[0] - 5),
							(Max_Xboundary[0] - Min_Xboundary[0] + 5 + 5),
							(Max_Yboundary[0] - Min_Yboundary[0] + 5 + 5));
						roi = img(m_select);
					}
					else if (Min_Xboundary[0] >= 0 && Min_Yboundary[0] >= 0 && Max_Xboundary[0] - Min_Xboundary[0] >= 0 && Max_Yboundary[0] - Min_Yboundary[0] >= 0
						&& Max_Xboundary[0] <= img.cols
						&& Max_Yboundary[0] <= img.rows)
					{
						Rect m_select(
							(Min_Xboundary[0]),
							(Min_Yboundary[0]),
							(Max_Xboundary[0] - Min_Xboundary[0]),
							(Max_Yboundary[0] - Min_Yboundary[0]));
						roi = img(m_select);
					}
					string Img_Name = ".//title_time/output2" + to_string(1) + ".jpg";
					imwrite(Img_Name, roi);
					imshow("out2", roi);
				}
			}
			/*for (int j = 0; j <= 3; j++)
			{
				line(img, P[j], P[(j + 1) % 4], Scalar(0, 255, 0), 2);
			}*/
		}
		vector<int>().swap(Xboundary);
		vector<int>().swap(Yboundary);
	}
	else
	{

	}

	//5.显示带轮廓的图像
	imshow("img", img);
	imwrite("./title_time/imgDrawRect.jpg", img);

	waitKey(0);
}

int _tmain(int argc, _TCHAR* argv[])
{
	Mat SrcImage = imread(ImagePath);
	DetectTextRegion(SrcImage);

	return 0;
}

I hope I can help you,If you have any questions, please  comment on this blog or send me a private message. I will reply in my free time. 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章