功能介绍
支持对中国大陆护照个人资料页所有15个字段进行结构化识别,包括国家码、护照号、姓名、姓名拼音、性别、出生地点、出生日期、签发地点、签发日期、有效期、签发机关、护照类型、国籍、MRZCode1、MRZCode2。
应用场景
- 境外旅游:使用护照识别技术,实现对用户护照信息的结构化识别和录入,可应用于境外旅游产品预订、酒店入住登记等场景,满足护照信息自动录入的需求,有效提升信息录入效率,降低用户输入成本,提升用户使用体验
- 留学信息登记:使用护照识别技术,实现对用户护照信息的结构化识别和录入,可应用于留学机构信息收集或个人留学手续办理等场景,满足护照信息自动录入的需求,有效提升信息录入效率,降低用户输入成本,提升用户使用体验
特色优势
- 字段齐全:支持对护照所有字段的结构化识别,能够实现不同业务场景下对任意字段信息的提取,满足各类相关需求
- 准确率高:依托百度优秀的图像处理技术,对护照反光、底纹等情况进行专项优化,关键字段识别准确率超过95%
- 服务稳定:依托百度云技术实力,提供高可靠性、弹性可伸缩、高并发承载的文字识别服务,服务可用性高达99.99%
接口描述
支持对中国大陆护照个人资料页所有15个字段进行结构化识别,包括国家码、护照号、姓名、姓名拼音、性别、出生地点、出生日期、签发地点、签发日期、有效期、签发机关、护照类型、国籍、MRZCode1、MRZCode2。
请求说明
- HTTP 方法: POST
- 请求 URL: https://aip.baidubce.com/rest/2.0/ocr/v1/passport
- URL参数: access_token
- Header 参数: Content-Type = application/x-www-form-urlencoded
- Body 参数:见下表
返回说明
返回参数如下表:
返回示例如下:
{
"log_id": 7377468409496932872,
"words_result_num": 14,
"words_result": {
"国家码": {
"location": {
"width": 59,
"top": 200,
"left": 762,
"height": 26
},
"words": "CHN"
},
"护照签发地点": {
"location": {
"width": 236,
"top": 505,
"left": 558,
"height": 43
},
"words": "山东/SHANDONG"
},
"MRZCode2": {
"location": {
"width": 1252,
"top": 797,
"left": 145,
"height": 88
},
"words": "E898657303CHNSDMO7O2<<<<<<<<<<<<<"
},
"有效期至": {
"location": {
"width": 287,
"top": 528,
"left": 955,
"height": 46
},
"words": "20261004"
},
"签发机关": {
"location": {
"width": 271,
"top": 583,
"left": 552,
"height": 42
},
"words": "公安部出入境管理局"
},
"MRZCode1": {
"location": {
"width": 1201,
"top": 781,
"left": 162,
"height": 45
},
"words": "PONSUN<
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/)
为了方便,首先根据返回参数定义了一个结构体,该结构体包括了返回参数中的参数,如下:
struct PassportInfo
{
uint64_t logID{};
uint32_t resultNumber{};
std::map wordsResult{};
void print()
{
std::cout << "log id: " << logID << '\n';
std::cout << "words result number: " << resultNumber << '\n';
for (auto& [name, res] : wordsResult)
{
std::cout << "\t" << UTF8ToGB(name.c_str()) << ": ";
res.print();
}
}
void draw(cv::Mat& img)
{
for (auto& [name, res] : wordsResult)
{
res.draw(img);
}
}
};
然后定义了一个类来调用接口并获取结果
class Passport
{
public:
Passport();
~Passport();
Json::Value request(std::string imgBase64, std::map& options);
void getResult(PassportInfo& 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 将请求的结果进行解析为自定义的结构体数据类型。以便用于后序的打印和绘图等。
完整代码如下
Passport.h 代码如下: screenshot/functions/Passport.h(https://github.com/busyboxs/screenshot/blob/master/functions/Passport.h)
#pragma once
#include
#include
#include
#include
#include
#include "util.h"
#include "customVariables.h"
struct PassportInfo
{
uint64_t logID{};
uint32_t resultNumber{};
std::map wordsResult{};
void print()
{
std::cout << "log id: " << logID << '\n';
std::cout << "words result number: " << resultNumber << '\n';
for (auto& [name, res] : wordsResult)
{
std::cout << "\t" << UTF8ToGB(name.c_str()) << ": ";
res.print();
}
}
void draw(cv::Mat& img)
{
for (auto& [name, res] : wordsResult)
{
res.draw(img);
}
}
};
class Passport
{
public:
Passport();
~Passport();
Json::Value request(std::string imgBase64, std::map& options);
void getResult(PassportInfo& result);
private:
Json::Value m_obj;
std::string m_url;
// file to save token key
std::string m_filename;
};
void PassportTest();
PassportInfo PassPortDetect(std::string imgPath);
Passport.cpp 代码如下: screenshot/functions/Passport.cpp(https://github.com/busyboxs/screenshot/blob/master/functions/Passport.cpp)
#include "Passport.h"
Passport::Passport()
{
m_url = "https://aip.baidubce.com/rest/2.0/ocr/v1/passport";
m_filename = "tokenKey";
}
Passport::~Passport()
{
}
Json::Value Passport::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 Passport::getResult(PassportInfo& 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.resultNumber = m_obj["words_result_num"].asUInt();
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 PassportTest()
{
std::string img_file = "./images/passport_test.png";
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;
Json::Value obj;
Passport passportObj;
PassportInfo result;
obj = passportObj.request(img_base64, options);
passportObj.getResult(result);
result.print();
cv::Mat img = cv::imread("./images/passport_test.png");
result.draw(img);
cv::namedWindow("passport", cv::WINDOW_NORMAL);
cv::imshow("passport", img);
cv::imwrite("./images/passport.jpg", img);
cv::waitKey();
}
PassportInfo PassPortDetect(std::string imgPath)
{
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;
Json::Value obj;
Passport passportObj;
PassportInfo result;
obj = passportObj.request(img_base64, options);
passportObj.getResult(result);
return result;
}
util.h 代码如下: screenshot/functions/util.h(https://github.com/busyboxs/screenshot/blob/master/functions/util.h)
#pragma once
#include
#include
#include
#include
#include
#include
#include
#include
#include "base64.h"
#include "customVariables.h"
size_t writeCallback(void* ptr, size_t size, size_t nmemb, void* userdata);
std::string getTokenKey();
int readImageFile(const char* filename, std::string& out);
void imgToBase64(std::string& imgFile, std::string& imgBase64);
int writeFile(const std::string& fileString, const std::string& str);
int readFile(const std::string& fileString, std::string& str);
std::string UTF8ToGB(const char* str);
void mergeHttpPostBody(std::string& body, std::string imgBase64, std::map& options);
void getHttpPostUrl(std::string& url, std::string& filename, std::string& token);
int httpPostRequest(std::string& url, std::string& body, std::string& response);
void generateJson(std::string& response, Json::Value& obj);
void checkErrorWithExit(Json::Value& obj);
void getLocation(Json::Value& loc, Location& location);
void getResultPart(Json::Value& res, ResultPart& resultPart);
void getWordsOnlyPart(Json::Value& res, WordsOnlyPart& wordsOnlyPart);
util.cpp 代码如下: screenshot/functions/util.cpp(https://github.com/busyboxs/screenshot/blob/master/functions/util.cpp)
使用时请替换 getTokenKey 中的 apikey 和 secritkey。
#include "util.h"
// callback function for curl
size_t writeCallback(void* ptr, size_t size, size_t nmemb, void* userdata)
{
std::string* str = dynamic_cast((std::string*)userdata);
str->append((char*)ptr, size * nmemb);
return size * nmemb;
}
// get access token from server by get method
std::string getTokenKey() {
std::string url = "https://aip.baidubce.com/oauth/2.0/token";
std::string apikey = "nXb3wUW1xSPav1vMN3BMetAl";
std::string secritkey = "DSlxiE0W1I3Sp3hvhCEcCg7IWE0LBSkd";
std::map params;
std::string response;
params["grant_type"] = "client_credentials";
params["client_id"] = apikey;
params["client_secret"] = secritkey;
// append url with parameters
for (auto it = params.begin(); it != params.end(); ++it) {
url += (it == params.begin() ? "?" : "&") + it->first + "=" + it->second;
}
CURL* curl = curl_easy_init();
struct curl_slist* slist = NULL;
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback); // set callback function
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response); // set var to receive return info from callback function
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, true);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false);
curl_easy_setopt(curl, CURLOPT_VERBOSE, false);
int status_code = curl_easy_perform(curl);
curl_easy_cleanup(curl);
curl_slist_free_all(slist);
Json::Value obj;
if (status_code != CURLcode::CURLE_OK) {
obj["curl_error_code"] = status_code;
return obj.toStyledString();
}
// parse json string
JSONCPP_STRING error;
Json::CharReaderBuilder builder;
const std::unique_ptr reader(builder.newCharReader());
reader->parse(response.data(), response.data() + response.size(), &obj, &error);
std::string access_token = obj["access_token"].asString();
return access_token;
}
// read image file [https://stackoverflow.com/questions/9612121/how-to-read-image-files-and-store-it-in-memorystdstring-in-c]
int readImageFile(const char* filename, std::string& out) {
std::ifstream in(filename, std::ios::in | std::ios::binary);
if (in) {
std::ostringstream oss;
oss << in.rdbuf();
out.assign(oss.str());
return 0;
}
else {
std::cerr << "Can't open image!" << std::endl;
return -1;
}
}
void imgToBase64(std::string& imgFile, std::string& imgBase64) {
// read image and encode to base64
std::string out;
readImageFile(imgFile.c_str(), out);
imgBase64 = base64_encode(out.c_str(), (int)out.size());
}
int writeFile(const std::string& fileString, const std::string& str) {
std::ofstream out(fileString, std::ios::binary);
if (out.is_open()) {
out << str;
out.close();
}
return 0;
}
int readFile(const std::string& fileString, std::string& str) {
std::ifstream in(fileString);
if (!in.is_open()) {
str = "";
return -1;
}
char buffer[256];
while (!in.eof()) {
in.getline(buffer, sizeof(buffer));
}
str = buffer;
return 0;
}
std::string UTF8ToGB(const char* str)
{
std::string result;
WCHAR* strSrc;
LPSTR szRes;
int i = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
strSrc = new WCHAR[i + 1];
MultiByteToWideChar(CP_UTF8, 0, str, -1, strSrc, i);
i = WideCharToMultiByte(CP_ACP, 0, strSrc, -1, NULL, 0, NULL, NULL);
szRes = new CHAR[i + 1];
WideCharToMultiByte(CP_ACP, 0, strSrc, -1, szRes, i, NULL, NULL);
result = szRes;
delete[]strSrc;
delete[]szRes;
return result;
}
void mergeHttpPostBody(std::string& body, std::string imgBase64, std::map& options) {
body = "image=" + std::string(curl_escape(imgBase64.c_str(), int(imgBase64.size()))) + "&";
// append body with options
for (auto it = options.begin(); it != options.end(); ++it) {
body += std::string(curl_escape(it->first.c_str(), (int)it->first.size()))
+ "=" + std::string(curl_escape(it->second.c_str(), (int)it->second.size())) + "&";
}
}
// first get token from file (if exist), then add token to url
void getHttpPostUrl(std::string& url, std::string& filename, std::string& token) {
// if token file is not exist, a new one should be create
if (readFile(filename, token) < 0) {
token = getTokenKey();
writeFile(filename, token);
}
url = url + "?access_token=" + token;
}
int httpPostRequest(std::string& url, std::string& body, std::string& response) {
struct curl_slist* slist = NULL;
CURL* curl = curl_easy_init();
// set headers, actually this is used by default
std::string headers = "Content-Type:application/x-www-form-urlencoded";
slist = curl_slist_append(slist, headers.c_str());
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
curl_easy_setopt(curl, CURLOPT_POST, true);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.data());
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, body.size());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&response);
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, true);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false);
curl_easy_setopt(curl, CURLOPT_VERBOSE, false);
int status_code = curl_easy_perform(curl);
curl_easy_cleanup(curl);
curl_slist_free_all(slist);
return status_code;
}
void generateJson(std::string& response, Json::Value& obj) {
// parse json string
JSONCPP_STRING error;
Json::CharReaderBuilder builder;
const std::unique_ptr reader(builder.newCharReader());
reader->parse(response.data(), response.data() + response.size(), &obj, &error);
}
void checkErrorWithExit(Json::Value& obj)
{
if (obj.get("error_code", "null")) {
std::cerr << obj.get("error_code", "null") << " : " << obj.get("error_msg", "null") << std::endl;
system("pause");
exit(EXIT_FAILURE);
}
}
void getLocation(Json::Value& loc, Location& location)
{
location.left = loc["left"].asInt();
location.top = loc["top"].asInt();
location.width = loc["width"].asInt();
location.height = loc["height"].asInt();
}
void getResultPart(Json::Value& res, ResultPart& resultPart)
{
getLocation(res["location"], resultPart.location);
resultPart.words = res["words"].asString().c_str();
}
void getWordsOnlyPart(Json::Value& res, WordsOnlyPart& wordsOnlyPart)
{
wordsOnlyPart.words = res["words"].asString().c_str();
}
运行结果
测试图像
测试结果