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