当前位置: 首页 > news >正文

百度OCR:证件识别

目录

一、编写目的

二、准备工作

2.1 OCR密钥

三、代码实现

3.1 配置文件

3.2 请求接收封装

3.3 请求响应封装

 3.4 服务类参数初始化

3.5 服务类实现

3.6 解析结果 

3.7 定义Web接口

四 测试效果

五、总结


欢迎来到盹猫🐱的博客

本篇文章主要介绍了

[百度OCR:证件识别]
❤博主广交技术好友,喜欢文章的可以关注一下❤

一、编写目的

        本篇文章是记录SpringBoot调用百度OCR识别身份证和银行卡信息服务接口的实现步骤,通过测试,识别速度快,识别信息准确。该功能可以用在方便用户认证、注册、用户信息更新等方面,为方便日后使用和查询,在这里对实现流程进行记录,希望可以帮到有需要的开发者。

二、准备工作

2.1 OCR密钥

        可以在OCR文字识别_免费试用_图片转文字-百度AI开放平台 进行账号的注册,如果已经有账号可以直接登录。点击立即使用进入百度控制台,开通[身份证识别]和[银行卡识别]两个功能:

这里有1000次的免费使用,当然付费该功能也很便宜。

        在应用列表功能中,单击创建一个应用,创建一个包含OCR识别功能的应用,当然可以选取全部功能,这样所有功能就都可以使用了。

        在应用列表中复制已创建应用的AppID、APIKey、Secret Key,在后续的application.yml配置文件中需要用到。

 

三、代码实现

3.1 配置文件

        创建一个基础的SpringBoot项目,并在配置文件中将已申请的AppID、APIKey、Secret Key 添加到application.yml文件(没有则在resources目录下进行创建)中,由于百度是通过access_token进行接口调用,在这里将access_token的获取地址和OCR功能请求地址一并配置,具体内容如下:

baidu:app_id: APPIDapi_key: API_KEYsecret_key: SECRET_KEYaccess_token_url: https://aip.baidubce.com/oauth/2.0/tokenocr:base_url: https://aip.baidubce.com/rest/2.0/ocr/v1

3.2 请求接收封装

        要实现的功能是用户可以对请求的识别的实体卡进行指定,同时可以指定识别正面和反面,所以这边先定期请求数据的接收(也就是接收POST的JSON数据),实体内容如下:

package com.uav.models;import com.uav.common.validator.group.UpdateGroup;
import lombok.Data;import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;@Data
public class BaiduOcrRequestDTO {/**正反面*/@Pattern(regexp = "^(front|back)$",message = "side 只能是 'front' 或 'back'",groups = UpdateGroup.class) // 可选:分组校验private String side;@NotEmpty(message = "图片url不能为空")private String url;@Pattern(regexp = "^(idcard|bankcard)$",message = "type 只能是 'idcard' 或 'bankcard'",groups = UpdateGroup.class)private String type;
}

3.3 请求响应封装

        返回的数据可能是身份证,也可能是银行卡,所以这边需要定义两个请求响应的封装,内容如下:

银行卡响应

package com.uav.models;import lombok.Data;@Data
public class BankCardDTO {private String validDate;         // 有效期private String bankCardNumber;    // 银行卡号private String bankName;          // 银行名称private int bankCardType;         // 卡类型private String holderName;        // 持卡人姓名
}

身份证响应

package com.uav.models;import lombok.Data;@Data
public class OcrIdCardDTO {private String name;          // 姓名private String nation;        // 民族private String address;       // 住址private String idNumber;      // 公民身份号码private String birthDate;     // 出生日期private String gender;        // 性别// 反面信息private String expiryDate;    // 失效日期private String issuingAuthority; // 签发机关private String issueDate;     // 签发日期
}

 3.4 服务类参数初始化

        因为请求有两个类型,先进行识别类型枚举RecognizeType的定义,它可以在代码编写过程中减少硬编码,增加可维护性,内容如下:

    /*** 支持的识别类型枚举*/public enum RecognizeType {IDCARD("idcard"),BANKCARD("bankcard");private final String type;RecognizeType(String type) {this.type = type;}public String getType() {return type;}/*** 根据类型字符串获取枚举值** @param type 类型字符串* @return 对应的枚举值,如果不存在则抛出异常*/public static RecognizeType fromString(String type) {for (RecognizeType recognizeType : values()) {if (recognizeType.getType().equalsIgnoreCase(type)) {return recognizeType;}}throw new IllegalArgumentException("不支持的识别类型: " + type);}}

        同样的,我们需要用到Http请求,这里使用OKhttpClient进行请求,同时将之前定义的参数通过@Value进行注入,内容如下:

@Value("${baidu.api_key}")private String API_KEY;@Value("${baidu.secret_key}")private String SECRET_KEY;@Value("${baidu.access_token_url}")private String ACCESS_TOKEN_URL;@Value("${baidu.ocr.base_url}")private String ORC_BASE_URL;// 使用 final 确保线程安全,并在 @PostConstruct 中初始化private OkHttpClient httpClient;/*** 初始化 OkHttpClient 实例*/@PostConstructpublic void init() {httpClient = new OkHttpClient.Builder().readTimeout(300, TimeUnit.SECONDS).build();}

3.5 服务类实现

        在服务类的实现时,需要先获取百度的access_token然后进行OCR接口的请求,所以这里先定义一个获取access_token的方法,内容如下:

    private String getAccessToken() throws IOException {RequestBody body = RequestBody.create(MediaType.parse("application/x-www-form-urlencoded"),"grant_type=client_credentials&client_id=" + API_KEY + "&client_secret=" + SECRET_KEY);Request request = new Request.Builder().url(ACCESS_TOKEN_URL).post(body).addHeader("Content-Type", "application/x-www-form-urlencoded").build();try (Response response = httpClient.newCall(request).execute()) {if (!response.isSuccessful()) {log.error("获取访问令牌失败,状态码: {}, 响应: {}", response.code(), response.body() != null ? response.body().string() : "null");throw new SysException("获取访问令牌失败,状态码: " + response.code());}String responseBody = Objects.requireNonNull(response.body()).string();JSONObject jsonResponse = JSON.parseObject(responseBody);if (!jsonResponse.containsKey("access_token")) {log.error("访问令牌响应中缺少 access_token 字段: {}", responseBody);throw new SysException("访问令牌响应格式错误");}return jsonResponse.getString("access_token");} catch (Exception e) {log.error("获取访问令牌过程中发生错误", e);throw new SysException("获取访问令牌失败,请重试!", e);}}

        然后开始编写识别图片方法,该方法的接收参数为一个请求封装的参数,即上面定义的BaiduOcrRequestDTO,内容如下:

    @Overridepublic Object recognizeImage(BaiduOcrRequestDTO requestDTO) throws IOException {try {// 获取访问令牌String accessToken = getAccessToken();// 构建请求 URL 和 BodyString requestUrl = buildRequestUrl(requestDTO.getType(), accessToken);String bodyContent = buildRequestBody(requestDTO);RequestBody body = RequestBody.create(MediaType.parse("application/x-www-form-urlencoded"), bodyContent);// 构建请求Request request = new Request.Builder().url(requestUrl).post(body).addHeader("Content-Type", "application/x-www-form-urlencoded").addHeader("Accept", "application/json").build();// 发送请求并获取响应Response response = httpClient.newCall(request).execute();if (!response.isSuccessful()) {log.error("OCR 请求失败,状态码: {}, 响应: {}", response.code(), response.body() != null ? response.body().string() : "null");throw new SysException("OCR 请求失败,状态码: " + response.code());}String result = Objects.requireNonNull(response.body()).string();log.debug("OCR 响应结果: {}", result);// 解析结果return new OcrParser().parseCardInfo(result, requestDTO.getType(), requestDTO.getSide());} catch (Exception e) {log.error("OCR 识别过程中发生错误,请求数据: {}", requestDTO, e);throw new SysException("OCR 识别失败,请重试!", e);}}

3.6 解析结果 

        百度OCR返回的为json字符串,但是其中有很多并不需要的信息,这样返回到前端并不利用阅读和解析,同时占用大量带宽,所以这里使用自定义的OcrParser 工具类对响应的数据进行解析,将其转换为上面提到的请求响应参数格式,内容如下:

package com.uav.common.util;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.uav.common.exception.SysException;
import com.uav.models.BankCardDTO;
import com.uav.models.OcrIdCardDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;/*** OCR 解析工具类,用于解析身份证和银行卡信息。*/
@Slf4j
public class OcrParser {/*** 识别类型枚举*/public enum CardType {IDCARD("idcard"),BANKCARD("bankcard");private final String type;CardType(String type) {this.type = type;}public String getType() {return type;}/*** 根据类型字符串获取枚举值** @param type 类型字符串* @return 对应的枚举值,如果不存在则抛出异常*/public static CardType fromString(String type) {for (CardType cardType : values()) {if (cardType.getType().equalsIgnoreCase(type)) {return cardType;}}throw new IllegalArgumentException("不支持的识别类型: " + type);}}/*** 解析卡片信息** @param cardJson JSON 字符串* @param type     识别类型("idcard" 或 "bankcard")* @param side     仅对身份证有效,"front" 表示正面,其他表示反面* @return 解析后的 DTO 对象* @throws SysException 如果解析失败*/public Object parseCardInfo(String cardJson, String type, String side) {try {JSONObject root = JSON.parseObject(cardJson);CardType cardType = CardType.fromString(type);switch (cardType) {case IDCARD:return parseIdCardInfo(root, side);case BANKCARD:return parseBankCardInfo(root);default:throw new SysException("不支持的识别类型: " + type);}} catch (IllegalArgumentException e) {throw new SysException("不支持的识别类型: " + type, e);} catch (Exception e) {log.error("解析卡片信息失败,JSON: {}, 类型: {}, 方向: {}", cardJson, type, side, e);throw new SysException("识别解析格式错误,请重新尝试!", e);}}/*** 解析身份证信息** @param root JSON 根对象* @param side "front" 表示正面,其他表示反面* @return OcrIdCardDTO 对象* @throws SysException 如果解析失败*/private OcrIdCardDTO parseIdCardInfo(JSONObject root, String side) throws SysException {JSONObject wordsResult = root.getJSONObject("words_result");if (wordsResult == null) {throw new SysException("JSON 中缺少 'words_result' 字段");}OcrIdCardDTO idCardInfo = new OcrIdCardDTO();if (StringUtils.equalsIgnoreCase(side, "front")) {parseFrontSide(wordsResult, idCardInfo);} else {parseBackSide(wordsResult, idCardInfo);}return idCardInfo;}/*** 解析身份证正面信息** @param wordsResult JSON 中的 words_result 对象* @param idCardInfo  目标 DTO 对象* @throws SysException 如果解析失败*/private void parseFrontSide(JSONObject wordsResult, OcrIdCardDTO idCardInfo) throws SysException {try {idCardInfo.setName(getWord(wordsResult, "姓名"));idCardInfo.setNation(getWord(wordsResult, "民族"));idCardInfo.setAddress(getWord(wordsResult, "住址"));idCardInfo.setIdNumber(getWord(wordsResult, "公民身份号码"));idCardInfo.setBirthDate(getWord(wordsResult, "出生"));idCardInfo.setGender(getWord(wordsResult, "性别"));} catch (Exception e) {throw new SysException("解析身份证正面信息失败", e);}}/*** 解析身份证反面信息** @param wordsResult JSON 中的 words_result 对象* @param idCardInfo  目标 DTO 对象* @throws SysException 如果解析失败*/private void parseBackSide(JSONObject wordsResult, OcrIdCardDTO idCardInfo) throws SysException {try {idCardInfo.setExpiryDate(getWord(wordsResult, "失效日期"));idCardInfo.setIssuingAuthority(getWord(wordsResult, "签发机关"));idCardInfo.setIssueDate(getWord(wordsResult, "签发日期"));} catch (Exception e) {throw new SysException("解析身份证反面信息失败", e);}}/*** 安全地从 words_result 中获取指定字段的 words 值** @param wordsResult JSON 中的 words_result 对象* @param fieldName   字段名称* @return 对应的 words 值* @throws SysException 如果字段不存在或解析失败*/private String getWord(JSONObject wordsResult, String fieldName) throws SysException {JSONObject field = wordsResult.getJSONObject(fieldName);if (field == null) {throw new SysException("缺少字段: " + fieldName);}String words = field.getString("words");if (StringUtils.isBlank(words)) {throw new SysException("字段 '" + fieldName + "' 的 words 值为空");}return words;}/*** 解析银行卡信息** @param root JSON 根对象* @return BankCardDTO 对象* @throws SysException 如果解析失败*/private BankCardDTO parseBankCardInfo(JSONObject root) throws SysException {try {JSONObject result = root.getJSONObject("result");if (result == null) {throw new SysException("JSON 中缺少 'result' 字段");}BankCardDTO bankCardDTO = new BankCardDTO();bankCardDTO.setValidDate(result.getString("valid_date"));bankCardDTO.setBankCardNumber(result.getString("bank_card_number"));bankCardDTO.setBankName(result.getString("bank_name"));bankCardDTO.setBankCardType(result.getInteger("bank_card_type"));bankCardDTO.setHolderName(result.getString("holder_name"));// 可选:对银行卡号进行脱敏处理if (StringUtils.isNotBlank(bankCardDTO.getBankCardNumber())) {bankCardDTO.setBankCardNumber(bankCardDTO.getBankCardNumber());}return bankCardDTO;} catch (Exception e) {throw new SysException("解析银行卡信息失败", e);}}/*** 对银行卡号进行脱敏处理** @param cardNumber 原始银行卡号* @return 脱敏后的银行卡号*/private String maskBankCardNumber(String cardNumber) {if (StringUtils.isBlank(cardNumber)) {return cardNumber;}// 假设银行卡号为 16-19 位,保留前4位和后4位,中间用 * 替换int length = cardNumber.length();if (length <= 8) {return "****"; // 如果长度不足,返回固定脱敏}return cardNumber.substring(0, 4) + "****" + cardNumber.substring(length - 4);}
}

3.7 定义Web接口

        定义一个OcrController接口,对前端页面发送的数据进行响应,直接调用已编写好的识别服务,内容如下:

package com.uav.controller.ocr;import com.uav.common.util.Result;
import com.uav.common.validator.ValidatorUtils;
import com.uav.common.validator.group.UpdateGroup;
import com.uav.models.BaiduOcrRequestDTO;
import com.uav.service.OcrService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.io.*;@RequestMapping("/ocr")
@RestController
public class OcrController {@AutowiredOcrService ocrService;@PostMapping(value = "/idcard/recognize")public Result<Object> recognizeImage(@RequestBody BaiduOcrRequestDTO requestDTO) throws IOException {ValidatorUtils.validateEntity(requestDTO, UpdateGroup.class);return new Result<>().ok(ocrService.recognizeImage(requestDTO));}}

四 测试效果

        使用Apifox测试一下接口,效果图如下:

五、总结

        上述内容即为百度OCR:证件识别的全部过程了,虽然百度官方也有API示例代码,但上述代码对数据进行了更好的解析和封装。如果你需要身份证和银行卡信息识别,可以直接拿来使用。希望可以帮到你。

如果你对区块链内容感兴趣可以查看我的专栏:小试牛刀-区块链

感谢您的关注和收藏!!!!!!

        

相关文章:

  • 【信息系统项目管理师】第10章:项目进度管理 - 48个经典题目及详解
  • 十三、面向对象底层逻辑-Dubbo序列化Serialization接口
  • React组件开发流程-03.1
  • 双指针算法:原理与应用详解
  • Notepad++ 学习(三)使用python插件编写脚本:实现跳转指定标签页(自主研发)
  • 龙芯中科2024年度业绩说明会:企稳向好,布局未来!
  • 抽奖相关功能测试思路
  • NIFI的处理器:PutDatabaseRecord 2.4.0
  • 【数据仓库面试题合集③】实时数仓建模思路与实践详解
  • C++_AVL树
  • 从API到UI:直播美颜SDK中的滤镜与贴纸功能开发与落地方案详解
  • 打破传统仓库管理困局:WMS如何重构出入库全流程
  • 告别Spring AI!我的Java轻量AI框架实践(支持多模型接入|注解式MCP架构|附开源地址)
  • 【框架安装】win10 配置安装GPU加速的tensorflow和keras教程
  • 3D 数据交换格式(.3DXML)简介
  • 【esp32 控制台】-命令
  • Flink流处理:实时计算URL访问量TopN(基于时间窗口)
  • 互联网大厂Java求职面试:Spring AI与大模型交互的高级模式与自定义开发
  • 【神经网络与深度学习】model.eval() 模式
  • WEB安全--SQL注入--MSSQL注入
  • 5月LPR下调:1年期、5年期以上品种均下调10个基点
  • 再囤三个月库存!美国客户抢付尾款,外贸企业发货订单排到7月
  • 申伟强任上海申通地铁集团有限公司副总裁
  • 印军称中国向巴基斯坦提供防空系统协助,外交部:中方十分重视与印、巴两国关系
  • 调查丨永久基本农田沦为垃圾堆场,整改为何成“纸面工程”?
  • 坐标大零号湾科创策源区,上海瑞金医院闵行院区正式启动建设