我們在對錶格進行提取之前,要先對掃描進來的表格進行矯正,由於我做的項目的原因,所需要的處理的表格全都是掃描版的,所以不會出現前文表格線是彎曲的情況,所以表格矯正的方法比較簡單。
請參考:https://blog.csdn.net/asiwxy/article/details/82955185
將表格矯正之後,我們接下來應該對錶格進行提取,首先,先對傳進來的表格模板進行圖形處理,先對將要使用的函數進行介紹:
一,函數介紹
(1) 函數 findContours(....) 用於查找輪廓。
void findContours//提取輪廓,用於提取圖像的輪廓
(
InputOutputArray image,//輸入圖像,必須是8位單通道圖像,並且應該轉化成二值的
OutputArrayOfArrays contours,//檢測到的輪廓,每個輪廓被表示成一個point向量
OutputArray hierarchy,//可選的輸出向量,包含圖像的拓撲信息。其中元素的個數和檢測到的輪廓的數量相等
int mode,//說明需要的輪廓類型和希望的返回值方式
int method,//輪廓近似方法
Point offset = Point()
)
裏面的參數介紹請參照:https://blog.csdn.net/qq_29540745/article/details/52496477
(2)函數 drawContours(....) 用於畫出輪廓。
void drawContours//繪製輪廓,用於繪製找到的圖像輪廓
(
InputOutputArray image,//要繪製輪廓的圖像
InputArrayOfArrays contours,//所有輸入的輪廓,每個輪廓被保存成一個point向量
int contourIdx,//指定要繪製輪廓的編號,如果是負數,則繪製所有的輪廓
const Scalar& color,//繪製輪廓所用的顏色
int thickness = 1, //繪製輪廓的線的粗細,如果是負數,則輪廓內部被填充
int lineType = 8, /繪製輪廓的線的連通性
InputArray hierarchy = noArray(),//關於層級的可選參數,只有繪製部分輪廓時纔會用到
int maxLevel = INT_MAX,//繪製輪廓的最高級別,這個參數只有hierarchy有效的時候纔有效
//maxLevel=0,繪製與輸入輪廓屬於同一等級的所有輪廓即輸入輪廓和與其相鄰的輪廓
//maxLevel=1, 繪製與輸入輪廓同一等級的所有輪廓與其子節點。
//maxLevel=2,繪製與輸入輪廓同一等級的所有輪廓與子節點以及子節點的子節點
Point offset = Point()
)
(3) 函數 adaptiveThreshold(.......) 用於自動閾值化
void adaptiveThreshold(InputArray src, //原圖像,無符號的八位
OutputArray dst, //輸出圖像
double maxValue,
int adaptiveMethod, //在領域內計算閾值採用的方法,塊處理
//ADAPTIVE_THRESH_MEAN_C 平均值
//ADAPTIVE_THRESH_GAUSSIAN_C 高斯分佈加權和
int thresholdType, //二值化的類型
//CV_THRESH_BINARY 大於爲最大值
//CV_THRESH_BINARY_INV 小於爲最大值
int blockSize, //塊的大小,爲基數
double C) //偏移量,但不知道有什麼用....尷尬
參數介紹:
1). int adaptiveMethod :塊處理的方法,
ADAPTIVE_THRESH_MEAN_C 相當於對圖片進行均值濾波的處理,計算出領域的平均值再減去第七個參數C
DAPTIVE_THRESH_GAUSSIAN_C 相當於對圖片進行了高斯模糊,計算出領域的平均值再減去第七個參數C
(4)形態學 getStructuringElement(.....)獲取結構元素, erode(......)腐蝕,黑色區域增多, dilate(......)膨脹,白色區域增多。
腐蝕膨脹參數完全一樣。
Mat getStructuringElement(int shape, //矩形:MORPH_RECT;
//交叉形:MORPH_CORSS;
//橢圓形:MORPH_ELLIPSE;
Size esize,
Point anchor = Point(-1, -1)); //錨點,默認在中心
void erode( const Mat& src,
Mat& dst,
const Mat& element, //結構元素
Point anchor=Point(-1,-1), //內核中心點
int iterations=1, //腐蝕次數
int borderType=BORDER_CONSTANT, //推斷邊緣類型
const Scalar& borderValue=morphologyDefaultBorderValue() ); //邊緣值
二,表格提取代碼
提供了源代碼的下載,但是 adaptiveThreshold()函數的最後兩個參數,要根據實際的情況庫,自己慢慢調,要不然對錶格進行處理之後,就會產生一些奇怪的現象,比如,表格的某條線被你弄丟了,或者某條線短了一截。對,沒毛病。。。我被這個坑了好幾天,換了好幾種方法。(瑟瑟發抖。。。)
void dealImage(const Mat & img, Mat & mask, Mat & joints)
{
// 調整尺寸
Mat rsz;
Size size(700, 900);
resize(img, rsz, size);
// 檢查是否爲灰度圖,如果不是,轉化爲灰度圖
Mat gray;
if (rsz.channels() != 1) {
cvtColor(rsz, gray, CV_BGR2GRAY);
}
else {
gray = rsz;
}
//轉化爲二值圖
Mat bw;
adaptiveThreshold(~gray, bw, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 161, -2);
Mat horizontal = bw.clone();
Mat vertical = bw.clone();
int scale = 15; //使用這個變量來增加/減少待檢測的行數
/*
* 提取水平線條
*/
//指定水平軸上的大小
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));
/*
* 提取豎直線條
*/
// 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));
//將兩張圖片進行與操作
bitwise_and(horizontal, vertical, joints);
imshow("joints", joints);
// create a mask which includes the tables
mask = horizontal + vertical;
}
原圖像如下:
圖一
將圖像進行水平方向的腐蝕和膨脹之後,結果如下:
圖二
對圖像進行豎直方向的腐蝕膨脹,結果如下:
圖三
將圖一和圖二相,結果如下:(圖有點黑,其實上面是有小點點的)
圖四
將圖二和圖三相加,結果如下:
經過以上的處理之後,我們就得到了只剩下線條的,完全空白的表格了,這下在對錶格的每個格子進行提取就方便很多了。
參考:http://answers.opencv.org/question/63847/how-to-extract-tables-from-an-image/