> 文章列表 > opencv实践项目-修改表格缺失轮廓

opencv实践项目-修改表格缺失轮廓

opencv实践项目-修改表格缺失轮廓

目录

  • 1. 背景
  • 2. 修复步骤
    • 2.1 图像灰度化,并进行高斯模糊
    • 2.2 对图像进行阀值处理
    • 2.3 查找轮廓
    • 2.4 利用存储的值了解表格的位置
    • 2.5 提取所有的水平线和垂直线
    • 2.6 合并垂直和水平的两个模版
  • 3. 完整代码

1. 背景

如果大家在输入图像时,看到的第二行中的单元格线未完全链接,在表格识别种,由于单元格不是闭合的框,算法将无法识别和考虑第二行,本文提出的解决方案不仅适用于这种情况。它也适用于表格中的其他虚线和孔。
在这里插入图片描述

2. 修复步骤

2.1 图像灰度化,并进行高斯模糊

有助于识别线条

	cv::Mat img = cv::imread("/Users/xialz/Downloads/5.png");cv::Mat gray_img;cv::cvtColor(img, gray_img, cv::COLOR_BGR2GRAY);cv::Mat gauss_img;cv::GaussianBlur(gray_img, gauss_img, Size(3,3), 1);

2.2 对图像进行阀值处理

	cv::Mat threshold_img;cv::threshold(gray_img, threshold_img, 0, 255, cv::THRESH_BINARY_INV|THRESH_OTSU);

2.3 查找轮廓

对于所有轮廓,将绘制一个边界矩形以创建表格的框/单元格,然后将这些框与四个值x,y,宽度,高度一起存储起来,并计算最小、最大高度、宽度以及x,y

	std::vector<std::vector<cv::Point>> contrours;cv::findContours(threshold_img, contrours, cv::RETR_TREE, cv::CHAIN_APPROX_NONE);std::vector<cv::Rect> box;std::vector<int> heights, widths, xs, ys;for (auto &c : contrours){cv::Rect rect = cv::boundingRect(c);box.push_back(rect);heights.push_back(rect.height);widths.push_back(rect.width);xs.push_back(rect.x);ys.push_back(rect.y);}sort(heights.begin(), heights.end());int min_height = heights[0];int max_height = heights.at(heights.size()-1);sort(widths.begin(), widths.end());int min_weight = widths[0];int max_weight = widths.at(widths.size()-1);sort(xs.begin(), xs.end());int min_x = xs[0];int max_x = xs.at(xs.size()-1);sort(ys.begin(), ys.end());int min_y = ys[0];int max_y = ys.at(ys.size()-1);

2.4 利用存储的值了解表格的位置

最小的y用于获取表的最上一行,该行可以视为表的起点。
x的最小值是表格的左边缘,要获得近似大小,我们需要检索最大y值,该值是表底部的单元格或行。
最后一行的y值表示单元格的上边缘,而不是单元格的底部。要考虑单元格和表格的整体大小,必须将最后一行的高度加到最大y以检索表格的完整高度。
最大的x将是表格的最后一列,并且连续的是表格的最右边的单元格或者行。
x值是每个单元格的左边缘,并且连续

	int max_y_height = 0, max_x_width = 0;for (auto &b : box){if (b.y == max_y){max_y_height = b.height;}if (b.x == max_x)max_x_width = b.width;}

2.5 提取所有的水平线和垂直线

由于反转,背景为黑色,前景为白色,这意味着表格是白色的,使用形态学的扩张可以是白色区域扩大,现在修复孔和虚线,为了进一步识别表,将考虑所有的单元格

	auto horizontal_kernal = cv::getStructuringElement(cv::MORPH_RECT, Size(50, 1));cv::Mat horizontal_mask;cv::morphologyEx(threshold_img, horizontal_mask, cv::MORPH_OPEN, horizontal_kernal);cv::dilate(horizontal_mask, horizontal_mask, horizontal_kernal, cv::Point(-1, -1), 9);auto vertcal_kernal = cv::getStructuringElement(cv::MORPH_RECT, Size(1, 50));cv::Mat vertical_mask;cv::morphologyEx(threshold_img, vertical_mask, cv::MORPH_OPEN, vertcal_kernal);cv::dilate(vertical_mask, vertical_mask, vertcal_kernal, cv::Point(-1, -1), 9);

2.6 合并垂直和水平的两个模版

使用bitwise_or合并表
使用255-bitwise_or的结果进行反色

    cv::Mat result;cv::bitwise_or(vertical_mask, horizontal_mask, result);result = 255 - result;

3. 完整代码

int main()
{cv::Mat img = cv::imread("/Users/xialz/Downloads/5.png");cv::Mat gray_img;cv::cvtColor(img, gray_img, cv::COLOR_BGR2GRAY);cv::Mat gauss_img;cv::GaussianBlur(gray_img, gauss_img, Size(3,3), 1);cv::Mat threshold_img;cv::threshold(gray_img, threshold_img, 0, 255, cv::THRESH_BINARY_INV|THRESH_OTSU);std::vector<std::vector<cv::Point>> contrours;cv::findContours(threshold_img, contrours, cv::RETR_TREE, cv::CHAIN_APPROX_NONE);std::vector<cv::Rect> box;std::vector<int> heights, widths, xs, ys;for (auto &c : contrours){cv::Rect rect = cv::boundingRect(c);box.push_back(rect);heights.push_back(rect.height);widths.push_back(rect.width);xs.push_back(rect.x);ys.push_back(rect.y);}sort(heights.begin(), heights.end());int min_height = heights[0];int max_height = heights.at(heights.size()-1);sort(widths.begin(), widths.end());int min_weight = widths[0];int max_weight = widths.at(widths.size()-1);sort(xs.begin(), xs.end());int min_x = xs[0];int max_x = xs.at(xs.size()-1);sort(ys.begin(), ys.end());int min_y = ys[0];int max_y = ys.at(ys.size()-1);int max_y_height = 0, max_x_width = 0;for (auto &b : box){if (b.y == max_y){max_y_height = b.height;}if (b.x == max_x)max_x_width = b.width;}auto horizontal_kernal = cv::getStructuringElement(cv::MORPH_RECT, Size(50, 1));cv::Mat horizontal_mask;cv::morphologyEx(threshold_img, horizontal_mask, cv::MORPH_OPEN, horizontal_kernal);cv::dilate(horizontal_mask, horizontal_mask, horizontal_kernal, cv::Point(-1, -1), 9);auto vertcal_kernal = cv::getStructuringElement(cv::MORPH_RECT, Size(1, 50));cv::Mat vertical_mask;cv::morphologyEx(threshold_img, vertical_mask, cv::MORPH_OPEN, vertcal_kernal);cv::dilate(vertical_mask, vertical_mask, vertcal_kernal, cv::Point(-1, -1), 9);cv::Mat result;cv::bitwise_or(vertical_mask, horizontal_mask, result);result = 255 - result;cv::imshow("result", result);cv::waitKey(0);return 0;
}

结果图:
在这里插入图片描述