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. 

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