今天介紹一個正常表格的檢測方法,針對在本次項目中的另一個對象。這個算法採用的是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.