功能介绍
结构化识别二代居民身份证正反面所有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; }
运行结果
正面测试图像
正面测试结果
反面测试图像
反面测试结果