如何使用OpenCV检测Passport页面的边界?

Meh*_*edi 8 c++ android opencv opencv3.0 opencv4android

我正在尝试开发一种可以用相机扫描Passport页面的扫描仪.

所以从这样的Passport页面: 护照页样本

我想裁掉标记的部分.

我已经编写了使用OpenCV进行边缘检测的代码,它可以找到轮廓,然后近似最大的四边形.最后,它进行了4点透视转换,以获得图像的俯视图.边缘检测代码如下所示:

public static List<MatOfPoint> findContours(Mat src){
    Mat img = src.clone();
    src.release();
    //find contours
    double ratio = getScaleRatio(img.size());
    int width = (int) (img.size().width / ratio);
    int height = (int) (img.size().height / ratio);
    Size newSize = new Size(width, height);
    Mat resizedImg = new Mat(newSize, CvType.CV_8UC4);
    Imgproc.resize(img, resizedImg, newSize);

    Imgproc.medianBlur(resizedImg, resizedImg, 5);

    Mat cannedImg = new Mat(newSize, CvType.CV_8UC1);
    Imgproc.Canny(resizedImg, cannedImg, 70, 200, 3, true);
    resizedImg.release();

    Imgproc.threshold(cannedImg, cannedImg, 200, 255, Imgproc.THRESH_OTSU);

    Mat dilatedImg = new Mat(newSize, CvType.CV_8UC1);
    Mat morph = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(3, 3));
    Imgproc.dilate(cannedImg, dilatedImg, morph, new Point(-1, -1), 2, 1, new Scalar(1));
    cannedImg.release();
    morph.release();

    ArrayList<MatOfPoint> contours = new ArrayList<>();
    Mat hierarchy = new Mat();
    Imgproc.findContours(dilatedImg, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
    hierarchy.release();

    Log.d(TAG, "contours found: " + contours.size());

    Collections.sort(contours, new Comparator<MatOfPoint>() {
        @Override
        public int compare(MatOfPoint o1, MatOfPoint o2) {
            return Double.valueOf(Imgproc.contourArea(o2)).compareTo(Imgproc.contourArea(o1));
        }
    });

    return contours;
}

for(MatOfPoint contour:contours){
         MatOfPoint2f mat = new MatOfPoint2f(contour.toArray());
         double peri = Imgproc.arcLength(mat, true);
         MatOfPoint2f approx = new MatOfPoint2f();
         Imgproc.approxPolyDP(mat, approx, 0.02 * peri, true);

         Point[] points = approx.toArray();
         Log.d("SCANNER", "approx size " + points.length);

         if (points.length == 4) {
              Point[] spoints = CVProcessor.sortPoints(points);

              if (CVProcessor.insideArea(spoints, newSize)) {
                      rectContour = contour;
                      foundPoints = spoints;
                      break;
              }
        }
    }
Run Code Online (Sandbox Code Playgroud)

此代码适用于单页文档,即身份证,信用卡.哪里有4个可区分的边缘.

但是对护照不起作用,因为上边缘不那么与众不同.

输入将取自Android上的摄像头.不知道如何检测护照页面?我正在使用OpenCV 3.1.

以下是一些示例输入(从Google图像搜索获得): 样品1 样本2

dha*_*hka 5

如果您可以找到护照的机器可读区(MRZ)(下图中以红色标出的区域),则可以提取页面.通常,MRZ与其背景之间存在非常好的对比,因此可以使用基于梯度的方法或MSER进行检测.

假设有一个标准模板(即页面的纵横比,MRZ,字段的偏移量等),根据这些模板准备好护照,一旦找到MRZ,就可以很容易地找到页面边框和其他字段例如,下面模板图像中显示的人物照片,其中MRZ以红色标出,页面边框以绿色标出.这是假设没有透视失真.如果存在这样的失真,首先应该更正它然后应用模板.您可以使用MRZ本身来校正失真,因为您知道MRZ区域的纵横比.

图像准备的模板.

模板

检查这里的一个非常简单的实现从一本护照本模板模型基于现场提取的.它不适用于您的图像,并且需要大量参数调整,因此我不建议您立即使用它.我所指的只是为了传达基于模板的提取和其他预处理方法的想法.

但是,如果护照如下图所示弯曲(您可以看到无法使用直线跟踪MRZ边界),则很难纠正失真.

最后,如果您使用的是高分辨率图像,那么下采样和处理它们将是一个好主意,因为它在嵌入式系统上会更快.从下采样图像中找到MRZ后,您可以使用高分辨率图像来细化角点. MRZ