Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/image-processing/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Opencv 检测国民身份证并获取详细信息_Opencv_Image Processing_Computer Vision_Ocr_Card.io - Fatal编程技术网

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

Opencv 检测国民身份证并获取详细信息,opencv,image-processing,computer-vision,ocr,card.io,Opencv,Image Processing,Computer Vision,Ocr,Card.io,我正在尝试检测以下类型的国家ID并获取其详细信息,例如,签名的位置应位于人物图像的右上角,在本例中为“BC” 我需要在iphone上做这个应用程序。我曾想过使用Opencv,但如何才能实现标记的细节?我是否需要使用类似的卡片或OCR来培训应用程序 是否有针对移动应用程序的特定实现 我还检查了检测信用卡详细信息的卡io,卡io是否也检测其他卡的详细信息 更新: 我已经使用了文本检测。如果图像只有文本,则Tesseract效果良好。因此,我裁剪了红色标记的区域,并将其作为Tesseract的输入,

我正在尝试检测以下类型的国家ID并获取其详细信息,例如,签名的位置应位于人物图像的右上角,在本例中为“BC”

我需要在iphone上做这个应用程序。我曾想过使用Opencv,但如何才能实现标记的细节?我是否需要使用类似的卡片或OCR来培训应用程序

是否有针对移动应用程序的特定实现

我还检查了检测信用卡详细信息的卡io,卡io是否也检测其他卡的详细信息

更新:

我已经使用了文本检测。如果图像只有文本,则Tesseract效果良好。因此,我裁剪了红色标记的区域,并将其作为Tesseract的输入,它与MRZ部件配合得很好

Tesseract有一个测试工具,我已经用它进行了测试

我需要做什么

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

1) 裁剪脸部(我已经使用Viola jones面部检测器完成)

2) 需要从照片中取本例中的首字母“BC”

3) 从ID卡中提取/检测MRZ区域


我正在尝试做2和3,任何想法或代码片段都会很棒。

Card.io是专为浮雕信用卡设计的。它不适用于此用例。

假设这些ID是根据具有特定宽度、高度、偏移、间距等的标准模板准备的,您可以尝试基于模板的方法

MRZ很容易被发现。在图像中检测到MRZ后,找到将模板中的MRZ映射到它的转换。知道此转换后,可以将模板上的任何区域(例如,个人照片)映射到图像并提取该区域

下面是一个非常简单的程序,它遵循一条快乐的道路。通常,您必须进行更多处理才能定位MRZ(例如,如果存在透视扭曲或旋转)。我只是通过测量图像来准备模板,它对你的案例不起作用。我只是想传达这个想法。这张照片是从

Mat rgb=imread(输入文件);
席灰色;
CVT颜色(rgb、灰色、CV_BGr2灰色);
垫梯度;
Mat morphKernel=getStructuringElement(变形椭圆,大小(3,3));
形态学(灰色、渐变、形态梯度、形态核);
Mat bw;
阈值(梯度、bw、0.0、255.0、二元阈值、大津阈值);
//连接水平方向的区域
垫子连接;
morphKernel=getStructuringElement(MORPH_RECT,Size(9,1));
morphologyEx(bw,连通,MORPH_-CLOSE,morphKernel);
//寻找轮廓
Mat mask=Mat::zeros(bw.size(),CV_8UC1);
矢量等值线;
向量层次;
findContours(连通、轮廓、层次、CV_RETR_CCOMP、CV_CHAIN_近似、简单、点(0,0));
载体mrz;
双r=0;
//过滤轮廓
对于(int idx=0;idx>=0;idx=hierarchy[idx][0])
{
Rect Rect=boundingRect(等高线[idx]);
r=直线高度?(双精度)(直线宽度/直线高度):0;
if((rect.width>connected.cols*.7)和&/*从rect width中筛选*/
(r>25)和&/*过滤器从宽度:高比率*/
(r<36)/*滤波器宽度:高比率*/
)
{
mrz.推回(rect);
矩形(rgb,rect,标量(0,255,0),1);
}
其他的
{
矩形(rgb,rect,标量(0,0,255),1);
}
}
如果(2==mrz.size())
{
//假设我们已经在MRZ中找到了两个数据条,并将它们组合起来
CvRect max=cvMaxRect(&(CvRect)mrz[0],&(CvRect)mrz[1]);
矩形(rgb,最大值,标量(255,0,0),2);//绘制MRZ
载体mrzSrc;
载体mrzDst;
//图像中的MRZ区域
mrzDst.推回(点2f((浮动)最大x,(浮动)最大y));
mrzDst.推回(点2F((浮动)(最大x+最大宽度),(浮动)最大y));
mrzDst.推回(点2F((浮动)(最大x+最大宽度),(浮动)(最大y+最大高度));
mrzDst.推回(点2F((浮动)最大x,(浮动)(最大y+最大高度));
//我们模板中的MRZ
mrzSrc.推回(点2f(0.23f,9.3f));
mrzSrc.向后推(点2F(18.0f,9.3f));
mrzSrc.向后推(点2F(18.0f,10.9f));
mrzSrc.推回(点2f(0.23f,10.9f));
//找到转变
Mat t=getPerspectiveTransform(mrzSrc,mrzDst);
//我们模板中的照片区域
矢量光刻;
photoSrc.向后推(点2F(0.0f,0.0f));
photoSrc.向后推(点2F(5.66f,0.0f));
photoSrc.推回(点2F(5.66f,7.16f));
photoSrc.向后推(点2F(0.0f,7.16f));
//我们模板中的姓氏区域
向量src;
姓src.push_back(点2f(6.4f,0.7f));
姓src.push_back(点2f(8.96f,0.7f));
姓src.push_back(点2f(8.96f,1.2f));
姓src.push_back(点2f(6.4f,1.2f));
矢量光电探测器(4);
病媒姓氏dst(4);
//将模板中的区域映射到图像
透视变换(photoSrc,photoDst,t);
透视变换(姓氏src,姓氏dst,t);
//绘制映射区域
对于(int i=0;i<4;i++)
{
线(rgb,photoDst[i],photoDst[(i+1)%4],标量(0128255),2);
}
对于(int i=0;i<4;i++)
{
行(rgb,姓氏DST[i],姓氏DST[(i+1)%4],标量(0128255),2);
}
}
结果:橙色的照片和姓氏区域。蓝色的MRZ。

现在有用于此目的的PassportEye库。这并不完美,但根据我的经验,效果相当不错:

如果这是一个真实的人,那么我希望安托万不介意他的ID被发布到网上,让所有人都能看到!是否要从ID提取数据
    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);
        }
    }