【问题标题】:OpenCV image matching - form photo vs form templateOpenCV 图像匹配 - 从照片与表单模板
【发布时间】:2017-10-19 18:08:03
【问题描述】:

我正在尝试检测一张照片是否代表一个预定义的公式模板,其中填充了数据。

我是图像处理和 OpenCV 的新手,但我的第一次尝试是使用 FlannBasedMatcher 并比较检测到的关键点的数量。

有没有更好的方法来做到这一点?

filled-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 < 0.7*n.distance:
    good.append(m)
if len(good)>MIN_MATCH_COUNT:
  print "ALL GOOD!" 
else:
  print "Not enough matches are found - %d/%d" % (len(good),MIN_MATCH_COUNT)
  matchesMask = None

【问题讨论】:

  • 你能发布你输入的图片(filled-form.jpgtemplate-form.jpg)吗?
  • @ElouarnLaine 已更新
  • filled-form.jpg 的链接对我来说似乎已损坏。无论如何,是的,我认为使用 SIFT 和关键点匹配器是解决这个问题的最可靠的方法。它应该适用于许多不同的表单模板。但是,SIFT 算法已获得专利,您对另一种不依赖 SIFT 的方法感兴趣吗?
  • @ElouarnLaine 谢谢!重新上传了图片。当然,另一种方法是什么?

标签: python opencv image-processing computer-vision


【解决方案1】:

我认为使用 SIFT关键点匹配器 是解决此问题的最稳健 方法。它应该适用于许多不同的表单模板。但是,SIFT 算法已获得专利,这是另一种应该也能很好地工作的方法:

第 1 步:二值化

  • 使用THRESH_OTSU 标签限制您的照片和模板表单。
  • 使用bitwise_not函数反转两个二进制结果Mats。

第 2 步:查找表单的边界矩形

对于 步骤 1 中的两个二进制 Mats:

  • 找到最大的轮廓。
  • 使用approxPolyDP将找到的轮廓近似为四边形(见上图)。

在我的代码中,这是在 getQuadrilateral() 内完成的。

第 3 步:单应性和变形

  • findHomography 找到两种形式的边界矩形之间的转换
  • 使用warpPerspective(以及之前计算的单应性Mat)扭曲照片的二进制Mat

第四步:模板与照片的对比

  • 扩展模板表单的二进制Mat
  • 减去扭曲的二进制Mat 和膨胀模板表单的二进制Mat

这允许提取填充的信息。但你也可以反过来做:

模板形式 - Dilated Warped Mat

在这种情况下,减法的结果应该是全黑的。然后我会使用mean 来获取平均像素值。最后,如果该值小于(假设)2,我会假设照片上的表单与模板表单匹配。


这里是 C++ 代码,翻译成 Python 应该不难:)

vector<Point> getQuadrilateral(Mat & grayscale)
{
    vector<vector<Point>> contours;
    findContours(grayscale, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);

    vector<int> 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<vector<Point>> 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<Point>());
}

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<Point> corners_template = getQuadrilateral(templateThresh);
    vector<Point> 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;
}

我希望它足够清楚! ;)

附:这个great answer 帮我找回了最大的轮廓。

【讨论】:

  • 很棒的解决方案,非常感谢!!我试图给它一个旋转,但我在第 16 行 ([&contours]) 表达式中遇到了一些编译器错误 pastebin.com/aAtB79Ma 知道有什么问题吗?
  • @maephisto 您的编译器必须符合 C++11 才能编译我的代码(发生错误的 lambda 函数确实是 C++11 的特性)。话虽如此,您可以用自己的排序函数(例如冒泡排序)完美地替换sort(indices.begin(), indices.end(), [&amp;contours](int lhs, int rhs) { return contours[lhs].size() &gt; contours[rhs].size(); });
  • 我正在学习opencv和python-opencv,翻译成python并不容易,请问如何旋转示例图像以匹配python中的模板图像?
  • @Sérgio 嗨,在我的示例中,模板和示例图像已经以相同的方式定向。如果不是您的情况,您可以查看heretherethere 以了解如何正确旋转图像。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-18
  • 2013-04-08
  • 2015-01-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多