C++ opencv table detection

今天介紹一個正常表格的檢測方法,針對在本次項目中的另一個對象。這個算法採用的是opencv中的查找閉合輪廓的方法來確定是否爲一個表格。但是這個方法很有很大的缺點,閉合輪廓裏面是否爲表格的準確性不好確定。

  • ---->Today I will introduce a normal table detection method for another object in this project.This algorithm uses the method of finding closed contour in opencv to determine whether it is a table or not.However, this method has great disadvantages. The accuracy of the table in the closed contour is not sure.

基本上只要是正常表格,傳統的算法還是很可觀的。這種方法很簡單,這裏不多做解釋了。

  • ---->Basically as long as it is a normal table, the traditional algorithm is still very impressive.This method is very simple, and I won't explain it here.

首先,形態學檢測直線,提取橫向直線和縱向直線。

  • ---->First, the morphology detects the straight lines, and extracts the horizontal and vertical lines.

將橫向直線圖像與縱向直線圖像進行疊加,可以得到只有橫向和縱向的表格框線圖像。

  • ---->The horizontal and vertical line images can be superimposed to obtain only horizontal and vertical line images.
string filename = "1-0.jpg";
Mat src = imread(filename);

// 檢查圖像是否加載良好
if (!src.data)
	cerr << "Problem loading image!!!" << endl;

//    // Show source image
//    imshow("src", src);

// 出於實際原因調整大小
//Mat rsz;
//Size size(800, 900);
//resize(src, rsz, size);
resize(src, src, Size(src.cols / 1.5, src.rows / 1.5), 0, 0, INTER_LINEAR);
//imshow("rsz", rsz);

// 源圖像轉換爲灰度圖
Mat gray;

if (src.channels() == 3)
{
	cvtColor(src, gray, CV_BGR2GRAY);
}
else
{
	gray = src;
}
//imshow("gray", gray);// Show gray image

// Apply adaptiveThreshold at the bitwise_not of gray, notice the ~ symbol
Mat bw;
threshold(gray, bw, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);
//threshold(~gray, bw, 170, 255, CV_THRESH_BINARY);
//imshow("binary", bw);
//創建將用於提取水平線和垂直線的圖像
Mat horizontal = bw.clone();
Mat vertical = bw.clone();

int scale = 64; // 使用此變量以增加/減少要檢測的行數

int horizontalsize = horizontal.cols / scale; //在水平軸上指定尺寸

//創建用於通過形態學操作提取水平線的結構元素
Mat horizontalStructure = getStructuringElement(MORPH_RECT/*內核的形狀是矩形*/, Size(horizontalsize, 1)/*內核尺寸*/);

// 應用形態學運算,先腐蝕再膨脹
erode(horizontal, horizontal, horizontalStructure, Point(-1, -1));
dilate(horizontal, horizontal, horizontalStructure, Point(-1, -1));

// 顯示提取的水平線
//imshow("horizontal", horizontal);
// Specify size on vertical axis
int verticalsize = vertical.rows / scale;

// 創建用於通過形態學操作提取垂直線的結構元素
Mat verticalStructure = getStructuringElement(MORPH_RECT, Size(1, verticalsize));

// 應用形態學運算,先腐蝕再膨脹
erode(vertical, vertical, verticalStructure, Point(-1, -1));
dilate(vertical, vertical, verticalStructure, Point(-1, -1));

// 顯示提取的垂直線
//imshow("vertical", vertical);
//創建一個包含表格的遮罩
Mat mask = horizontal + vertical;
//imshow("mask", mask);

通過函數與操作實現交點的提取, 通過函數findContours找到輪廓圖像。

  • --->Through the function of "&" to extract the intersection, find the contour image through the function findContours.
/*找到表格線之間的接縫,我們將使用此信息從圖片中區分表格
(表格將包含4個以上的接縫,而圖片僅包含4個接縫(即,在角落處))*/
Mat joints;
bitwise_and(horizontal, vertical, joints);
//imshow("joints", joints);
// 查找外部輪廓,該輪廓很可能屬於表格或圖像
vector<Vec4i> hierarchy;
std::vector<std::vector<cv::Point> > contours;
cv::findContours(mask,      //輸入圖像
	contours,               //檢測到的輪廓,每個輪廓被表示成一個point向量
	hierarchy,              //可選的輸出向量,包含圖像的拓撲信息。其中元素的個數和檢測到的輪廓的數量相等
	CV_RETR_EXTERNAL,       //說明需要的輪廓類型和希望的返回值方式,CV_RETR_EXTERNAL 只檢測出最外輪廓
	CV_CHAIN_APPROX_SIMPLE, //壓縮水平,垂直或斜的部分,只保存最後一個點
	Point(0, 0));

正確框選可能存在的表格區域,準確來說框選出來的是一個閉合區域,不一定是表格區域。只要是閉合的都是可以框選出來的。這個算法需要更近一步的改進。那麼如何進行進一步的改進,這是個遺留的問題,那麼繼續思考。

  • ---->The correct box selects the possible table area, exactly the box selects a closed area, not necessarily the table area.Anything that's closed can be boxed.This algorithm needs further improvement.So how to make further improvements, that's a left question, so keep thinking about it.

對於傳統算法的改進,是一個比較難搞的話題。現在只能慢慢思考啦。

  • --->It is a difficult topic to improve the traditional algorithm.Now I can only think slowly.
//contours代表輸出的多個輪廓
vector<vector<Point> > contours_poly(contours.size());//描述多個輪廓,即將多個輪廓存在一個vector中
vector<Rect> boundRect(contours.size());
vector<Mat> rois;

for (size_t i = 0; i < contours.size(); i++)
{
	//獲取區域的面積,如果小於某個值就忽略,代表是雜線不是表格
	double area = contourArea(contours[i]);

	if (area < 40) // 值是隨機選擇的,需要通過反覆試驗程序自行找到該值
		continue;

	//approxPolyDP 函數用來逼近區域成爲一個形狀,true值表示產生的區域爲閉合區域
	//boundingRect 函數爲將這片區域轉化爲矩形,此矩形包含輸入的形狀
	approxPolyDP(Mat(contours[i]), contours_poly[i], 10, true);
	boundRect[i] = boundingRect(Mat(contours_poly[i]));//獲取最小外接矩形

	// 查找每個表具有的節點數
	Mat roi = joints(boundRect[i]);

	vector<vector<Point> > joints_contours;
	findContours(roi, joints_contours, RETR_CCOMP, CHAIN_APPROX_SIMPLE);

	// 從表格的特性看,如果這片區域的點數小於4,那就代表沒有一個完整的表格,忽略掉
	if (joints_contours.size() <= 4)
		continue;
	//保存這片區域
	//rois.push_back(src(boundRect[i]).clone());
	int x0 = 0, y0 = 0, w0 = 0, h0 = 0;
	x0 = boundRect[i].x;
	y0 = boundRect[i].y;
	w0 = boundRect[i].width;
	h0 = boundRect[i].height;
	//rois.push_back(src(boundRect[i]).clone());
	Rect m_select(
		(x0 - 10),
		(y0 - 40),//開始的Y座標//Min_Y[0] + delta_title
		(w0 + 20),
		(h0 + 50));//終止的Y座標減去初始的Y座標 //Variable_Y_End[1] - Vertical_Black_Y[0] +50
	Mat rectangle_roi = src(m_select);//對目標圖像進行裁剪保存

	rois.push_back(rectangle_roi);
	//rectangle(src, Point(x0 - 10, y0 - 10), Point(x0 + w0 + 10, y0 + h0 + 10), Scalar(0, 255, 0), 2, 8);
	//drawContours( src, contours, i, Scalar(0, 0, 255), CV_FILLED, 8, vector<Vec4i>(), 0, Point() );
	//將矩形畫在原圖上
	//rectangle(src, Point(10, 15), Point(790, 320), Scalar(0, 255, 0), 1, 1, 0);
	//rectangle(src, boundRect[i].tl(), boundRect[i].br(), Scalar(0, 255, 0), 2, 8, 0);//繪製邊界
}
imwrite(".//output.jpg",src);

result:

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.

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