C++ opencv table detection

55 篇文章 0 订阅
50 篇文章 1 订阅

今天介绍一个正常表格的检测方法,针对在本次项目中的另一个对象。这个算法采用的是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.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值