Estaba haciendo un proyecto divertido: resolver un Sudoku a partir de una imagen de entrada usando OpenCV (como en las gafas de Google, etc.). Y he completado la tarea, pero al final encontré un pequeño problema por el que vine aquí.
Hice la programación usando Python API de OpenCV 2.3.1.
A continuación es lo que hice:
- Lee la imagen
- Encuentra los contornos
- Seleccione el que tenga el área máxima, (y también algo equivalente al cuadrado).
Encuentra los puntos de esquina.
Por ejemplo, a continuación:
( Observe aquí que la línea verde coincide correctamente con el límite real del Sudoku, por lo que el Sudoku se puede deformar correctamente . Verifique la siguiente imagen)
deformar la imagen a un cuadrado perfecto
por ejemplo, imagen:
Realice OCR (para lo cual utilicé el método que le di en OCR de reconocimiento de dígitos simple en OpenCV-Python )
Y el método funcionó bien.
Problema:
Mira esta imagen.
Realizar el paso 4 en esta imagen da el resultado a continuación:
La línea roja dibujada es el contorno original, que es el verdadero contorno del límite de sudoku.
La línea verde dibujada es un contorno aproximado que será el contorno de la imagen deformada.
Lo cual, por supuesto, existe una diferencia entre la línea verde y la línea roja en el borde superior del sudoku. Entonces, al deformar, no obtengo el límite original del Sudoku.
Mi pregunta :
¿Cómo puedo deformar la imagen en el límite correcto del Sudoku, es decir, la línea roja O cómo puedo eliminar la diferencia entre la línea roja y la línea verde? ¿Hay algún método para esto en OpenCV?
fuente
Respuestas:
Tengo una solución que funciona, pero tendrá que traducirla a OpenCV usted mismo. Está escrito en Mathematica.
El primer paso es ajustar el brillo de la imagen, dividiendo cada píxel con el resultado de una operación de cierre:
El siguiente paso es encontrar el área de sudoku, para que pueda ignorar (enmascarar) el fondo. Para eso, uso el análisis de componentes conectados y selecciono el componente que tiene el área convexa más grande:
Al llenar esta imagen, obtengo una máscara para la cuadrícula de sudoku:
Ahora, puedo usar un filtro derivado de segundo orden para encontrar las líneas verticales y horizontales en dos imágenes separadas:
Utilizo el análisis de componentes conectados nuevamente para extraer las líneas de cuadrícula de estas imágenes. Las líneas de la cuadrícula son mucho más largas que los dígitos, por lo que puedo usar la longitud del calibrador para seleccionar solo los componentes conectados a las líneas de la cuadrícula. Al ordenarlos por posición, obtengo imágenes de máscara de 2x10 para cada una de las líneas de cuadrícula verticales / horizontales en la imagen:
A continuación, tomo cada par de líneas de cuadrícula verticales / horizontales, las dilato, calculo la intersección píxel por píxel y calculo el centro del resultado. Estos puntos son las intersecciones de la línea de la cuadrícula:
El último paso es definir dos funciones de interpolación para el mapeo X / Y a través de estos puntos, y transformar la imagen usando estas funciones:
Todas las operaciones son funciones básicas de procesamiento de imágenes, por lo que esto también debería ser posible en OpenCV. La transformación de imagen basada en spline puede ser más difícil, pero no creo que realmente la necesite. Probablemente, usar la transformación de perspectiva que usa ahora en cada celda individual dará resultados suficientemente buenos.
fuente
La respuesta de Nikie resolvió mi problema, pero su respuesta estaba en Mathematica. Así que pensé que debería dar su adaptación OpenCV aquí. Pero después de implementar, pude ver que el código OpenCV es mucho más grande que el código matemático de Nikie. Y también, no pude encontrar el método de interpolación realizado por nikie en OpenCV (aunque se puede hacer usando scipy, lo diré cuando llegue el momento).
1. Preprocesamiento de imagen (operación de cierre)
Resultado:
2. Encontrar el cuadrado de Sudoku y crear una imagen de máscara
Resultado:
3. Encontrar líneas verticales
Resultado:
4. Encontrar líneas horizontales
Resultado:
Por supuesto, este no es tan bueno.
5. Encontrar puntos de cuadrícula
Resultado:
6. Corrigiendo los defectos
Aquí, Nikie hace algún tipo de interpolación, sobre la cual no tengo mucho conocimiento. Y no pude encontrar ninguna función correspondiente para este OpenCV. (Puede ser que esté allí, no lo sé).
Echa un vistazo a este SOF que explica cómo hacer esto usando SciPy, que no quiero usar: transformación de imagen en OpenCV
Entonces, aquí tomé 4 esquinas de cada subcuadro y apliqué Perspectiva de urdimbre a cada una.
Para eso, primero encontramos los centroides.
Pero los centroides resultantes no se ordenarán. Echa un vistazo a la imagen de abajo para ver su orden:
Entonces los ordenamos de izquierda a derecha, de arriba a abajo.
Ahora vea a continuación su orden:
Finalmente aplicamos la transformación y creamos una nueva imagen de tamaño 450x450.
Resultado:
El resultado es casi el mismo que el de Nikie, pero la longitud del código es grande. Puede ser, hay mejores métodos disponibles, pero hasta entonces, esto funciona bien.
Saludos ARCA.
fuente
Podría intentar utilizar algún tipo de modelo basado en cuadrículas de su deformación arbitraria. Y dado que el sudoku ya es una cuadrícula, eso no debería ser demasiado difícil.
Por lo tanto, podría intentar detectar los límites de cada subregión 3x3 y luego deformar cada región individualmente. Si la detección tiene éxito, le daría una mejor aproximación.
fuente
Quiero agregar que el método anterior funciona solo cuando el tablero de sudoku está recto, de lo contrario, la prueba de relación altura / ancho (o viceversa) probablemente fallará y no podrá detectar los bordes del sudoku. (También quiero agregar que si las líneas que no son perpendiculares a los bordes de la imagen, las operaciones sobel (dx y dy) seguirán funcionando, ya que las líneas tendrán bordes con respecto a ambos ejes).
Para poder detectar líneas rectas, debe trabajar en el análisis de contorno o píxel, como contourArea / boundingRectArea, puntos superior izquierdo e inferior derecho ...
Editar: logré verificar si un conjunto de contornos forma una línea o no aplicando regresión lineal y verificando el error. Sin embargo, la regresión lineal funcionó mal cuando la pendiente de la línea es demasiado grande (es decir,> 1000) o está muy cerca de 0. Por lo tanto, aplicar la prueba de razón anterior (en la mayoría de las respuestas votadas) antes de que la regresión lineal sea lógica y funcionó para mí.
fuente
Para eliminar las esquinas no detectadas, apliqué corrección gamma con un valor gamma de 0.8.
El círculo rojo se dibuja para mostrar la esquina que falta.
El codigo es:
Esto se suma a la respuesta de Abid Rahman si faltan algunos puntos de esquina.
fuente
Pensé que esta era una gran publicación, y una gran solución de ARK; Muy bien presentado y explicado.
Estaba trabajando en un problema similar y construí todo. Hubo algunos cambios (es decir, xrange a range, argumentos en cv2.findContours), pero esto debería funcionar de forma inmediata (Python 3.5, Anaconda).
Esta es una compilación de los elementos anteriores, con algunos de los códigos faltantes agregados (es decir, etiquetado de puntos).
fuente