1.驾驶证识别

This commit is contained in:
jiangtd 2026-05-19 14:59:30 +08:00
parent fb2889a864
commit b6195a04a8
5 changed files with 309 additions and 158 deletions

View File

@ -0,0 +1,80 @@
package com.heyu.api.baidu.handle.traffic;
import com.heyu.api.baidu.BaiduBaseHandle;
import com.heyu.api.baidu.request.traffic.BDriverLicenseRequest;
import com.heyu.api.data.annotation.CustomPath;
import com.heyu.api.data.constants.ApiConstants;
import com.heyu.api.data.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* https://console.bce.baidu.com/support/#/api?product=AI&project=文字识别&parent=交通场景OCR&api=rest%2F2.0%2Focr%2Fv1%2Fdriving_license&method=post
* <p>
* <p>
* 驾驶证识别 - 百度OCR
*
* 支持对机动车驾驶证正页及副页所有字段进行结构化识别
* 驾驶证正页证号姓名性别国籍出生日期初次领证日期准驾车型有效起始日期失效日期住址发证单位 11个字段
* 驾驶证副页姓名记录证号档案编号 4个字段
* 同时支持识别交管12123 APP 发放的电子驾驶证正页包括证号姓名性别国籍出生日期初次领证日期准驾车型有效起始日期失效日期累积记分状态档案编号生成时间当前时间条形码下编号 15个字段
*/
@Component
@Slf4j
@CustomPath("driverLicense")
public class BDriverLicenseHandle extends BaiduBaseHandle<BDriverLicenseRequest, Map> {
@Override
public String getUri() {
return "/rest/2.0/ocr/v1/driving_license";
}
@Override
public String check(BDriverLicenseRequest request) {
if (checkNotTrueFalse(request.getDetectDirection())) {
return "detectDirection 必须传 " + ApiConstants.trueOrFalse
+ ",false默认值不检测朝向, true检测朝向";
}
if (checkNotFrontBack(request.getDrivingLicenseSide())) {
return "drivingLicenseSide 必须传 " + ApiConstants.frontOrback
+ ",front识别驾驶证正页、电子驾驶证正页, back识别驾驶证副页";
}
if (checkNotTrueFalse(request.getUnifiedValidPeriod())) {
return "unifiedValidPeriod 必须传 " + ApiConstants.trueOrFalse
+ ",false默认值不进行归一化处理, true归一化格式输出";
}
if (checkNotTrueFalse(request.getQualityWarn())) {
return "qualityWarn 必须传 " + ApiConstants.trueOrFalse
+ ",false不输出质量告警信息, true输出遮挡、不完整质量告警信息";
}
if (checkNotTrueFalse(request.getRiskWarn())) {
return "riskWarn 必须传 " + ApiConstants.trueOrFalse
+ ",false不输出风险告警信息, true开启输出驾驶正复印、翻拍、PS等告警信息";
}
return checkImageUri(request);
}
@Override
public String getContent(BDriverLicenseRequest request) {
StringBuffer sb = getImageContent(request);
if (StringUtils.isNotBlank(request.getDetectDirection())) {
sb.append("&detect_direction=").append(request.getDetectDirection());
}
if (StringUtils.isNotBlank(request.getDrivingLicenseSide())) {
sb.append("&driving_license_side=").append(request.getDrivingLicenseSide());
}
if (StringUtils.isNotBlank(request.getUnifiedValidPeriod())) {
sb.append("&unified_valid_period=").append(request.getUnifiedValidPeriod());
}
if (StringUtils.isNotBlank(request.getQualityWarn())) {
sb.append("&quality_warn=").append(request.getQualityWarn());
}
if (StringUtils.isNotBlank(request.getRiskWarn())) {
sb.append("&risk_warn=").append(request.getRiskWarn());
}
return sb.toString();
}
}

View File

@ -0,0 +1,52 @@
package com.heyu.api.baidu.request.traffic;
import com.heyu.api.baidu.request.BaiduImageUrlRequest;
import lombok.Data;
/**
* 驾驶证识别 - 百度OCR API请求
* https://console.bce.baidu.com/support/#/api?product=AI&project=文字识别&parent=交通场景OCR&api=rest%2F2.0%2Focr%2Fv1%2Fdriving_license&method=post
*
*
* 百度驾驶证识别/rest/2.0/ocr/v1/driving_license
*
* 驾驶证正页证号姓名性别国籍出生日期初次领证日期准驾车型有效起始日期失效日期住址发证单位 11个字段
* 电子驾驶证正页证号姓名性别国籍出生日期初次领证日期准驾车型有效起始日期失效日期累积记分状态档案编号生成时间当前时间条形码下编号 15个字段
* 驾驶证副页姓名记录证号档案编号 4个字段
*/
@Data
public class BDriverLicenseRequest extends BaiduImageUrlRequest {
/**
* - false默认值不检测朝向朝向是指输入图像是正常方向逆时针旋转90/180/270度
* - true检测朝向
*/
private String detectDirection = "false";
/**
* - front默认值识别驾驶证正页电子驾驶证正页
* - back识别驾驶证副页
*/
private String drivingLicenseSide = "front";
/**
* - false: 默认值不进行归一化处理
* - true: 归一化格式输出将驾驶证正页的有效起始日期+有效期限有效期限+归一化为有效起始日期+失效日期格式输出
*/
private String unifiedValidPeriod = "false";
/**
* 是否开启质量检测功能仅在驾驶证正页识别时生效
* - false默认值不输出质量告警信息
* - true 输出驾驶证遮挡不完整质量告警信息
*/
private String qualityWarn = "false";
/**
* 是否开启风险检测功能仅在驾驶证正页识别时生效
* - false默认值不输出风险告警信息
* - true开启输出驾驶证复印翻拍PS等告警信息
*/
private String riskWarn = "false";
}

View File

@ -1,14 +1,15 @@
package com.heyu.api.controller.car;
import com.aliyun.ocr20191230.models.RecognizeDriverLicenseResponse;
import com.aliyun.ocr20191230.models.RecognizeDriverLicenseResponseBody;
import com.heyu.api.alibaba.handle.common.text.ARecognizeDriverLicenseHandle;
import com.heyu.api.alibaba.request.common.text.ARecognizeDriverLicenseRequest;
import com.heyu.api.baidu.handle.traffic.BDriverLicenseHandle;
import com.heyu.api.baidu.request.traffic.BDriverLicenseRequest;
import com.heyu.api.controller.BaseController;
import com.heyu.api.data.annotation.EbAuthentication;
import com.heyu.api.data.constants.ApiConstants;
import com.heyu.api.data.utils.ApiR;
import com.heyu.api.data.utils.MapUtils;
import com.heyu.api.data.utils.R;
import com.heyu.api.data.utils.StringUtils;
import com.heyu.api.request.car.DriverLicenseRecognizeRequest;
import com.heyu.api.resp.car.RecognizeDriverLicenseBackResp;
import com.heyu.api.resp.car.RecognizeDriverLicenseFaceResp;
import lombok.extern.slf4j.Slf4j;
@ -18,96 +19,128 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
/**
* https://next.api.aliyun.com/api/ocr/2019-12-30/RecognizeDriverLicense?tab=DEMO&lang=JAVA
* <p>
* 驾驶证识别
* <p>
* RecognizeDriverLicense
*
* 驾驶证识别能力可以识别驾驶证首页和副页关键字段内容包括档案编号姓名有效期时长性别发证日期驾驶证号驾驶证准驾车型有效期开始时间地址 9 个关键字段信息
*
*
*
*
* https://console.bce.baidu.com/support/?_=1740219852952&timestamp=1740325062124#/api?product=AI&project=%E6%96%87%E5%AD%97%E8%AF%86%E5%88%AB&parent=%E4%BA%A4%E9%80%9A%E5%9C%BA%E6%99%AFOCR&api=rest%2F2.0%2Focr%2Fv1%2Fdriving_license&method=post
*
*
* 驾驶证识别
*
*
*
* 驾驶证识别百度OCR
*
* 支持对机动车驾驶证正页及副页所有字段进行结构化识别
* 驾驶证正页证号姓名性别国籍出生日期初次领证日期准驾车型有效起始日期失效日期住址发证单位 11个字段
* 驾驶证副页姓名记录证号档案编号 4个字段
* 同时支持识别交管12123 APP 发放的电子驾驶证正页包括证号姓名性别国籍出生日期初次领证日期准驾车型有效起始日期失效日期累积记分状态档案编号生成时间当前时间条形码下编号 15个字段
*
* 百度文档https://console.bce.baidu.com/support/#/api?product=AI&project=文字识别&parent=交通场景OCR&api=rest%2F2.0%2Focr%2Fv1%2Fdriving_license&method=post
*/
@Slf4j
@RestController
@RequestMapping("/driver/license")
@SuppressWarnings("unchecked")
public class RecognizeDriverLicenseController extends BaseController {
@Autowired
private ARecognizeDriverLicenseHandle aRecognizeDriverLicenseHandle;
private BDriverLicenseHandle bDriverLicenseHandle;
// http://localhost:8888/driver/license/recognize?side=face&imageBase64=/9j/4AAQSkZJRgABA
@EbAuthentication(tencent = ApiConstants.TENCENT_AUTH)
@PostMapping("/recognize")
public R recognize(@RequestBody ARecognizeDriverLicenseRequest request) {
if (!hasImage(request)) {
return R.error("imageUrl和imageBase64不能同时为空");
public R recognize(@RequestBody DriverLicenseRecognizeRequest request) {
long start = System.currentTimeMillis();
if (request == null) {
return R.error("请求参数不能为空");
}
if (isBlank(request.getImageBase64()) && isBlank(request.getImageUrl())) {
return R.error("imageBase64和imageUrl不能同时为空");
}
log.info("驾驶证识别-参数校验耗时:{}ms", System.currentTimeMillis() - start);
long t1 = System.currentTimeMillis();
BDriverLicenseRequest bRequest = toBaiduRequest(request);
log.info("驾驶证识别-构建百度请求耗时:{}ms", System.currentTimeMillis() - t1);
long t2 = System.currentTimeMillis();
ApiR<Map> apiR = bDriverLicenseHandle.handle(bRequest);
long baiduCost = System.currentTimeMillis() - t2;
log.info("驾驶证识别-调用百度OCR接口耗时:{}ms", baiduCost);
if (apiR == null || !apiR.isSuccess() || apiR.getData() == null) {
return R.error(thirdError(apiR));
}
ApiR<RecognizeDriverLicenseResponse> aR = aRecognizeDriverLicenseHandle.handle(request);
if (!isValidAliResponse(aR)) {
return R.error(thirdError(aR));
}
RecognizeDriverLicenseResponseBody.RecognizeDriverLicenseResponseBodyData data =
aR.getData().getBody().getData();
long t3 = System.currentTimeMillis();
Map<String, Object> data = apiR.getData();
Object resp;
if (ApiConstants.face.equals(request.getSide())) {
if (data.getFaceResult() == null) {
return R.error("未识别到驾驶证正页信息");
resp = toFaceResp(data);
} else {
resp = toBackResp(data);
}
log.info("驾驶证识别-结果映射耗时:{}ms, side:{}", System.currentTimeMillis() - t3, request.getSide());
log.info("驾驶证识别-总耗时:{}ms, 其中第三方(百度OCR):{}ms", System.currentTimeMillis() - start, baiduCost);
return R.ok().setData(resp);
}
private BDriverLicenseRequest toBaiduRequest(DriverLicenseRecognizeRequest request) {
BDriverLicenseRequest bRequest = new BDriverLicenseRequest();
bRequest.setImageBase64(request.getImageBase64());
bRequest.setImageUrl(request.getImageUrl());
// "face" 映射为百度API的 "front"
if (StringUtils.isNotBlank(request.getSide())) {
if (ApiConstants.face.equals(request.getSide())) {
bRequest.setDrivingLicenseSide("front");
} else {
bRequest.setDrivingLicenseSide(request.getSide());
}
return R.ok().setData(toFaceResp(data.getFaceResult()));
}
if (data.getBackResult() == null) {
return R.error("未识别到驾驶证副页信息");
return bRequest;
}
private RecognizeDriverLicenseFaceResp toFaceResp(Map<String, Object> data) {
RecognizeDriverLicenseFaceResp resp = new RecognizeDriverLicenseFaceResp();
// 驾驶证正页 11个字段
resp.setLicenseNumber(MapUtils.getByExpr(data, "words_result.证号.words"));
resp.setName(MapUtils.getByExpr(data, "words_result.姓名.words"));
resp.setGender(MapUtils.getByExpr(data, "words_result.性别.words"));
resp.setNationality(MapUtils.getByExpr(data, "words_result.国籍.words"));
resp.setBirthDate(MapUtils.getByExpr(data, "words_result.出生日期.words"));
resp.setIssueDate(MapUtils.getByExpr(data, "words_result.初次领证日期.words"));
resp.setVehicleType(MapUtils.getByExpr(data, "words_result.准驾车型.words"));
// 电子/归一化模式返回 "有效起始日期"非归一化返回 "有效期限"
String startDate = MapUtils.getByExpr(data, "words_result.有效起始日期.words");
if (startDate == null) {
startDate = MapUtils.getByExpr(data, "words_result.有效期限.words");
}
return R.ok().setData(toBackResp(data.getBackResult()));
resp.setStartDate(startDate);
// 电子/归一化模式返回 "失效日期"非归一化返回 ""
String endDate = MapUtils.getByExpr(data, "words_result.失效日期.words");
if (endDate == null) {
endDate = MapUtils.getByExpr(data, "words_result.至.words");
}
resp.setEndDate(endDate);
resp.setAddress(MapUtils.getByExpr(data, "words_result.住址.words"));
resp.setIssueUnit(MapUtils.getByExpr(data, "words_result.发证单位.words"));
// 电子驾驶证正页 额外字段
resp.setAccumulatedPoints(MapUtils.getByExpr(data, "words_result.累积记分.words"));
resp.setStatus(MapUtils.getByExpr(data, "words_result.状态.words"));
resp.setArchiveNumber(MapUtils.getByExpr(data, "words_result.档案编号.words"));
resp.setGenerateTime(MapUtils.getByExpr(data, "words_result.生成时间.words"));
resp.setCurrentTime(MapUtils.getByExpr(data, "words_result.当前时间.words"));
resp.setBarcodeNumber(MapUtils.getByExpr(data, "words_result.条形码下编号.words"));
return resp;
}
private boolean isValidAliResponse(ApiR<RecognizeDriverLicenseResponse> apiR) {
return apiR != null
&& apiR.isSuccess()
&& apiR.getData() != null
&& isSuccessStatusCode(apiR.getData().getStatusCode())
&& apiR.getData().getBody() != null
&& apiR.getData().getBody().getData() != null;
private RecognizeDriverLicenseBackResp toBackResp(Map<String, Object> data) {
RecognizeDriverLicenseBackResp resp = new RecognizeDriverLicenseBackResp();
resp.setName(MapUtils.getByExpr(data, "words_result.姓名.words"));
resp.setRecord(MapUtils.getByExpr(data, "words_result.记录.words"));
resp.setCardNumber(MapUtils.getByExpr(data, "words_result.证号.words"));
resp.setArchiveNumber(MapUtils.getByExpr(data, "words_result.档案编号.words"));
return resp;
}
private RecognizeDriverLicenseFaceResp toFaceResp(
RecognizeDriverLicenseResponseBody.RecognizeDriverLicenseResponseBodyDataFaceResult faceResult) {
RecognizeDriverLicenseFaceResp faceResp = new RecognizeDriverLicenseFaceResp();
faceResp.setVehicleType(faceResult.getVehicleType());
faceResp.setIssueDate(faceResult.getIssueDate());
faceResp.setEndDate(faceResult.getEndDate());
faceResp.setGender(faceResult.getGender());
faceResp.setAddress(faceResult.getAddress());
faceResp.setStartDate(faceResult.getStartDate());
faceResp.setLicenseNumber(faceResult.getLicenseNumber());
faceResp.setName(faceResult.getName());
faceResp.setIssueUnit(faceResult.getIssueUnit());
faceResp.setNationality(faceResult.getNationality());
faceResp.setBirthDate(faceResult.getBirthDate());
return faceResp;
}
private RecognizeDriverLicenseBackResp toBackResp(
RecognizeDriverLicenseResponseBody.RecognizeDriverLicenseResponseBodyDataBackResult backResult) {
RecognizeDriverLicenseBackResp backResp = new RecognizeDriverLicenseBackResp();
backResp.setArchiveNumber(backResult.getArchiveNumber());
backResp.setName(backResult.getName());
backResp.setCardNumber(backResult.getCardNumber());
backResp.setRecord(backResult.getRecord());
return backResp;
}
}

View File

@ -0,0 +1,32 @@
package com.heyu.api.request.car;
import lombok.Data;
/**
* 驾驶证识别请求参数
*
* 对外提供简洁接口支持识别机动车驾驶证正页及副页所有字段
*/
@Data
public class DriverLicenseRecognizeRequest {
/**
* 图像数据base64编码后进行urlencode要求base64编码和urlencode后大小不超过4M
* 支持jpg/jpeg/png/bmp格式
* 和url二选一
*/
private String imageBase64;
/**
* 图片完整URLURL长度不超过1024字节
* 和imageBase64二选一
*/
private String imageUrl;
/**
* face识别驾驶证正页电子驾驶证正页默认
* back识别驾驶证副页
*/
private String side = "face";
}

View File

@ -2,111 +2,65 @@ package com.heyu.api.resp.car;
import lombok.Data;
/***
* https://next.api.aliyun.com/api/ocr/2019-12-30/RecognizeDriverLicense?tab=DOC&lang=JAVA
/**
* 驾驶证识别 - 正页/电子驾驶证正页 响应
*
* 支持识别驾驶证正页11字段电子驾驶证正页15字段
*/
@Data
public class RecognizeDriverLicenseFaceResp {
/***
* 驾驶证准驾车型
*
* 示例值:
* C1
*/
private String vehicleType;
/***
* 初次发证日期格式YYYYMMDD例如 19800101 1980 01 01
*
* 示例值:
* 20130208
*
*
*/
private String issueDate;
/***
*
*/
private String endDate;
/***
* 性别
*
* 示例值:
* 男der
*/
private String gender;
/***
* 地址
*
* 示例值:
* 江苏省徐州市铜山区棠张镇田河村1队129号
*/
private String address;
/***
*
*/
private String startDate;
/***
* 证号
*
* 示例值:
* 210288898898898888
*/
/** 证号 */
private String licenseNumber;
/***
* 姓名
*
* 示例值:
* 张三
*/
/** 姓名 */
private String name;
/** 性别 */
private String gender;
/***
* 发证单位
*
* 示例值:
* 江苏省徐州市公安局交通巡逻警察支队
*/
private String issueUnit;
/***
* 国籍
*
* 示例值:
* 中国
*/
/** 国籍 */
private String nationality;
/***
* 出生日期
*
* 示例值:
* 1992-05-20
*/
/** 出生日期格式YYYYMMDD */
private String birthDate;
/** 初次领证日期格式YYYYMMDD */
private String issueDate;
/** 准驾车型,如 C1 */
private String vehicleType;
/** 有效起始日期格式YYYYMMDD */
private String startDate;
/** 失效日期格式YYYYMMDD */
private String endDate;
/** 住址 */
private String address;
/** 发证单位 */
private String issueUnit;
// ======== 电子驾驶证正页 额外字段 ========
/** 累积记分,如 "0分",仅电子驾驶证正页返回 */
private String accumulatedPoints;
/** 状态,如 "正常",仅电子驾驶证正页返回 */
private String status;
/** 档案编号,仅电子驾驶证正页返回 */
private String archiveNumber;
/** 生成时间,如 "2021年09月04日",仅电子驾驶证正页返回 */
private String generateTime;
/** 当前时间,如 "2022年04月16日14:09:39",仅电子驾驶证正页返回 */
private String currentTime;
/** 条形码下编号,如 "*4360028416376*",仅电子驾驶证正页返回 */
private String barcodeNumber;
}