Suraj
Suraj

Reputation: 2477

Removing horizontal and vertical grids in sudoku image

This is my input image:

enter image description here

It is a grayscale sudoku image with grids and 81 numbers.

I tried to remove horizontal and vertical grids from this image using opencv by referring some websites.

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

This is my current output image:

enter image description here

I face 2 problems :

How can I correct my code?

Upvotes: 0

Views: 682

Answers (3)

kavko
kavko

Reputation: 2831

There are several ways how to complete such a task. In addition to other answers I have made two other examples on how to achieve that with numpy and OpenCV. Choosing the right way matters on with what you wish to replace that grid with.

Method 1: Using cv2.inpaint() function Method 2: Find white pixel and draw them out

# 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()

Results:

enter image description here

Method 1

enter image description here

Method 2 (5 pixel radius)

enter image description here

Method 2 (1 pixel radius)

Upvotes: 1

Ahx
Ahx

Reputation: 7985

    1. Detect the line, using fastLineDetector

    1. Set length threshold

    1. Draw the line the same as the background.

Output:


  • enter image description here

Code:


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()

We first check if the lines are detected:

if lines is not None:

If the lines are detected, then get the coordinates:

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

Then draw line:

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

You can change the thickness of the line, For instance, if you set the thickness to 10.

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

Output:

enter image description here

Upvotes: 1

Mark Setchell
Mark Setchell

Reputation: 207465

I don't know if this approach will work for all your images, but I notice that there are white lines at all the places where you want the grids removed and all the remainder of the image is dark, so they seem a useful place to target for processing.

I used ImageMagick but the approach can easily be converted to OpenCV. So, here are the steps:

  • clone image and threshold to get the light coloured lines white and the rest black

  • dilate the white areas with a size 3 square so the white lines will expand to cover the nearby black grids

  • replace white with grey(171) to match your background

  • make black transparent

  • composite the result over the original to conceal white lines and nearby black areas with grey


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

enter image description here

Upvotes: 2

Related Questions