Reputation: 2477
This is my input image:
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:
I face 2 problems :
Not all the horizontal and vertical grids are removed.
The code also removes part of the number 4.
How can I correct my code?
Upvotes: 0
Views: 682
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:
Method 1
Method 2 (5 pixel radius)
Method 2 (1 pixel radius)
Upvotes: 1
Reputation: 7985
fastLineDetector
Output:
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:
Upvotes: 1
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
Upvotes: 2