Coincidencia de imágenes de OpenCV: formulario de foto frente a plantilla de formulario

Estoy intentando detectar si una foto representa una plantilla de formulario predefinida llena de datos.

Soy nuevo en el procesamiento de imágenes y en OpenCV, pero mi primer bash es usar FlannBasedMatcher y comparar la cantidad de puntos clave detectados.

¿Hay una mejor manera de hacer esto?

lleno-form.jpg

form-template.jpg

import numpy as np import cv2 from matplotlib import pyplot as plt MIN_MATCH_COUNT = 10 img1 = cv2.imread('filled-form.jpg',0) # queryImage img2 = cv2.imread('template-form.jpg',0) # trainImage # Initiate SIFT detector sift = cv2.xfeatures2d.SIFT_create() # find the keypoints and descriptors with SIFT kp1, des1 = sift.detectAndCompute(img1,None) kp2, des2 = sift.detectAndCompute(img2,None) FLANN_INDEX_KDTREE = 1 index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5) search_params = dict(checks = 50) flann = cv2.FlannBasedMatcher(index_params, search_params) matches = flann.knnMatch(des1,des2,k=2) # store all the good matches as per Lowe's ratio test. good = [] for m,n in matches: if m.distance MIN_MATCH_COUNT: print "ALL GOOD!" else: print "Not enough matches are found - %d/%d" % (len(good),MIN_MATCH_COUNT) matchesMask = None 

Creo que usar SIFT y un emparejador de puntos clave es el enfoque más sólido para este problema. Debería funcionar bien con muchas plantillas de formulario diferentes. Sin embargo, al estar patentado el algoritmo SIFT, aquí hay otro enfoque que también debería funcionar bien:

Paso 1: Binarizar

  • Umbral su foto y el formulario de plantilla utilizando la etiqueta THRESH_OTSU .
  • Invierta las dos Mat resultados binarios con la función bitwise_not .

Paso 2: Encuentra las formas ‘delimitadoras rect

Para las dos Mat binarias del Paso 1 :

  • Encuentra el contorno más grande.
  • Use approxPolyDP para aproximar el contorno encontrado a un cuadrilátero (vea la imagen de arriba).

Límite de la forma de la muestra.

En mi código, esto se hace dentro de getQuadrilateral() .

Paso 3: Homografía y Warping.

  • Encuentre la transformación entre el límite de las dos formas con findHomography
  • La deformación del warpPerspective binario de la foto usando warpPerspective (y la homografía Mat calculada previamente).

Forma de muestra deformada

Paso 4: Comparación entre plantilla y foto

  • Dilata la plantilla del binario Mat .
  • Reste la Mat binaria combada y la Mat binaria del formulario de plantilla dilatada.

Resta entre la plantilla y las esteras combadas.

Esto permite extraer las informaciones llenas. Pero también puedes hacerlo al revés:

Forma de plantilla Mat deformada dilatada

En este caso, el resultado de la resta debe ser totalmente negro. Entonces usaría mean para obtener el valor de píxel promedio. Finalmente, si ese valor es menor que (digamos) 2, asumiría que el formulario de la foto coincide con el formulario de la plantilla.


Aquí está el código C ++, no debería ser demasiado difícil de traducir a Python 🙂

 vector getQuadrilateral(Mat & grayscale) { vector> contours; findContours(grayscale, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE); vector indices(contours.size()); iota(indices.begin(), indices.end(), 0); sort(indices.begin(), indices.end(), [&contours](int lhs, int rhs) { return contours[lhs].size() > contours[rhs].size(); }); vector> polygon(1); approxPolyDP(contours[indices[0]], polygon[0], 5, true); if (polygon[0].size() == 4) // we have found a quadrilateral { return(polygon[0]); } return(vector()); } int main(int argc, char** argv) { Mat templateImg, sampleImg; templateImg = imread("template-form.jpg", 0); sampleImg = imread("sample-form.jpg", 0); Mat templateThresh, sampleTresh; threshold(templateImg, templateThresh, 0, 255, THRESH_OTSU); threshold(sampleImg, sampleTresh, 0, 255, THRESH_OTSU); bitwise_not(templateThresh, templateThresh); bitwise_not(sampleTresh, sampleTresh); vector corners_template = getQuadrilateral(templateThresh); vector corners_sample = getQuadrilateral(sampleTresh); Mat homography = findHomography(corners_sample, corners_template); Mat warpSample; warpPerspective(sampleTresh, warpSample, homography, Size(templateThresh.cols, templateThresh.rows)); Mat element_dilate = getStructuringElement(MORPH_ELLIPSE, Size(8, 8)); dilate(templateThresh, templateThresh, element_dilate); Mat diff = warpSample - templateThresh; imshow("diff", diff); waitKey(0); return 0; } 

Espero que sea lo suficientemente claro! 😉

PD Esta gran respuesta me ayudó a recuperar el contorno más grande.