检测国民身份证并获取详细信息

26

我正在尝试检测下面类型的国家身份证并获取其详细信息。例如,签名位置应在人物图像的右上角找到,在此示例中为"BC"。

enter image description here

我需要在iPhone中实现此应用程序。我考虑使用Opencv,但如何实现标记的细节? 我需要用类似的卡训练应用程序或OCR可以帮助吗?

是否有特定的移动应用实现?

我还查看了card-io,它可以检测信用卡详细信息,那么Card-io是否也检测其他卡片的详细信息?

更新:

我已经使用tesseract进行文本检测。如果图像仅有文本,则tesseract效果很好。因此,我裁剪了红色标记区域,并将其作为输入提供给Tesseract,它可以很好地处理MRZ部分。

有一个IOS实现的Tesseract,我已经测试过了。

我需要做什么?

现在我正在尝试自动化文本检测部分。现在我计划自动化以下项目:

1) 裁剪人脸(我已使用Viola-jones人脸检测器完成)。

2) 需要从照片中获取此示例中的"BC"的首字母。

3) 从身份证中提取/检测MRZ区域。

我正在尝试做2&3,任何想法或代码片段将非常好。


7
如果那是一个真实的人,我希望安东尼不介意他的身份被公开在网络上供所有人查看! - QED
@Vitalik 你说得对,我没有注意到MRZ中的内容。谢谢回复。有没有关于仅查找MRZ部分的想法?我计划尝试使用方形检测来查找MRZ部分。这样可行吗? - 2vision2
类似 - LovaBill
1
@QED。我在ID上看到一个“样本”。这通常意味着它是假的,仅用于测试目的。 - Rob Audenaerde
1
@RobAu QED是正确的,我在他的评论后更改了图片。我从谷歌图片中获取了Antonies的图像,但之后我进行了更改! - 2vision2
显示剩余2条评论
3个回答

24
假设这些ID是按照特定的宽度、高度、偏移量、间距等标准模板准备的,您可以尝试基于模板的方法来处理。检测MRZ会很容易。一旦您在图像中检测到它,找到将MRZ映射到您的模板的变换。当您知道这个变换时,您就可以将模板上的任何区域(例如个人的照片)映射到图像并提取该区域。
下面是一个非常简单的程序,遵循快乐路径。您需要进行更多的处理来普遍定位MRZ(例如,如果存在透视扭曲或旋转)。我只是通过测量图像来准备模板,它不适用于您的情况。我只想传达这个想法。图片来源于维基百科
    Mat rgb = imread(INPUT_FILE);
    Mat gray;
    cvtColor(rgb, gray, CV_BGR2GRAY);

    Mat grad;
    Mat morphKernel = getStructuringElement(MORPH_ELLIPSE, Size(3, 3));
    morphologyEx(gray, grad, MORPH_GRADIENT, morphKernel);

    Mat bw;
    threshold(grad, bw, 0.0, 255.0, THRESH_BINARY | THRESH_OTSU);

    // connect horizontally oriented regions
    Mat connected;
    morphKernel = getStructuringElement(MORPH_RECT, Size(9, 1));
    morphologyEx(bw, connected, MORPH_CLOSE, morphKernel);

    // find contours
    Mat mask = Mat::zeros(bw.size(), CV_8UC1);
    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
    findContours(connected, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));

    vector<Rect> mrz;
    double r = 0;
    // filter contours
    for(int idx = 0; idx >= 0; idx = hierarchy[idx][0])
    {
        Rect rect = boundingRect(contours[idx]);
        r = rect.height ? (double)(rect.width/rect.height) : 0;
        if ((rect.width > connected.cols * .7) && /* filter from rect width */
            (r > 25) && /* filter from width:hight ratio */
            (r < 36) /* filter from width:hight ratio */
            )
        {
            mrz.push_back(rect);
            rectangle(rgb, rect, Scalar(0, 255, 0), 1);
        }
        else
        {
            rectangle(rgb, rect, Scalar(0, 0, 255), 1);
        }
    }
    if (2 == mrz.size())
    {
        // just assume we have found the two data strips in MRZ and combine them
        CvRect max = cvMaxRect(&(CvRect)mrz[0], &(CvRect)mrz[1]);
        rectangle(rgb, max, Scalar(255, 0, 0), 2);  // draw the MRZ

        vector<Point2f> mrzSrc;
        vector<Point2f> mrzDst;

        // MRZ region in our image
        mrzDst.push_back(Point2f((float)max.x, (float)max.y));
        mrzDst.push_back(Point2f((float)(max.x+max.width), (float)max.y));
        mrzDst.push_back(Point2f((float)(max.x+max.width), (float)(max.y+max.height)));
        mrzDst.push_back(Point2f((float)max.x, (float)(max.y+max.height)));

        // MRZ in our template
        mrzSrc.push_back(Point2f(0.23f, 9.3f));
        mrzSrc.push_back(Point2f(18.0f, 9.3f));
        mrzSrc.push_back(Point2f(18.0f, 10.9f));
        mrzSrc.push_back(Point2f(0.23f, 10.9f));

        // find the transformation
        Mat t = getPerspectiveTransform(mrzSrc, mrzDst);

        // photo region in our template
        vector<Point2f> photoSrc;
        photoSrc.push_back(Point2f(0.0f, 0.0f));
        photoSrc.push_back(Point2f(5.66f, 0.0f));
        photoSrc.push_back(Point2f(5.66f, 7.16f));
        photoSrc.push_back(Point2f(0.0f, 7.16f));

        // surname region in our template
        vector<Point2f> surnameSrc;
        surnameSrc.push_back(Point2f(6.4f, 0.7f));
        surnameSrc.push_back(Point2f(8.96f, 0.7f));
        surnameSrc.push_back(Point2f(8.96f, 1.2f));
        surnameSrc.push_back(Point2f(6.4f, 1.2f));

        vector<Point2f> photoDst(4);
        vector<Point2f> surnameDst(4);

        // map the regions from our template to image
        perspectiveTransform(photoSrc, photoDst, t);
        perspectiveTransform(surnameSrc, surnameDst, t);
        // draw the mapped regions
        for (int i = 0; i < 4; i++)
        {
            line(rgb, photoDst[i], photoDst[(i+1)%4], Scalar(0,128,255), 2);
        }
        for (int i = 0; i < 4; i++)
        {
            line(rgb, surnameDst[i], surnameDst[(i+1)%4], Scalar(0,128,255), 2);
        }
    }

结果:照片和姓氏区域为橙色。MRZ为蓝色。 enter image description here

3

Card.io是专为凸起的信用卡设计的。它不能用于此用例。


谢谢回复。我能用其他方法实现这个用例吗? - 2vision2
有足够的努力,就可以做到任何事情。 ;) 我会从这里开始:https://dev59.com/qFDTa4cB1Zd3GeqPKZZp - tomwhipple

3

根据我的测试和其他评论,从这里http://www.pyimagesearch.com/2015/11/30/detecting-machine-readable-zones-in-passport-images/(请参见Don的评论),这个模块还没有准备好用于生产环境(离生产环境还有很远的路要走)。 - comte

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接