opencv 没有网格的表格,绘制水平和垂直网格线

8fq7wneg  于 2023-03-03  发布在  其他
关注(0)|答案(2)|浏览(282)

我有下面的图像:

注意,上面的图像只是一个例子,我需要一个解决方案,将工作在每一个图像,我的图像可以有不同的大小,更多的空单元格或更少的空单元格,等等(我会尝试总是删除网格线之前,我会在这里找到的解决方案,但仍然)。
我试着在上面画出水平和垂直的图像。

水平功能:

def getHorizontalCnt(old_image):
  # read image
  img = old_image.copy() # cv2.imread(image_path1)
  hh, ww = img.shape[:2]
  # convert to grayscale 
  gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
  # average gray image to one row
  row = cv2.resize(gray, (1,hh), interpolation = cv2.INTER_AREA)
  # threshold on white
  thresh = cv2.threshold(row, 240, 255, cv2.THRESH_BINARY_INV)[1]
  plt.imshow(thresh)
  # get contours
  contours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
  contours = contours[0] if len(contours) == 2 else contours[1]
  return contours, ww

垂直功能:

def getVerticalCnt(old_image):
  # read image
  img = old_image.copy() # cv2.imread(image_path1)
  hh, ww = img.shape[:2]
  # convert to grayscale 
  gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
  # average gray image to one column
  column = cv2.resize(gray, (ww, 1), interpolation = cv2.INTER_AREA)
  # threshold on white
  thresh = cv2.threshold(column, 254, 255, cv2.THRESH_BINARY)[1]
  # get contours
  contours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
  contours = contours[0] if len(contours) == 2 else contours[1]
  return contours, hh

应用它们:

original_image = cv2.imread(image_path)
# Horizontal lines
contours_h, ww_ = getHorizontalCnt(original_image.copy())
# Vertical lines
contours_v, hh_ = getVerticalCnt(original_image.copy()) 
# Draw horizontal
for cntr in contours_h:
    x, y, w, h = cv2.boundingRect(cntr)
    ycenter = y + h //2
    cv2.line(original_image, (0, ycenter), (ww_ - 1, ycenter), (0, 0, 0), 1)
# Draw vertical
for cntr in contours_v:
    x, y, w, h = cv2.boundingRect(cntr)
    xcenter = x + w // 2
    cv2.line(original_image, (xcenter, 0), (xcenter, hh_ - 1), (0, 0, 0), 1)
    
plt.imshow(original_image)

结果:

如果将# Draw horizontal区域从ycenter = y + h //2更改为ycenter = y + h + 2,则会得到以下结果:

在这两个函数中,我尝试了从240到255的阈值,每次都是1比1地提高和降低,但没有一个对我有效(有时我得到了不同的结果,但仍然不是一个好的结果,以上是我到目前为止得到的最好的结果)。

更新,额外背景图像:

输入:

输出:

更新不包含matplotlib.pyplot.imshow()的输入图像

图片链接:https://imgur.com/a/aCv4eZE

lndjwyie

lndjwyie1#

下面的代码在Python/OpenCV中的表格上绘制线条。

  • 读取输入
  • 转换为灰色
  • 阈值(在220,但可能是图像相关的),以消除您的图像中的噪声。注意,您不会看到噪声,除非您在一个高阈值)
  • 将阈值图像平均为一行
  • 阈值为254
  • 应用一个小的水平形态开放连接第5个文本列标题中的句号,这样就不会在那里画额外的线。
  • 得到轮廓线、边界框和X轴中心。
  • 在垂直线结果的输入副本和输入的第二个副本(最终结果)的X坐标处,将等值线绘制为垂直线
  • 将阈值平均到一列
  • 阈值为254
  • 得到轮廓线、边界框和Y轴中心。
  • 在水平线结果的输入副本和输入的前一个第二副本(最终结果)上的Y坐标处绘制等值线作为水平线
  • 保存结果

输入:

import cv2
import numpy as np

# read the input
img = cv2.imread('table4b.png')
hh, ww = img.shape[:2]

# convert to grayscale 
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# threshold on white
thresh = cv2.threshold(gray, 220, 255, cv2.THRESH_BINARY)[1]

# average gray image to one row
row = cv2.resize(thresh, (ww, 1), interpolation = cv2.INTER_AREA)

# threshold on white
thresh1 = cv2.threshold(row, 254, 255, cv2.THRESH_BINARY)[1]

# apply small amount of morphology to merge period with column of text
kernel = cv2.getStructuringElement(cv2.MORPH_RECT , (5,1))
thresh1 = cv2.morphologyEx(thresh1, cv2.MORPH_OPEN, kernel)

# get contours
contours = cv2.findContours(thresh1, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
result = img.copy()
result1 = img.copy()
for cntr in contours:
    x, y, w, h = cv2.boundingRect(cntr)
    xcenter = x + w // 2
    cv2.line(result1, (xcenter,0), (xcenter, hh-1), (0, 0, 0), 1)
    cv2.line(result, (xcenter,0), (xcenter, hh-1), (0, 0, 0), 1)

# average gray image to one column
column = cv2.resize(thresh, (1, hh), interpolation = cv2.INTER_AREA)

# threshold on white
thresh2 = cv2.threshold(column, 254, 255, cv2.THRESH_BINARY)[1]
# get contours

contours = cv2.findContours(thresh2, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
result2 = img.copy()
for cntr in contours:
    x, y, w, h = cv2.boundingRect(cntr)
    ycenter = y + h // 2
    cv2.line(result2, (0, ycenter), (ww-1, ycenter), (0, 0, 0), 1)
    cv2.line(result, (0, ycenter), (ww-1, ycenter), (0, 0, 0), 1)
    
# save results
cv2.imwrite('table4_threshold.png', thresh)
cv2.imwrite('table4_column_lines.png', result1)
cv2.imwrite('table4_row_lines.png', result2)
cv2.imwrite('table4_column_and_row_lines.png', result)
   
# show result
cv2.imshow('thresh', thresh)
cv2.imshow('result1', result1)
cv2.imshow('result2', result2)
cv2.imshow('result', result)
cv2.waitKey(0)

阈值图像:

行线图像:

列线图像:

行线和列线图像:

57hvy0tb

57hvy0tb2#

我试过了,但可能需要为输入图像调整一些阈值...
(The代码是C++,但我认为您可以从注解中了解它的作用。)

int main()
{
    //- Load Image as GrayScale
    cv::Mat Img = cv::imread( "Tbl.png", cv::IMREAD_GRAYSCALE );
    if( Img.empty() ){  std::cout << "imread() failed" << std::endl;    return 0;   }

    //- Count white pixels for each row
    std::vector<int> WhitePixCnts( Img.rows, 0 );
    {
        const unsigned char PixValThresh = 200; //Threshold 1
        for( int y=0; y<Img.rows; ++y )
        {
            const unsigned char *p = Img.ptr<unsigned char>( y );
            for( int x=0; x<Img.cols; ++x, ++p )
            {
                if( *p >= PixValThresh ){   ++WhitePixCnts[y];  }
            }
        }
    }

    //- Grouping consecutive white rows
    std::vector< std::pair<int,int> > WhiteBelts;
    {
        const int CntThresh = (int)( Img.cols * 0.95 ); //Threshold 2

        int BeltTopY = -1;
        for( int y=0; y<Img.rows; ++y )
        {
            if( WhitePixCnts[y] < CntThresh )
            {
                if( BeltTopY >= 0 )
                {
                    WhiteBelts.emplace_back( BeltTopY, y );
                    BeltTopY = -1;
                }
            }
            else if( BeltTopY<0 ){  BeltTopY = y;   }
        }
        if( BeltTopY >= 0 )WhiteBelts.emplace_back( BeltTopY, Img.rows );
    }

    //* (For simplification)
    //  In this sample, if the number of groups is insufficient here, give up.
    if( WhiteBelts.size() < 3 ){    std::cout << "give up" << std::endl;    return 0;   }

    //- Decide the threshold for belt thickness based on the found belt's thickness
    int ThicknessThresh = 0;
    {
        int MaxBeltThickness = 0;

        for( int i=1; i+1<WhiteBelts.size(); ++i ) //* Excluding top and bottom belt, because they will include margin.
        {
            const auto &Belt = WhiteBelts[i];
            MaxBeltThickness = std::max( MaxBeltThickness, Belt.second - Belt.first );
        }
        //Very simple decision...
        ThicknessThresh = (int)( MaxBeltThickness * 0.5 );  //Threshold 3
    }

    //- Accept top, bottom, and enoughly bold belts
    cv::Mat ShowImg;
    cv::cvtColor( Img, ShowImg, cv::COLOR_GRAY2BGR );
    
    for( int i=0; i<(int)WhiteBelts.size(); ++i )
    {
        const auto &Belt = WhiteBelts[i];
        if( i==0  ||  (i+1)==(int)WhiteBelts.size()  ||  ( Belt.second - Belt.first >= ThicknessThresh ) )
        {
            int BeltCenterY = ( Belt.first + Belt.second ) / 2;
            cv::line( ShowImg, cv::Point(0,BeltCenterY), cv::Point(ShowImg.cols,BeltCenterY), cv::Scalar(0,0,255) );
        }
    }
    cv::imshow( "Result", ShowImg );
    cv::waitKey();
    return 0;
}

这是结果:

并且该结果是从90度旋转的输入图像获得的:

(The通过简单地组合这些,可以获得完整的结果。)

相关问题