【问题标题】:Removing horizontal and vertical grids in sudoku image删除数独图像中的水平和垂直网格
【发布时间】:2020-09-10 13:28:40
【问题描述】:

这是我的输入图像:

这是一个带有网格和 81 个数字的灰度数独图像。

我通过引用一些网站尝试使用 opencv 从此图像中删除水平和垂直网格。

import cv2
import math
import numpy as np
import matplotlib.pyplot as plt
gray = cv2.imread('gray.png', cv2.IMREAD_GRAYSCALE)

thresh = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, 5, 5 )
horizontal = np.copy(thresh)
vertical = np.copy(thresh)

# Specify size on horizontal axis
cols = horizontal.shape[1]
horizontal_size = math.ceil(cols / 20)

# Create structure element for extracting horizontal lines through morphology operations
horizontalStructure = cv2.getStructuringElement(cv2.MORPH_RECT, (horizontal_size, 1))

# Apply morphology operations
horizontal = cv2.erode(horizontal, horizontalStructure)
horizontal = cv2.dilate(horizontal, horizontalStructure)

# Show extracted horizontal lines
cv2.imwrite("horizontal.jpg", horizontal)

# Specify size on vertical axis
rows = vertical.shape[0]
verticalsize = math.ceil(rows / 20)

# Create structure element for extracting vertical lines through morphology operations
verticalStructure = cv2.getStructuringElement(cv2.MORPH_RECT, (1, verticalsize))

# Apply morphology operations
vertical = cv2.erode(vertical, verticalStructure)
vertical = cv2.dilate(vertical, verticalStructure)

thresh_ = thresh - horizontal - vertical

这是我当前的输出图像:

我面临两个问题:

  • 并非所有水平和垂直网格都被删除。

  • 代码还删除了数字 4 的一部分。

如何更正我的代码?

【问题讨论】:

    标签: python python-3.x opencv computer-vision


    【解决方案1】:

    我不知道这种方法是否适用于您的所有图像,但我注意到您想要移除网格的所有位置都有白线,并且图像的所有其余部分都是黑暗的,所以它们看起来处理目标的有用位置。

    我使用了 ImageMagick,但该方法可以轻松转换为 OpenCV。所以,这里是步骤:

    • 克隆图像和阈值以使浅色线条变为白色,其余为黑色

    • 将白色区域扩大为 3 个正方形,这样白线将扩大以覆盖附近的黑色网格

    • 用灰色(171)替换白色以匹配您的背景

    • 使黑色透明

    • 在原始图像上合成结果以用灰色隐藏白线和附近的黑色区域


    magick sudoku.png \( +clone -threshold 80% -fill "gray(171)" -morphology dilate square:3 -opaque white -transparent black \) -composite result.png
    

    【讨论】:

      【解决方案2】:
        1. 检测线路,使用fastLineDetector

        1. 设置长度阈值

        1. 绘制与背景相同的线条。

      输出:



      代码:


      import cv2
      
      gray = cv2.imread("gray.png", cv2.IMREAD_GRAYSCALE)
      lines = cv2.ximgproc.createFastLineDetector(_length_threshold=15).detect(gray)
      
      if lines is not None:
          for line in lines:
              (x_start, y_start, x_end, y_end) = line[0]
              cv2.line(gray, (x_start, y_start), (x_end, y_end), (172, 172, 172), thickness=4)
      
          cv2.imwrite("gray_result.png", gray)
          cv2.imshow("result", gray)
          cv2.waitKey(0)
          cv2.destroyAllWindows()
      

      我们首先检查是否检测到线条:

      if lines is not None:
      

      如果检测到线条,则获取坐标:

      (x_start, y_start, x_end, y_end) = line[0]
      

      然后画线:

      cv2.line(gray, (x_start, y_start), (x_end, y_end), (172, 172, 172), thickness=4)
      

      您可以更改线条的粗细,例如,如果您将粗细设置为 10。

      cv2.line(gray, (x_start, y_start), (x_end, y_end), (172, 172, 172), thickness=10)
      

      输出:

      【讨论】:

        【解决方案3】:

        有几种方法可以完成这样的任务。除了其他答案之外,我还制作了另外两个示例,说明如何使用 numpy 和 OpenCV 实现这一目标。选择正确的方式对您希望用什么替换该网格很重要。

        方法一:使用cv2.inpaint()函数 方法2:找到白色像素并绘制出来

        # imports
        import cv2
        import numpy as np
        
        img = cv2.imread("sudoku.png")  # read image
        color = img[3, 3]  # color of pixel in (3,3) coordinate
        color = [int(i) for i in color]  # list of integer values of color
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # convert to grayscale
        thresh = cv2.threshold(gray, 250, 255, cv2.THRESH_BINARY)[1]  # threshold image so that only white grid is left
        dst = cv2.inpaint(img.copy(), thresh, 3, cv2.INPAINT_TELEA)  # Method 1: perform inpaint
        coords = np.argwhere(gray==255)  # Method 2: find all coordinates of white pixels
        
        dst2 = img.copy()  # hard copy of original image
        dst3 = img.copy()  # hard copy of original image
        
        # iterate through pixels and draw out the grid (Method 2)
        for i in coords:
        
            cv2.circle(dst2, (i[0], i[1]), 3, color, -1)  # cirle with radius 3
            cv2.circle(dst3, (i[0], i[1]), 1, color, -1)  # circle only one pixel
        
        # Write and display images
        cv2.imwrite("dst.png", dst)
        cv2.imwrite("dst2.png", dst2)
        cv2.imwrite("dst3.png", dst3)
        cv2.imshow("dst", dst)
        cv2.imshow("dst2", dst2)
        cv2.imshow("dst3", dst3)
        
        cv2.waitKey(0)
        cv2.destroyAllWindows()
        

        结果:

        方法一

        方法2(5像素半径)

        方法2(1像素半径)

        【讨论】:

          猜你喜欢
          • 2019-05-26
          • 1970-01-01
          • 2017-07-16
          • 2017-08-09
          • 1970-01-01
          • 2013-05-18
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多