Удаление горизонтальных и вертикальных сеток в изображении судоку

#python #python-3.x #opencv #computer-vision

#python #python-3.x #opencv #компьютерное зрение

Вопрос:

Это мое входное изображение:

введите описание изображения здесь

Это изображение судоку в оттенках серого с сетками и 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
  

Это мое текущее выходное изображение:

введите описание изображения здесь

Я сталкиваюсь с 2 проблемами :

  • Не все горизонтальные и вертикальные сетки удалены.

  • Код также удаляет часть числа 4.

Как я могу исправить свой код?

Комментарии:

1. Вау, круто! @nathancy

Ответ №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. Выбор правильного способа зависит от того, чем вы хотите заменить эту сетку.

Метод 1: использование 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()
  

Результаты:

введите описание изображения здесь

Способ 1

введите описание изображения здесь

Метод 2 (радиус 5 пикселей)

введите описание изображения здесь

Метод 2 (радиус 1 пиксель)