我们在对表格进行提取之前,要先对扫描进来的表格进行矫正,由于我做的项目的原因,所需要的处理的表格全都是扫描版的,所以不会出现前文表格线是弯曲的情况,所以表格矫正的方法比较简单。
请参考: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/