检测盒子/桌子并将其删除

时间:2019-02-25 17:04:24

标签: c++ opencv

如何删除构成框/表的所有垂直和水平线

我已经搜索并尝试过。但是无法使其工作

最近几天尝试搜索它。.找到了一些不起作用的示例。.试图将各个部分放在一起。

cv:Mat img = cv::imread(input, CV_LOAD_IMAGE_GRAYSCALE);

cv::Mat grad;
cv::Mat morphKernel = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(3, 3));
cv::morphologyEx(img, grad, cv::MORPH_GRADIENT, morphKernel);

cv::Mat res;
cv::threshold(grad, res, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);

// find contours
cv::Mat mask = cv::Mat::zeros(res.size(), CV_8UC1);
std::vector<std::vector<cv::Point>> contours;
std::vector<cv::Vec4i> hierarchy;
cv::findContours(res, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);

for(int i = 0; i < contours.size(); i++){
    cv::Mat approx;
    double peri = cv::arcLength(contours[i], true);
    cv::approxPolyDP(contours[i], approx, 0.04 * peri, true);
    int num_vertices = approx.rows;

    if(num_vertices == 4){
        cv::Rect rect = cv::boundingRect(contours[i]);

        // this is a rectangle
    }
}

enter image description here

enter image description here

3 个答案:

答案 0 :(得分:1)

您可以尝试类似的方法:

  • 阈值图像
  • 计算连接的组件
  • 删除至少四个边界框顶部中的三个与粒子接触的粒子

这应该给你这样的东西: result

以下是相关的源代码:

#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <limits>

using namespace cv;

struct BBox {

    BBox() :
        _xMin(std::numeric_limits<int>::max()),
        _xMax(std::numeric_limits<int>::min()),
        _yMin(std::numeric_limits<int>::max()),
        _yMax(std::numeric_limits<int>::min())
    {}

    int _xMin;
    int _xMax;
    int _yMin;
    int _yMax;
};

int main()
{
    // read input image
    Mat inputImg = imread("test3_1.tif", IMREAD_GRAYSCALE);

    // create binary image
    Mat binImg;
    threshold(inputImg, binImg, 254, 1, THRESH_BINARY_INV);

    // compute connected components
    Mat labelImg;
    const int nbComponents = connectedComponents(binImg, labelImg, 8, CV_32S);

    // compute associated bboxes
    std::vector<BBox> bboxColl(nbComponents);
    for (int y = 0; y < labelImg.rows; ++y) {

        for (int x = 0; x < labelImg.cols; ++x) {

            const int curLabel = labelImg.at<int>(y, x);
            BBox& curBBox = bboxColl[curLabel];
            if (curBBox._xMin > x)
                curBBox._xMin = x;
            if (curBBox._xMax < x)
                curBBox._xMax = x;
            if (curBBox._yMin > y)
                curBBox._yMin = y;
            if (curBBox._yMax < y)
                curBBox._yMax = y;
        }
    }

    // parse all labels
    std::vector<bool> lutTable(nbComponents);
    for (int i=0; i<nbComponents; ++i) {

        // check current label width
        const BBox& curBBox = bboxColl[i];
        if (curBBox._xMax - curBBox._xMin > labelImg.cols * 0.3)
            lutTable[i] = false;
        else
            lutTable[i] = true;
    }

    // create output image
    Mat resImg(binImg);
    MatConstIterator_<int> iterLab = labelImg.begin<int>();
    MatIterator_<unsigned char> iterRes = resImg.begin<unsigned char>();
    while (iterLab != labelImg.end<int>()) {

        if (lutTable[*iterLab] == true)
            *iterRes = 1;
        else
            *iterRes = 0;

        ++iterLab;
        ++iterRes;
    }

    // write result
    imwrite("resImg3_1.tif", resImg);
}

我只需删除所有大于图像总宽度30%的标签。您的图片很吵,所以我不能像以前说的那样使用边框顶部的修饰,对不起...

不知道这是否与您所有的图片都匹配,但是您可以添加一些几何滤镜来改进第一个版本。

此致

答案 1 :(得分:1)

您可以为此目的使用LineSegmentDetector

import numpy as np
import cv2

image = cv2.imread("image.png")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# This is the detector, you might have to play with the parameters
lsd = cv2.createLineSegmentDetector(0, _scale=0.6)

lines, widths, _, _ = lsd.detect(gray)

if lines is not None:
    for i in range(0, len(lines)):
        l = lines[i][0]
        # Much slower version of Euclidean distance
        if np.sqrt((l[0]-l[2])**2 + (l[1]-l[3])**2) > 50:
            # You might have to tweak the threshold as well for other images
            cv2.line(image, (l[0], l[1]), (l[2], l[3]), (255, 255, 255), 3, 
                     cv2.LINE_AA)
cv2.imwrite("result.png", image)

输出:

Result

Result

如您所见,顶部图像中的行并未完全删除,因此我将 tweatking 部分留给您。希望对您有帮助!

答案 2 :(得分:0)

我想使用此答案框发表一些评论。

首先,如果您可以轻松地直观地看到输出效果,则它更容易查看进度。考虑到这一点,这里是代码的更新,重点是查看临时结果。我在Win10中使用VS Studio Community 2017OpenCV version 4.0.1(64位)供任何想重复此练习的人使用。有一些例程需要OpenCV 4的更新...

#include "pch.h"
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

int main()
{    
    cv::Mat img = cv::imread("0zx9Q.png", cv::IMREAD_GRAYSCALE );        // --> Contour size = 0x000000e7 hex (231 each)
    // cv::Mat img = cv::imread("0zx9Q.png", cv::IMREAD_REDUCED_GRAYSCALE_2); // --> Contour size = 0x00000068 hex (104 each)
    // cv::Mat img = cv::imread("0zx9Q.png", cv::IMREAD_REDUCED_GRAYSCALE_4); // --> Contour size = 0x0000001f hex (31 each)
    // cv::Mat img = cv::imread("0zx9Q.png", cv::IMREAD_REDUCED_GRAYSCALE_8); // --> Contour size = 0x00000034 hex (52 each)

    if (!img.data)                              // Check for invalid input
    {
        std::cout << "Could not open or find the image" << std::endl;
        return -1;
    }

    // cv::namedWindow("Display Window - GrayScale Image", cv::WINDOW_NORMAL);   // Create a window for display.
    // cv::imshow("Display Window - GrayScale Image", img);                      // Show our image inside it.
    // cv::waitKey(0);                                                           // Wait for a keystroke in the window

    cv::Mat imgOriginal = cv::imread("0zx9Q.png", cv::IMREAD_UNCHANGED);
    cv::namedWindow("Display Window of Original Document", cv::WINDOW_NORMAL);   // Create a window for display.

    cv::Mat grad;
    cv::Mat morphKernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(25, 25));
    // MORPH_ELLIPSE, contourSize: 0x00000005    when 60,60... but way slow...
    // MORPH_ELLIPSE, contourSize: 0x00000007    when 30,30...  
    // MORPH_ELLIPSE, contourSize: 0x00000007    when 20,20... 
    // MORPH_ELLIPSE, contourSize: 0x0000000a    when 15,15...
    // MORPH_ELLIPSE, contourSize: 0x0000007a    when 5,5...
    // MORPH_ELLIPSE, contourSize: 0x000000e7    when 3,3 and IMREAD_GRAYSCALE 
    // MORPH_CROSS,   contourSize: 0x0000008e    when 5,5 
    // MORPH_CROSS,   contourSize: 0x00000008    when 25,25 
    // MORPH_RECT,    contourSize: 0x00000007    when 25,25 

    cv::morphologyEx(img, grad, cv::MORPH_GRADIENT, morphKernel);

    cv::Mat res;
    cv::threshold(grad, res, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);

    // find contours
    cv::Mat mask = cv::Mat::zeros(res.size(), CV_8UC1);
    std::vector<std::vector<cv::Point>> contours;
    std::vector<cv::Vec4i> hierarchy;
    cv::findContours(res, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);

    int contourSize = contours.size();
    std::cout << " There are a total of " << contourSize << " contours. \n";
    for (int i = 0; i < contourSize; i++) {
        cv::Mat approx;
        double peri = cv::arcLength(contours[i], true);
        cv::approxPolyDP(contours[i], approx, 0.04 * peri, true);
        int num_vertices = approx.rows;
        std::cout << " Contour # " << i << " has " << num_vertices << " vertices.\n";
        if (num_vertices == 4) {
            cv::Rect rect = cv::boundingRect(contours[i]);
            cv::rectangle(imgOriginal, rect, cv::Scalar(255, 0, 0), 4);
        }
    }

    cv::imshow("Display Window of Original Document", imgOriginal);           // Show our image inside it.
    cv::waitKey(0);                                                           // Wait for a keystroke in the window
}

话虽如此,getStructuringElement()的参数非常重要。我花了很多时间尝试不同的选择,但结果却参差不齐。事实证明,有很多findContours()响应没有四个顶点。我怀疑整个findContours()方法可能有缺陷。我经常会在单词和短语的文本字符周围发现错误的矩形。此外,围绕某些框状区域的较浅线条将被忽略。

Highlighted Rectangles within original document

相反,如果C ++而非python存在这样的响应,我想我会努力研究直线检测via techniques discussed here。也许是here还是here?我希望线路检测技术最终能获得更好的结果。嘿,如果所选的文档/图像始终包含白色背景,则可以很容易地通过LineTypes: cv::FILLED

此处提供的信息,不是作为已发布问题的答案,而是作为直观地确定未来成功的方法。