从opencv中的数独中提取框

Extract boxes from sudoku in opencv

我已经使用 opencv 将数独图像转换为数独网格

现在我想从图像中提取每个框,最好的方法是什么?

据我所知,我正在尝试找到线的交点以找到每个框的角

class SudokuSolverPlay:
      def __init__(self, image):
         
      def __preProcess(self, img):
        """return grayscale image"""
      def __maskSudoku(self, img):
        """return masked image"""
      def __dectactEdge(self, img):
        """return sudoku grid"""
        def drawLines(src, dest, iteration=1):
            minLineLength = 100
            src = cv2.convertScaleAbs(src)
            for _ in range(iteration):
                lines = cv2.HoughLinesP(image=src, rho=1, theta=np.pi / 180,
                                    threshold=100, lines=np.array([]),
                                    minLineLength=minLineLength, maxLineGap=100)
                a, b, c = lines.shape
                for i in range(a):
                    x1, y1, x2, y2 = lines[i][0][0], lines[i][0][1], lines[i][0][2], lines[i][0][3]
                    cv2.line(dest, (x1, y1), (x2, y2),255, 1, cv2.LINE_AA)
                src = cv2.convertScaleAbs(dest)

        def findVerticalLines(img):
            imgX = cv2.GaussianBlur(img, (5, 5), 0)
            kernelx = cv2.getStructuringElement(cv2.MORPH_RECT, (2, 10))
            imgY = cv2.Sobel(img, cv2.CV_64F, 1, 0)
            imgY = cv2.convertScaleAbs(imgY)
            cv2.normalize(imgY, imgY, 0, 255, cv2.NORM_MINMAX)
            imgY = cv2.morphologyEx(imgY, cv2.MORPH_CLOSE, kernelx, iterations=1)
            return imgY

        def findHorizontalLines(img):
            """same as above only args different"""

        img1 = np.zeros(img.shape)

        edges = cv2.Canny(img, 50, 150, apertureSize=3)
        laplacian = cv2.Laplacian(edges, cv2.CV_64F)
        drawLines(laplacian, img1, iteration=1)

        sby = findVerticalLines(img1)
        sbx = findHorizontalLines(img1)
        return img1
      def solveSudoku(self):
          gray = self.__preProcess(self.__originalImg)
          masked = self.__maskSudoku(gray)
          grid = self.__dectactGrid(masked)
                   

if __name__ == '__main__':
    colorImg = cv2.imread('sudoku1.jpg')
    solver = SudokuSolverPlay(colorImg)
    solver.solveSudoku()

此处findVerticalLines()findHorizontalLines()无法正确地指示水平和垂直线

一种解决方法是做一个形态学操作,从canny边缘图像中找到垂直和水平线,然后做一个连接的组件分析来找到盒子。 我在下面做了一个示例版本。您可以进一步微调它以使其更好。 我从蒙版图像作为输入开始。

### reading input image
gray_scale=cv2.imread('masked_image.jpg',0)

执行 Canny 边缘检测并添加膨胀层

img_bin = cv2.Canny(gray_scale,50,110)
dil_kernel = np.ones((3,3), np.uint8)
img_bin=cv2.dilate(img_bin,dil_kernel,iterations=1)

现在,膨胀后的二值图像看起来像这样。

假设最小盒子尺寸为 20*20

line_min_width = 20

寻找水平线

kernal_h = np.ones((1,line_min_width), np.uint8)
img_bin_h = cv2.morphologyEx(img_bin, cv2.MORPH_OPEN, kernal_h)

寻找垂直线

kernal_v = np.ones((line_min_width,1), np.uint8)
img_bin_v = cv2.morphologyEx(img_bin, cv2.MORPH_OPEN, kernal_v)

合并并添加一个膨胀层来关闭小间隙

img_bin_final=img_bin_h|img_bin_v
final_kernel = np.ones((3,3), np.uint8)
img_bin_final=cv2.dilate(img_bin_final,final_kernel,iterations=1)

应用连通分量分析

ret, labels, stats,centroids = cv2.connectedComponentsWithStats(~img_bin_final, connectivity=8, ltype=cv2.CV_32S)

可视化连通分量图像

如您所见,我们检测到一些文本也作为方框,我们可以通过简单的过滤条件轻松删除它们,这里我过滤的条件是区域至少应为 1000 像素。

在检测到的框上绘制矩形。

### 1 and 0 and the background and residue connected components whihc we do not require
for x,y,w,h,area in stats[2:]:
#     cv2.putText(image,'box',(x-10,y-10),cv2.FONT_HERSHEY_SIMPLEX, 1.0,(0,255,0), 2)
    if area>1000:
        cv2.rectangle(image,(x,y),(x+w,y+h),(0,255,0),2)

最终输出图像

此答案基于我使用 OpenCV 在图像中查找 checkboxes/tables 的解决方案。您可以在我位于 Towards Data Science 的 blog 中找到详细解释。 希望这会让您更接近解决方案。

编码愉快:)

-- 编辑 1

进行连通分量可视化的代码

def imshow_components(labels):
    ### creating a hsv image, with a unique hue value for each label
    label_hue = np.uint8(179*labels/np.max(labels))
    ### making saturation and volume to be 255
    empty_channel = 255*np.ones_like(label_hue)
    labeled_img = cv2.merge([label_hue, empty_channel, empty_channel])
    ### converting the hsv image to BGR image
    labeled_img = cv2.cvtColor(labeled_img, cv2.COLOR_HSV2BGR)
    labeled_img[label_hue==0] = 0
    ### returning the color image for visualising Connected Componenets
    return labeled_img