Esta es la contestación más acertada que encomtrarás compartir, sin embargo obsérvala detenidamente y analiza si es compatible a tu proyecto.
Solución:
Aquí hay una posible solución:
-
Obtener imagen binaria. Convertir imagen a escala de grises y umbral adaptativo
-
Filtre todos los números y el ruido para aislar solo las cajas. Filtramos usando el área de contorno para eliminar los números ya que solo queremos cada celda individual
- Fijar líneas de cuadrícula. Realice un cierre morfológico con un kernel horizontal y vertical para reparar las líneas de la cuadrícula.
- Ordene cada celda en orden de arriba a abajo y de izquierda a derecha. Organizamos cada celda en un orden secuencial usando
imutils.contours.sort_contours()
con eltop-to-bottom
yleft-to-right
parámetro
Aquí está la imagen binaria inicial (izquierda) y los números filtrados + líneas de cuadrícula reparadas + imagen invertida (derecha)
Aquí hay una visualización de la iteración de cada celda.
Los números detectados en cada celda.
Código
import cv2
from imutils import contours
import numpy as np
# Load image, grayscale, and adaptive threshold
image = cv2.imread('1.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.adaptiveThreshold(gray,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV,57,5)
# Filter out all numbers and noise to isolate only boxes
cnts = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
area = cv2.contourArea(c)
if area < 1000:
cv2.drawContours(thresh, [c], -1, (0,0,0), -1)
# Fix horizontal and vertical lines
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,5))
thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, vertical_kernel, iterations=9)
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,1))
thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, horizontal_kernel, iterations=4)
# Sort by top to bottom and each row by left to right
invert = 255 - thresh
cnts = cv2.findContours(invert, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
(cnts, _) = contours.sort_contours(cnts, method="top-to-bottom")
sudoku_rows = []
row = []
for (i, c) in enumerate(cnts, 1):
area = cv2.contourArea(c)
if area < 50000:
row.append(c)
if i % 9 == 0:
(cnts, _) = contours.sort_contours(row, method="left-to-right")
sudoku_rows.append(cnts)
row = []
# Iterate through each box
for row in sudoku_rows:
for c in row:
mask = np.zeros(image.shape, dtype=np.uint8)
cv2.drawContours(mask, [c], -1, (255,255,255), -1)
result = cv2.bitwise_and(image, mask)
result[mask==0] = 255
cv2.imshow('result', result)
cv2.waitKey(175)
cv2.imshow('thresh', thresh)
cv2.imshow('invert', invert)
cv2.waitKey()
Nota: La idea de clasificación se adaptó de una respuesta anterior anterior en la extracción de color del solucionador de cubos de Rubrik.
Tienes la posibilidad difundir este enunciado si si solucionó tu problema.