OCR 识别小工具(5)身份证识别
busyboxs 发布于2020-12 浏览:10782 回复:0
0
收藏

功能介绍

结构化识别二代居民身份证正反面所有8个字段,包括姓名、性别、民族、出生日期、住址、身份证号、签发机关、有效期限,识别准确率超过99%;同时支持身份证正面头像检测,并返回头像切片的base64编码及位置信息

  • 全字段识别:支持对二代居民身份证正反两面全部字段的结构化识别,充分满足身份证使用场景中对任意字段的识别需求
  • 证件风险检测:检测上传的图片是否为身份证复印件、临时身份证、屏幕翻拍身份证或被PS过的身份证,并返回对应的风险类型
  • 端上质量校验:提供移动端SDK,使用时可实时检测取景框中是否包含身份证,是否存在模糊、欠/过曝等情况,并提示用户矫正,提高图片采集质量,提升识别准确率

应用场景

  • 远程身份认证:使用身份证识别和人脸识别技术,自动识别录入用户身份信息,可应用于金融、保险、电商、O2O、直播等场景,对用户、商家、主播等进行实名身份认证,有效降低用户输入成本,控制业务风险

特色优势

  • 字段齐全:可识别二代居民身份证所有8个字段,不同业务场景下快速提取任意字段信息,满足各类应用需求
  • 准确率高:支持识别各种角度的身份证照片,并针对各少数民族身份证进行专项模型优化,综合识别准确率超过99%
  • 服务稳定:依托百度云技术实力,提供高可靠性、弹性可伸缩、高并发承载的文字识别云端服务,服务可用性高达99.99%

接口描述

支持对二代居民身份证正反面所有8个字段进行结构化识别,包括姓名、性别、民族、出生日期、住址、身份证号、签发机关、有效期限,识别准确率超过99%;同时支持身份证正面头像检测,并返回头像切片的base64编码及位置信息。

同时,支持对用户上传的身份证图片进行图像风险和质量检测,可识别图片是否为复印件或临时身份证,是否被翻拍或编辑,是否存在正反颠倒、模糊、欠曝、过曝等质量问题。

请求说明

  • HTTP 方法: POST
  • 请求 URL: https://aip.baidubce.com/rest/2.0/ocr/v1/idcard
  • URL参数: access_token
  • Header 参数: Content-Type = application/x-www-form-urlencoded
  • Body 参数:见下表

返回说明
返回参数如下表:

返回示例如下:

{
    "log_id": 2648325511,
    "direction": 0,
    "image_status": "normal",
    "photo": "/9j/4AAQSkZJRgABA......",
    "photo_location": {
        "width": 1189,
        "top": 638,
        "left": 2248,
        "height": 1483
    },
    "words_result": {
        "住址": {
            "location": {
                "left": 267,
                "top": 453,
                "width": 459,
                "height": 99
            },
            "words": "南京市江宁区弘景大道3889号"
        },
        "公民身份号码": {
            "location": {
                "left": 443,
                "top": 681,
                "width": 589,
                "height": 45
            },
            "words": "330881199904173914"
        },
        "出生": {
            "location": {
                "left": 270,
                "top": 355,
                "width": 357,
                "height": 45
            },
            "words": "19990417"
        },
        "姓名": {
            "location": {
                "left": 267,
                "top": 176,
                "width": 152,
                "height": 50
            },
            "words": "伍云龙"
        },
        "性别": {
            "location": {
                "left": 269,
                "top": 262,
                "width": 33,
                "height": 52
            },
            "words": "男"
        },
        "民族": {
            "location": {
                "left": 492,
                "top": 279,
                "width": 30,
                "height": 37
            },
            "words": "汉"
        }
    },
    "words_result_num": 6
}

C++ 代码实现调用

这里假设前置准备已经做好了,如果没有,请阅读以下文章;如果有,则直接跳过;

(基础篇 01)在控制台创建对应的应用 https://yangshun.win/blogs/dea770b9/
(基础篇 02)Windows 下使用 Vcpkg 配置百度 AI 图像识别 C++开发环境(VS2017)  https://yangshun.win/blogs/3b103680/
(基础篇 03)C++ 获取 access token  https://yangshun.win/blogs/49f400d2/
(基础篇 04)C++ base64 编解码原理及实现  https://yangshun.win/blogs/3f2fcf2e/
下面的代码中部分函数定义在 util.h 和 util.cpp 中

screenshot/functions/util.h  https://github.com/busyboxs/screenshot/blob/master/functions/util.h
screenshot/functions/util.cpp  https://github.com/busyboxs/screenshot/blob/master/functions/util.cpp
为了方便,首先根据返回参数定义了一个结构体,该结构体包括了返回参数中的参数,如下:

// https://cloud.baidu.com/doc/OCR/s/rk3h7xzck
struct IDCardInfo
{
    uint64_t                          logID{};
    int32_t                           direction{};
    uint32_t                          resultNumber{};
    std::string                       imageStatus{};
    std::string                       riskType{};
    std::string                       editTool{};
    std::string                       photo{};
    Location                          photoLocation{};
    std::map wordsResult{};

    void print()
    {
        std::cout << "log_id: " << logID << '\n';
        std::cout << "direction: " << direction << '\n';
        std::cout << "imageStatus: " << imageStatus << '\n';
        std::cout << "riskType: " << riskType << '\n';
        std::cout << "editTool: " << editTool << '\n';
        std::cout << "photo: " << photo << '\n';
        std::cout << "photo location: ";
        photoLocation.print();
        std::cout << "result number: " << resultNumber << '\n';

        for (auto& [name, res] : wordsResult)
        {
            std::cout << "\t" << UTF8ToGB(name.c_str()) << ": ";
            res.print();
        }

    }

    void draw(cv::Mat& img)
    {
        photoLocation.draw(img);
        for (auto& [name, res] : wordsResult)
        {
            res.draw(img);
        }
    }
};

然后定义了一个类来调用接口并获取结果

class IDCard
{
public:
    IDCard();
    ~IDCard();

    Json::Value request(std::string imgBase64, std::map& options);

    void getResult(IDCardInfo& result);

private:
    Json::Value m_obj;
    std::string m_url;
    // file to save token key
    std::string m_filename;
};

类中的私有成员 m_obj 表示返回结果对应的 json 对象。m_url 表示请求的 url,m_filename 表示用于存储 access token 的文件的文件名。

request 函数输入请求图像的 base64 编码以及请求参数,返回一个 json 对象,json 对象中包含请求的结果。

getResult 将请求的结果进行解析为自定义的结构体数据类型。以便用于后序的打印和绘图等。

完整代码如下

IDCard.h 代码如下: screenshot/functions/IDCard.h https://github.com/busyboxs/screenshot/blob/master/functions/IDCard.h

#pragma once
#include 
#include 
#include 
#include 
#include 
#include "util.h"
#include "customVariables.h"

// https://cloud.baidu.com/doc/OCR/s/rk3h7xzck
struct IDCardInfo
{
    uint64_t                          logID{};
    int32_t                           direction{};
    uint32_t                          resultNumber{};
    std::string                       imageStatus{};
    std::string                       riskType{};
    std::string                       editTool{};
    std::string                       photo{};
    Location                          photoLocation{};
    std::map wordsResult{};

    void print()
    {
        std::cout << "log_id: " << logID << '\n';
        std::cout << "direction: " << direction << '\n';
        std::cout << "imageStatus: " << imageStatus << '\n';
        std::cout << "riskType: " << riskType << '\n';
        std::cout << "editTool: " << editTool << '\n';
        std::cout << "photo: " << photo << '\n';
        std::cout << "photo location: ";
        photoLocation.print();
        std::cout << "result number: " << resultNumber << '\n';

        for (auto& [name, res] : wordsResult)
        {
            std::cout << "\t" << UTF8ToGB(name.c_str()) << ": ";
            res.print();
        }

    }

    void draw(cv::Mat& img)
    {
        photoLocation.draw(img);
        for (auto& [name, res] : wordsResult)
        {
            res.draw(img);
        }
    }
};

class IDCard
{
public:
    IDCard();
    ~IDCard();

    Json::Value request(std::string imgBase64, std::map& options);

    void getResult(IDCardInfo& result);

private:
    Json::Value m_obj;
    std::string m_url;
    // file to save token key
    std::string m_filename;
};

void IDCardTest();
IDCardInfo IDCardDetect(std::string imgPath, bool front);

IDCard.cpp 代码如下: screenshot/functions/IDCard.cpp https://github.com/busyboxs/screenshot/blob/master/functions/IDCard.cpp

#include "IDCard.h"

IDCard::IDCard()
{
    m_url = "https://aip.baidubce.com/rest/2.0/ocr/v1/idcard";
    m_filename = "tokenKey";
}

IDCard::~IDCard()
{
}

Json::Value IDCard::request(std::string imgBase64, std::map& options)
{
    std::string response;
    Json::Value obj;
    std::string token;

    // 1. get HTTP post body
    std::string body;
    mergeHttpPostBody(body, imgBase64, options);

    // 2. get HTTP url with access token
    std::string url = m_url;
    getHttpPostUrl(url, m_filename, token);

    // 3. post request, response store the result
    int status_code = httpPostRequest(url, body, response);
    if (status_code != CURLcode::CURLE_OK) {
        obj["curl_error_code"] = status_code;
        m_obj = obj;
        return obj; // TODO: maybe should exit 
    }

    // 4. make string to json object
    generateJson(response, obj);

    // if access token is invalid or expired, we will get a new one
    if (obj["error_code"].asInt() == 110 || obj["error_code"].asInt() == 111) {
        token = getTokenKey();
        writeFile(m_filename, token);
        return request(imgBase64, options);

    }

    m_obj = obj;

    //checkErrorWithExit(obj);

    return obj;
}

void IDCard::getResult(IDCardInfo& result)
{
    if (m_obj.get("error_code", "null"))
    {
        result.wordsResult["error_code"].words = m_obj.get("error_code", "null").asString();
        result.wordsResult["error_msg"].words = m_obj.get("error_msg", "null").asString();
        return;
    }

    result.logID = m_obj["log_id"].asUInt64();
    result.direction = m_obj["direction"].asInt();
    result.imageStatus = m_obj["image_status"].asString();
    result.editTool = m_obj["edit_tool"].asString();
    result.photo = m_obj["photo"].asString();
    getLocation(m_obj["photo_location"], result.photoLocation);
    result.resultNumber = m_obj["words_result_num"].asInt();

    Json::Value::Members keys = m_obj["words_result"].getMemberNames();

    for (auto it = keys.begin(); it != keys.end(); ++it)
    {
        ResultPart resultPart;
        getResultPart(m_obj["words_result"][*it], resultPart);
        result.wordsResult[*it] = resultPart;
    }
}

void IDCardTest()
{
    std::string img_file = "./images/b.jpg";
    std::string out;
    readImageFile(img_file.c_str(), out);
    std::string img_base64 = base64_encode(out.c_str(), (int)out.size());

    // set options
    std::map options;
    options["id_card_side"] = "front";
    //options["detect_direction"] = "false";
    //options["detect_risk"] = "false";
    options["detect_photo"] = "true";
    //options["detect_rectify"] = "false";

    Json::Value obj;
    IDCard idCardObj;
    IDCardInfo result;
    obj = idCardObj.request(img_base64, options);

    idCardObj.getResult(result);
    result.print();

    cv::Mat img = cv::imread("./images/b.jpg");
    result.draw(img);
    cv::namedWindow("idcard", cv::WINDOW_NORMAL);
    cv::imshow("idcard", img);
    cv::imwrite("./images/idcard_result.jpg", img);
    cv::waitKey();
}

IDCardInfo IDCardDetect(std::string imgPath, bool front)
{
    std::string out;
    readImageFile(imgPath.c_str(), out);
    std::string img_base64 = base64_encode(out.c_str(), (int)out.size());

    // set options
    std::map options;

    options["id_card_side"] = front ? "front" : "back";
    options["detect_photo"] = "true";

    Json::Value obj;
    IDCard idCardObj;
    IDCardInfo result;
    obj = idCardObj.request(img_base64, options);

    idCardObj.getResult(result);

    return result;
}

运行结果
正面测试图像

正面测试结果

反面测试图像

反面测试结果

 

 

收藏
点赞
0
个赞
TOP
切换版块