身份证识别——iOS端实现身份证检测

前言

1.之前在PC端做过身份证检测识别相关的项目,用的环境是Caffe-SSD训练的VGG16,模型大小大概为90M左右,在PC下,不调用GPU加速的话,处理检测速度并不理想。之后想把这个项目移植到移动端,然后在IPhone XR Max 上做了测试,速度比PC端更慢了,而且体积巨大,根本没有办法应用到项目上。
2.为了能在移动端运行目标检测模型,那只能重新训练,看了一堆资料和测试各种官方Demo,之后选了MobileNetv2-SSDLite,训练框架还是用Caffe。

模型训练

1.关于前期的数据准备与数据样本标注,可以看我之前身份证识别的博客,我训练时还是用VOC2007这种数据格式。
2.MobileNetv2-SSDLite什么训练自己的数据集,可以看这个博客
,博主写得很详细。
3.在训练过程中发现,同样的数据集,同样的迭代次数,caffe-ssd训练出来的模型精度要高出MobileNetv2-SSDLite几个百分点,而且MobileNetv2-SSDLite对特征弱的物体识别很容易出现误检的现象,为了精度能达到可用的级别,唯一的办法是加样本,但身份证这种数据集又比较敏感,很不好收集,想了各种办法,才收集了一万张左右的数据,再写个仿真算法,把数据扩增到十万张左右,迭代20万代左右,精度可以达99.5%。
4.最终的模型大小在14M左右,我放了6个类型在里面,在真机下检测一张图像的速度大概在0.02秒左右,基本上可以达到实时。

应用代码

1.在OpenCV3之后的版本都有dnn这个模块,很好的对接深度学习的模型,我这里用的是OpenCV4.2这个版本,iOS是不支持直接显示OpenCV的Mat这种图像格式的,要把Mat转成UIImage才能在iOS上显示,关于转换的代码可以看我之前的博客。
2.OC是可以直接与C++交互的,所以检测的代码我直接用C++写的。
代码:

bool idDetection(cv::Mat &cv_src, cv::Mat &cv_dst, std::string &model_path, std::string &proto_path, std::vector<std::string> &label)
{
    
    

    if (cv_src.empty())
    {
    
    
        return false;
    }

    cv_dst = cv_src.clone();

    cv::Size reso(300, 300);

    cv::dnn::Net net = cv::dnn::readNet(model_path,proto_path);
    if (net.empty())
    {
    
    
        return false;
    }

    cv::Mat blob = cv::dnn::blobFromImage(cv_src, 1.0, reso, cv::Scalar(0, 0, 0), true, false);
    net.setInput(blob);
    cv::Mat out = net.forward();

    cv::Mat detectionMat(out.size[2], out.size[3], CV_32F, out.ptr<float>());

    float confThreshold = 0.25f;
    float nmsThreshold = 0.5f;
    std::vector<int> classIds;
    std::vector<float> confidences;
    std::vector<cv::Rect> boxes;
    for (int i = 0; i < detectionMat.rows; i++)
    {
    
    
        float confidence = detectionMat.at<float>(i, 2);
        if (confidence > confThreshold)
        {
    
    
            size_t objectClass = (size_t)(detectionMat.at<float>(i, 1));
            int left = static_cast<int>(detectionMat.at<float>(i, 3) * cv_src.cols);
            int top = static_cast<int>(detectionMat.at<float>(i, 4) * cv_src.rows);
            int right = static_cast<int>(detectionMat.at<float>(i, 5) * cv_src.cols);
            int bottom = static_cast<int>(detectionMat.at<float>(i, 6) * cv_src.rows);
            int width = right - left + 1;
            int height = bottom - top + 1;

            classIds.push_back(objectClass);
            boxes.push_back(cv::Rect(left, top, width, height));
            confidences.push_back(confidence);
        }
    }
    std::vector<int> indices;
    cv::dnn::NMSBoxes(boxes, confidences, confThreshold, nmsThreshold, indices);
    std::vector<int> id;

    for (size_t i = 0; i < indices.size(); ++i)
    {
    
    
        int idx = indices[i];
        cv::Rect box = boxes[idx];
        rectangle(cv_dst, filter_ida.at(i).tl(), filter_ida.at(i).br(), cv::Scalar(0, 0, 255), 2, 8, 0);
        //id.push_back(classIds[idx]);
    }
}

3.在Xcode里面,把要与C++交互的源码文件.m更改成.mm,定义一个点击事件,然后添加代码:

-(void)idDetectioBtn
{
    
    
    NSString* const model_file_name = @"inference";
    NSString* const model_file_type = @"caffemodel";
    NSString* const proto_file_name = @"inference";
    NSString* const proto_file_type = @"prototxt";
          
    NSString* model_path = [[NSBundle mainBundle] pathForResource:model_file_name ofType:model_file_type];
    NSString* prototxt_path = [[NSBundle mainBundle] pathForResource:proto_file_name ofType:proto_file_type];
    std::string str_proto = [prototxt_path UTF8String];
    std::string str_model = [model_path UTF8String];
    cv::Mat cv_src,cv_dst;
    UIImageToMat(self.ui_selected_image, cv_src);
    std::vector<std::string> id_label;
    idDetection(cv_src, cv_dst, str_model, str_proto, id_label);
    
    UIImage *ui_image = MatToUIImage(cv_dst);
    self.ui_show_view.image = ui_image;
}

4.运行效果
在这里插入图片描述
在这里插入图片描述
注:
对图像处理有兴趣的可以可以加
在这里插入图片描述

Guess you like

Origin blog.csdn.net/matt45m/article/details/105743203