Algoritmo para detectar las esquinas de la hoja de papel en la foto


¿Cuál es la mejor manera de detectar las esquinas de una factura/recibo/hoja de papel en una foto? Esto se utilizará para la corrección de perspectiva posterior, antes del OCR.

Mi enfoque actual ha sido:

RGB > Gray > Detección de bordes Canny con umbral > Dilate(1) > Remove small objects(6) > clear boarder objects > pick larges blog basado en Área convexa. > [detección de esquinas-No implementado]

No puedo evitar pensar que debe haber una más robusta enfoque 'inteligente' / estadístico para manejar este tipo de segmentación. No tengo muchos ejemplos de entrenamiento, pero probablemente podría obtener 100 imágenes juntas.

Contexto más amplio:

Estoy usando matlab para crear prototipos y planeo implementar el sistema en OpenCV y Tesserect-OCR. Este es el primero de una serie de problemas de procesamiento de imágenes que necesito resolver para esta aplicación específica. Así que estoy buscando rodar mi propia solución y volver a familiarizarme con el procesamiento de imágenes algoritmo.

Aquí hay algunas imágenes de muestra que me gustaría que el algoritmo manejara: Si desea aceptar el desafío, las imágenes grandes están en http://madteckhead.com/tmp

Caso 1 http://madteckhead.com/tmp/IMG_0773_sml.jpg caso 2 http://madteckhead.com/tmp/IMG_0774_sml.jpg caso 3 http://madteckhead.com/tmp/IMG_0775_sml.jpg caso 4 http://madteckhead.com/tmp/IMG_0776_sml.jpg

En el mejor de los casos esto da:

Caso 1-astuto http://madteckhead.com/tmp/IMG_0773_canny.jpg caso 1-post canny http://madteckhead.com/tmp/IMG_0773_postcanny.jpg caso 1 - blog más grande http://madteckhead.com/tmp/IMG_0773_blob.jpg

Sin embargo falla fácilmente en otros casos:

Caso 2-astuto http://madteckhead.com/tmp/IMG_0774_canny.jpg caso 2-post canny http://madteckhead.com/tmp/IMG_0774_postcanny.jpg caso 2-blog más grande http://madteckhead.com/tmp/IMG_0774_blob.jpg

Gracias de antemano por todas las grandes ideas! ¡Me encanta!

EDITAR: Progreso de la transformación de Hough

P: ¿Qué algoritmo agruparía las líneas de hough para encontrar esquinas? Siguiendo los consejos de las respuestas, pude usar la transformación Hough, elegir líneas y filtrarlas. Mi enfoque actual es bastante crudo. He hecho la suposición de que la factura siempre será inferior a 15deg fuera de alineación con la imagen. Termino con razonable resultados para líneas si este es el caso (ver abajo). Pero no estoy completamente seguro de un algoritmo adecuado para agrupar las líneas (o votar) para extrapolar las esquinas. Las líneas de Hough no son continuas. Y en las imágenes ruidosas, puede haber líneas paralelas, por lo que se requieren métricas de forma o distancia desde el origen de la línea. Alguna idea?

Caso 1 http://madteckhead.com/tmp/IMG_0773_hough.jpg caso 2 http://madteckhead.com/tmp/IMG_0774_hough.jpg caso 3 http://madteckhead.com/tmp/IMG_0775_hough.jpg caso 4 http://madteckhead.com/tmp/IMG_0776_hough.jpg

Author: Nathan Keller, 2011-07-02

8 answers

Soy amigo de Martin que estaba trabajando en esto a principios de este año. Este fue mi primer proyecto de codificación, y un poco terminó en un poco de prisa, por lo que el código necesita un poco de errr...decodificar... Voy a dar algunos consejos de lo que te he visto haciendo ya, y luego ordenar mi código en mi día libre de mañana.

Primer consejo, OpenCV y python son impresionantes, pasar a ellos tan pronto como sea posible. : D

En lugar de eliminar objetos pequeños y / o ruido, baje las restricciones astutas, por lo que acepta más bordes, y luego encuentre el contorno cerrado más grande (en OpenCV use findcontour() con algunos parámetros simples, creo que usé CV_RETR_LIST). todavía podría luchar cuando está en un pedazo de papel blanco, pero definitivamente estaba proporcionando los mejores resultados.

Para la transformación Houghline2(), intente con CV_HOUGH_STANDARD en lugar de CV_HOUGH_PROBABILISTIC, dará rho y theta valores, definiendo la línea en coordenadas polares, y luego puede agrupar las líneas dentro de una cierta tolerancia a esas.

Mi agrupación funcionó como un buscar tabla, para cada línea de salida de la transformación de hough daría un par rho y theta. Si estos valores estaban dentro, digamos 5% de un par de valores en la tabla, se descartaron, si estaban fuera de ese 5%, se agregó una nueva entrada a la tabla.

Entonces puede hacer análisis de líneas paralelas o distancia entre líneas mucho más fácilmente.

Espero que esto ayude.

 24
Author: Daniel Crowley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-27 17:35:54

Un grupo de estudiantes de mi universidad recientemente demostró una aplicación para iPhone (y una aplicación python OpenCV) que habían escrito para hacer exactamente esto. Según recuerdo, los pasos eran algo así:

  • Filtro mediano para eliminar completamente el texto en el papel (este fue texto escrito a mano en papel blanco con bastante buena iluminación y puede no funcionar con texto impreso, funcionó muy bien). La razón fue que hace que la detección de esquinas sea mucho más fácil.
  • Hough Transformar para líneas
  • Encuentre los picos en el espacio acumulador de Transformación de Hough y dibuje cada línea a través de toda la imagen.
  • Analice las líneas y elimine las que estén muy cerca unas de otras y estén en un ángulo similar (agrupe las líneas en una). Esto es necesario porque la transformación de Hough no es perfecta, ya que está trabajando en un espacio de muestra discreto.
  • Encuentre pares de rectas que son aproximadamente paralelas y que intersecan otros pares para ver qué rectas forman quads.

Esto parecía funcionar bastante bien y pudieron tomar una foto de un pedazo de papel o libro, realizar la detección de esquinas y luego mapear el documento en la imagen en un plano casi en tiempo real (había una sola función OpenCV para realizar el mapeo). No había OCR cuando lo vi funcionar.

 17
Author: Martin Foot,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-07-02 08:03:24

Esto es lo que se me ocurrió después de un poco de experimentación:

import cv, cv2, numpy as np
import sys

def get_new(old):
    new = np.ones(old.shape, np.uint8)
    cv2.bitwise_not(new,new)
    return new

if __name__ == '__main__':
    orig = cv2.imread(sys.argv[1])

    # these constants are carefully picked
    MORPH = 9
    CANNY = 84
    HOUGH = 25

    img = cv2.cvtColor(orig, cv2.COLOR_BGR2GRAY)
    cv2.GaussianBlur(img, (3,3), 0, img)


    # this is to recognize white on white
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(MORPH,MORPH))
    dilated = cv2.dilate(img, kernel)

    edges = cv2.Canny(dilated, 0, CANNY, apertureSize=3)

    lines = cv2.HoughLinesP(edges, 1,  3.14/180, HOUGH)
    for line in lines[0]:
         cv2.line(edges, (line[0], line[1]), (line[2], line[3]),
                         (255,0,0), 2, 8)

    # finding contours
    contours, _ = cv2.findContours(edges.copy(), cv.CV_RETR_EXTERNAL,
                                   cv.CV_CHAIN_APPROX_TC89_KCOS)
    contours = filter(lambda cont: cv2.arcLength(cont, False) > 100, contours)
    contours = filter(lambda cont: cv2.contourArea(cont) > 10000, contours)

    # simplify contours down to polygons
    rects = []
    for cont in contours:
        rect = cv2.approxPolyDP(cont, 40, True).copy().reshape(-1, 2)
        rects.append(rect)

    # that's basically it
    cv2.drawContours(orig, rects,-1,(0,255,0),1)

    # show only contours
    new = get_new(img)
    cv2.drawContours(new, rects,-1,(0,255,0),1)
    cv2.GaussianBlur(new, (9,9), 0, new)
    new = cv2.Canny(new, 0, CANNY, apertureSize=3)

    cv2.namedWindow('result', cv2.WINDOW_NORMAL)
    cv2.imshow('result', orig)
    cv2.waitKey(0)
    cv2.imshow('result', dilated)
    cv2.waitKey(0)
    cv2.imshow('result', edges)
    cv2.waitKey(0)
    cv2.imshow('result', new)
    cv2.waitKey(0)

    cv2.destroyAllWindows()

No es perfecto, pero al menos funciona para todas las muestras:

1234

 16
Author: Vanuan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-09-20 02:42:09

En lugar de comenzar desde detección de bordes, podría usar Detección de esquinas.

Marvin Framework proporciona una implementación del algoritmo de Moravec para este propósito. Usted podría encontrar las esquinas de los papeles como punto de partida. Debajo de la salida del algoritmo de Moravec:

introduzca la descripción de la imagen aquí

 8
Author: Gabriel Ambrósio Archanjo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-27 19:16:58

También puede usar MSER (Regiones extremales de máxima estabilidad) sobre el resultado del operador Sobel para encontrar las regiones estables de la imagen. Para cada región devuelta por MSER puede aplicar casco convexo y aproximación poly para obtener algunos como este:

Pero este tipo de detección es útil para la detección en vivo de más de una sola imagen que no siempre devuelve el mejor resultado.

resultado

 4
Author: Flayn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-17 10:36:57

Después de la detección de bordes, use Transformación de Hough. Luego, coloque esos puntos en un SVM (supporting vector machine) con sus etiquetas, si los ejemplos tienen líneas suaves en ellos, SVM no tendrá ninguna dificultad para dividir las partes necesarias del ejemplo y otras partes. Mi consejo sobre SVM, poner un parámetro como la conectividad y la longitud. Es decir, si los puntos están conectados y son largos, es probable que sean una línea del recibo. Entonces, usted puede eliminar todos los otros puntos.

 3
Author: Hephaestus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-07-02 22:17:31

Aquí tienes el código de @Vanuan usando C++:

cv::cvtColor(mat, mat, CV_BGR2GRAY);
cv::GaussianBlur(mat, mat, cv::Size(3,3), 0);
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Point(9,9));
cv::Mat dilated;
cv::dilate(mat, dilated, kernel);

cv::Mat edges;
cv::Canny(dilated, edges, 84, 3);

std::vector<cv::Vec4i> lines;
lines.clear();
cv::HoughLinesP(edges, lines, 1, CV_PI/180, 25);
std::vector<cv::Vec4i>::iterator it = lines.begin();
for(; it!=lines.end(); ++it) {
    cv::Vec4i l = *it;
    cv::line(edges, cv::Point(l[0], l[1]), cv::Point(l[2], l[3]), cv::Scalar(255,0,0), 2, 8);
}
std::vector< std::vector<cv::Point> > contours;
cv::findContours(edges, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_TC89_KCOS);
std::vector< std::vector<cv::Point> > contoursCleaned;
for (int i=0; i < contours.size(); i++) {
    if (cv::arcLength(contours[i], false) > 100)
        contoursCleaned.push_back(contours[i]);
}
std::vector<std::vector<cv::Point> > contoursArea;

for (int i=0; i < contoursCleaned.size(); i++) {
    if (cv::contourArea(contoursCleaned[i]) > 10000){
        contoursArea.push_back(contoursCleaned[i]);
    }
}
std::vector<std::vector<cv::Point> > contoursDraw (contoursCleaned.size());
for (int i=0; i < contoursArea.size(); i++){
    cv::approxPolyDP(Mat(contoursArea[i]), contoursDraw[i], 40, true);
}
Mat drawing = Mat::zeros( mat.size(), CV_8UC3 );
cv::drawContours(drawing, contoursDraw, -1, cv::Scalar(0,255,0),1);
 3
Author: GBF_Gabriel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-09 21:09:42
  1. Convertir a lab space

  2. Utilice el clúster del segmento 2 de kmeans

  3. A continuación, utilizar contornos o hough en uno de los grupos (intenral)
 1
Author: user3452134,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-29 07:24:19