2023/8/16 华为云OCR识别驾驶证、行驶证

目录

一、 注册华为云账号开通识别驾驶证、行驶证服务

二、编写配置文件

        2.1、配置秘钥

        2.2、 编写配置工具类

三、接口测试

        3.1、测试接口

        3.2、结果

 四、实际工作中遇到的问题

        4.1、前端传值问题

        4.2、后端获取数据问题

        4.3、使用openfeign调用接口报错

        4.3、前端显示问题


hello大家好,好久没写博客了,你们找到工作了吗?博主在去年11月成功找到工作,到现在上班大半年了,最近需求用到华为云OCR文字识别,这里就详细记录一下!

一、 注册华为云账号开通识别驾驶证、行驶证服务

华为云官网:特惠专区_云服务器_云主机_企业上云-华为云

 如上图所示,华为云还有很多文字识别服务,这个看个人需求了解即可。

 开放api接口地址体验:https://console.huaweicloud.com/apiexplorer/#/openapi/OCR/debug?api=RecognizeDriverLicense

二、编写配置文件

        2.1、配置秘钥

ocr:
  projectId: xxxxxxxxxxxxxx   // 项目id:华为云个人凭证获取
  area: cn-north-4        // 区域:北京4区,目前好像只有北京4区支持这两个证件的识别服务
  ak: xxxxxxxxxxxxx        //AK:华为云个人凭证获取
  sk: xxxxxxxxxxxxx        //SK:华为云个人凭证获取

        2.2、 编写配置工具类

@Component
public class OcrUtil {
    private static final Logger log = LoggerFactory.getLogger(HttpUtils.class);

    @Value("${ocr.projectId}")
    private String projectId;
    @Value("${ocr.area}")
    private String area;
    @Value("${ocr.ak}")
    private String AK;
    @Value("${ocr.sk}")
    private String SK;
    private static CloseableHttpClient httpClient;
    static {
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
        cm.setMaxTotal(100);
        cm.setDefaultMaxPerRoute(20);
        cm.setDefaultMaxPerRoute(50);
        httpClient = HttpClients.custom().setConnectionManager(cm).build();
    }

    public String getTokenByAKSK (){
        String url = "https://iam."+area+".myhuaweicloud.com/v3/auth/tokens";
        Map<String,String> p1 = new HashMap<>();
        Map<String,Object> p2 = new HashMap<>();
        Map<String,Object> p3 = new HashMap<>();
        Map<String,Object> p4 = new HashMap<>();
        Map<String,Object> p5 = new HashMap<>();
        Map<String,Object> p6 = new HashMap<>();
        Map<String,Object> p7 = new HashMap<>();
        Map<String,Object> p8 = new HashMap<>();
        p1.put("key",AK);
        p2.put("key",SK);
        p3.put("access",p1);
        p3.put("secret",p2);
        p4.put("hw_ak_sk",p3);
        String[] str = new String[1];
        str[0] = "hw_ak_sk";
        p4.put("methods",str);
        p5.put("identity",p4);
        p6.put("name",area);
        p7.put("project",p6);
        p5.put("scope",p7);
        p8.put("auth",p5);
        String result = postJson(url, JSON.toJSONString(p8));
        return result;
    }

    public static String postJson(String url, String jsonString) {
        CloseableHttpResponse response = null;
        String result = "";
        try {
            HttpPost httpPost = new HttpPost(url);
            RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(30000).setConnectionRequestTimeout(30000).setSocketTimeout(30000).build();
            httpPost.setConfig(requestConfig);
            httpPost.setConfig(requestConfig);
            httpPost.addHeader("Content-type", "application/json; charset=utf-8");
            httpPost.setHeader("Accept", "application/json");
            httpPost.setEntity(new StringEntity(jsonString, StandardCharsets.UTF_8));
            response = httpClient.execute(httpPost);
            Header[] h = response.getAllHeaders();
            for(Header header : h){
                if(header.getName().equals("X-Subject-Token")){
                    result = header.getValue();
                }
            }
        } catch (IOException e) {
            log.error("调用HttpUtils.Post IOException, url=" + url + ",param=" + jsonString, e);
        } finally {
            try {
                if (response != null) {
                    response.close();
                }
            } catch (IOException e) {
                log.error("调用HttpUtils.Post IOException, url=" + url + ",param=" + jsonString, e);
            }
        }
        return result;
    }

    public static Result postJson(String url, String token, String jsonString) {
        CloseableHttpResponse response = null;
        BufferedReader in;
        String result = "";
        boolean err = false;
        try {
            HttpPost httpPost = new HttpPost(url);
            RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(30000).setConnectionRequestTimeout(30000).setSocketTimeout(30000).build();
            httpPost.setConfig(requestConfig);
            httpPost.setConfig(requestConfig);
            httpPost.addHeader("Content-type", "application/json; charset=utf-8");
            httpPost.setHeader("Accept", "application/json");
            httpPost.setHeader("X-Auth-Token", token);
            httpPost.setEntity(new StringEntity(jsonString, StandardCharsets.UTF_8));
            response = httpClient.execute(httpPost);
            in = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
            StringBuilder sb = new StringBuilder();
            String line;
            String NL = System.getProperty("line.separator");
            while ((line = in.readLine()) != null) {
                sb.append(line).append(NL);
            }
            in.close();
            result = sb.toString();
            err = response.getStatusLine().getStatusCode() == 400;
        } catch (IOException e) {
            log.error("调用HttpUtils.Post IOException, url=" + url + ",param=" + jsonString, e);
        } finally {
            try {
                if (response != null) {
                    response.close();
                }
            } catch (IOException e) {
                log.error("调用HttpUtils.Post IOException, url=" + url + ",param=" + jsonString, e);
            }
        }
        if (err) {
            return Result.error().data("data", result);
        } else {
            return Result.ok().data("data", result);
        }
    }
    /**
     * 功能描述:
     * 华为云驾驶证识别
     * @Param: [token, imageUrl]
     * @Return: com.ruoyi.phaseone.common.utils.R
     * @Author: Mr.Huang
     * @Date: 2023/8/10 10:21
     **/
    public Result driverLicense(String token, String imageUrl) {
        String url = "https://ocr."+area+".myhuaweicloud.com/v2/"+projectId+"/ocr/driver-license";
        Map<String, Object> p = new HashMap<>();
        // url:图片地址  image:图片的base64数据 两个参数二选一,详情见官网API
        p.put("url", imageUrl);
        p.put("side", "front");
        log.info("ocr驾驶证识别请求路径:{},请求参数:{}",url,p);
        return postJson(url, token, JSON.toJSONString(p));
    }
    /**
     * 功能描述:
     * 华为云行驶证识别
     * @Param: [token, imageUrl]
     * @Return: com.sy.milkteasyservice.response.Result
     * @Author: Mr.Huang
     * @Date: 2023/8/14 10:52
     **/
    public Result vehicleLicense(String token, String imageUrl) {
        String url = "https://ocr."+area+".myhuaweicloud.com/v2/"+projectId+"/ocr/vehicle-license";
        Map<String, Object> p = new HashMap<>();
        // url:图片地址  image:图片的base64数据 两个参数二选一,详情见官网API
        p.put("url", imageUrl);
        // front:行驶证主页  back:行驶证副页
        p.put("side", "front");
        log.info("ocr行驶证识别请求路径:{},请求参数:{}",url,p);
        return postJson(url, token, JSON.toJSONString(p));
    }
}

调用此接口的流程就是:获取token后设置请求头,发起请求获得结果

三、接口测试

        3.1、测试接口

@RestController
public class HuaWeiObsController {
    @Autowired
    private ObsService ObsService;

    @Autowired
    private OcrUtil ocrUtil;

    @ApiOperation(value = "上传图片文件",notes = "xxxxx")
    @PostMapping("/uploadImgFile")
    public Result upload(MultipartFile file){
        String url = ObsService.upload(file);
        // 行驶证识别

/*        Result result = ocrUtil.vehicleLicense(ocrUtil.getTokenByAKSK(), url);
        System.out.println("调用华为云行驶证识别结果:"+result.toString());
        Map<String, Object> data = result.getData();
        Object data1 = data.get("data");
        JSONObject jsonObject = JSON.parseObject(data1.toString());
        String result1 = jsonObject.get("result").toString();
        System.out.println(result1);
        vehicleLicense vehicleLicense = JSON.parseObject(result1, vehicleLicense.class);
        System.out.println("orc识别结果是:"+vehicleLicense.toString());
        return Result.ok().data("result",vehicleLicense).data("url",url);*/
        // 驾驶证识别
        Result result = ocrUtil.driverLicense(ocrUtil.getTokenByAKSK(), url);
        System.out.println("调度接口获取的结果集:"+result);
        Map<String, Object> data = result.getData();
        Object data1 = data.get("data");
        JSONObject jsonObject = JSON.parseObject(data1.toString());
        String result1 = jsonObject.get("result").toString();
        System.out.println(result1);
        drivingLicence drivingLicence = JSON.parseObject(result1, drivingLicence.class);
        System.out.println("orc识别结果是:"+drivingLicence.toString());
        return Result.ok().data("result",drivingLicence).data("url",url);
    }
}

这里的文件上传用的是华为云的OBS服务,上传图片后拿到该图片对应的地址,注意:此图片地址必须要可以访问,如设置访问权限则会调用接口失败!

        3.2、结果

 

 

 四、实际工作中遇到的问题

        4.1、前端传值问题

问题描述:由于前端使用的是根据el-upload封装后的组件,我发现驾驶证和行驶证上传到的是同一个接口,那这样就分不清上传的驾驶证还是行驶证。

解决办法:在调用后端接口传入图片类型字段,判断是驾驶证还是行驶证

:action="this.$http.adornUrl(`/proxyKpiApi/${config.uploadUrl}?${config.id ? `id=${config.id}&` : ''}token=${$cookie.get('token')}&${config.type ? `type=${config.type}` : ''}`)"

 

        4.2、后端获取数据问题

问题描述:项目中使用的华为云obs文件上传服务,但是上传后获得的图片地址因为安全性考虑,不能直接访问,导致调用接口失败。

 

    // openfeign驾驶证服务
    @PostMapping(value = "/performance/driverLicense", params = "{url={url}}")
    R driverLicense(@RequestParam("url") String url);
    // openfeign行驶证服务
    @PostMapping(value = "/performance/vehicleLicense", params = "{url={url}}")
    R vehicleLicense(@RequestParam("url") String url);

解决办法:将上传的文件转换成base64格式,调用接口时改成使用image参数,图片的base64数据。

                if(StringUtils.isNotBlank(type)){
                    byte[] fileBytes = file.getBytes();
                    // 将上传的文件转换成base64格式
                    String base64String = Base64.getEncoder().encodeToString(fileBytes);
                    if(type.equals("driverLicense")){
                        // 远程调用识别驾驶证服务
                        com.ruoyi.phaseone.common.utils.R r = carrierPerformanceService.driverLicense(base64String);
                        log.info("ocr驾驶证识别请求结果:{}",r);
                        if(String.valueOf(r.get("code")).equals("0")){
                            Object data = r.get("data");
                            JSONObject jsonObject = JSON.parseObject(data.toString());
                            String result1 = jsonObject.get("result").toString();
                            DrivingLicenceEntity drivingLicenceEntity = JSON.parseObject(result1, DrivingLicenceEntity.class);
                            map.put("ocrResult",drivingLicenceEntity);
                        }
                    }else if(type.equals("vehicleLicense")){
                        // 远程调用识别行驶证服务
                        com.ruoyi.phaseone.common.utils.R r = carrierPerformanceService.vehicleLicense(base64String);
                        log.info("ocr行驶证识别请求结果:{}",r);
                        if(String.valueOf(r.get("code")).equals("0")){
                            Object data = r.get("data");
                            JSONObject jsonObject = JSON.parseObject(data.toString());
                            String result1 = jsonObject.get("result").toString();
                            VehicleLicenseEntity vehicleLicenseEntity = JSON.parseObject(result1, VehicleLicenseEntity.class);
                            map.put("ocrResult",vehicleLicenseEntity);
                        }
                    }
                }

        4.3、使用openfeign调用接口报错

问题描述:按照上面步骤修改后,由于项目是微服务架构,文件上传服务和前端调用的接口不在同一个服务上,所以要使用openfeign远程调用。但是调用的过程中报错了。报错信息:[<h1>Bad Message 414</h1><pre>reason: URI Too Long</pre>]

解决办法:此问题是因为图片数据转换成base64后,通过远程调用传的值URI太长了,因此我们将接口改造一下,将参数改成对象。

@Data
public class ImageEntity implements Serializable {
    private String imageUrl;
    private String imageBase64;
}
    // openfeign驾驶证服务
    @PostMapping( "/performance/driverLicense")
    R driverLicense(@RequestBody ImageEntity image);
    // openfeign行驶证服务
    @PostMapping("/performance/vehicleLicense")
    R vehicleLicense(@RequestBody ImageEntity image);
                if(StringUtils.isNotBlank(type)){
                    byte[] fileBytes = file.getBytes();
                    // 将上传的文件转换成base64格式
                    String base64String = Base64.getEncoder().encodeToString(fileBytes);
                    ImageEntity image =new ImageEntity();
                    image.setImageBase64(base64String);
                    if(type.equals("driverLicense")){
                        // 远程调用识别驾驶证服务
                        com.ruoyi.phaseone.common.utils.R r = carrierPerformanceService.driverLicense(image);
                        log.info("ocr驾驶证识别请求结果:{}",r);
                        if(String.valueOf(r.get("code")).equals("0")){
                            Object data = r.get("data");
                            JSONObject jsonObject = JSON.parseObject(data.toString());
                            String result1 = jsonObject.get("result").toString();
                            DrivingLicenceEntity drivingLicenceEntity = JSON.parseObject(result1, DrivingLicenceEntity.class);
                            map.put("ocrResult",drivingLicenceEntity);
                        }
                    }else if(type.equals("vehicleLicense")){
                        // 远程调用识别行驶证服务
                        com.ruoyi.phaseone.common.utils.R r = carrierPerformanceService.vehicleLicense(image);
                        log.info("ocr行驶证识别请求结果:{}",r);
                        if(String.valueOf(r.get("code")).equals("0")){
                            Object data = r.get("data");
                            JSONObject jsonObject = JSON.parseObject(data.toString());
                            String result1 = jsonObject.get("result").toString();
                            VehicleLicenseEntity vehicleLicenseEntity = JSON.parseObject(result1, VehicleLicenseEntity.class);
                            map.put("ocrResult",vehicleLicenseEntity);
                        }
                    }
                }

        4.3、前端显示问题

问题描述:调用接口成功后,由于前端使用的是el-upload封装后的组件,用的watch函数监听值的变化,每次打开该控件都会显示其结果。

    watch: {
      'dataForm.carLicence.attachments' (newVal, oldVal) {

      }

解决方案:判断newVal中的ocrResult是否为空,不为空在赋值,因为可以上传多个文件,所以每次更新选取集合中最后一个元素的数据

    watch: {
      'dataForm.carLicence.attachments' (newVal, oldVal) {
        if (newVal[newVal.length - 1] && newVal[newVal.length - 1].ocrResult !== undefined && newVal[newVal.length - 1].ocrResult !== null) {
          // 车牌号
          this.dataForm.carNumber = newVal[newVal.length - 1].ocrResult.number;
          // 发动机号
          this.dataForm.engineNumber = newVal[newVal.length - 1].ocrResult.engine_no;
          // 所有人
          this.dataForm.carLicence.person = newVal[newVal.length - 1].ocrResult.name;
          // 品牌型号
          this.dataForm.carLicence.brandModel = newVal[newVal.length - 1].ocrResult.model;
          // 注册日期
          this.dataForm.carLicence.registerTime = newVal[newVal.length - 1].ocrResult.register_date;
          // 发证日期
          this.dataForm.carLicence.certificationTime = newVal[newVal.length - 1].ocrResult.issue_date;
        }
      }
    },

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/85583.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SQL 语句继续学习之记录二

三&#xff0c; 聚合与排序 对表进行聚合查询&#xff0c;即使用聚合函数对表中的列进行合计值或者平均值等合计操作。 通常&#xff0c;聚合函数会对null以外的对象进行合计。但是只有count 函数例外&#xff0c;使用count(*) 可以查出包含null在内的全部数据行数。 使用dis…

javeee eclipse项目导入idea中

步骤一 复制项目到idea工作空间 步骤二 在idea中导入项目 步骤三 配置classes目录 步骤四 配置lib目录 步骤五 添加tomcat依赖 步骤六 添加artifacts 步骤七 部署到tomcat

数学建模-模型详解(2)

微分模型 当谈到微分模型时&#xff0c;通常指的是使用微分方程来描述某个系统的动态行为。微分方程是描述变量之间变化率的数学方程。微分模型可以用于解决各种实际问题&#xff0c;例如物理学、工程学、生物学等领域。 微分模型可以分为两类&#xff1a;常微分方程和偏微分…

【计算机网络】日志与守护进程

文章目录 日志日志的创建logmessage 函数日志左边部分实现日志右边部分实现 完整代码log.hpp(整体实现)err.hpp (错误信息枚举&#xff09; 守护进程PGID SID TTY 的介绍shell中控制进程组的方式结论 为什么要有守护进程存在&#xff1f;守护进程的创建使用守护进程的条件守护进…

Tomcat线程池梳理

Tomcat梳理 文章目录 Tomcat梳理1.问题2.监控tomcat线程池springboot1.xspringboot2.x转为json格式打印输出 3.SpringBoot内置线程解析测试controlleryaml配置可知ThreadPoolExecutor有如下五种线程池状态。线程池监控指标并发测试并发请求数 < Tomcat最大线程数20并发请求数…

聊聊 Docker

聊聊 Docker Docker 是什么&#xff1f; 定义 Docker 是一款 开源的应用容器引擎。 简单来说&#xff0c;就是 以容器虚拟化技术为基础的软件。可以把应用程序和所依赖的包一起打包到一个可移植的镜像中&#xff0c;发布到 Linux 或者 Windows 上运行。&#xff08;代码 运…

从零开始学习YOLOv5 保姆级教程

一、前言 YOLO系列是one-stage且是基于深度学习的回归方法&#xff0c;而R-CNN、Fast-RCNN、Faster-RCNN等是two-stage且是基于深度学习的分类方法。 YOLOv5是一种单阶段目标检测算法&#xff0c;该算法在YOLOv4的基础上添加了一些新的改进思路&#xff0c;使其速度与精度都得…

使用 wxPython 和 pymupdf进行 PDF 加密

PDF 文件是一种常见的文档格式&#xff0c;但有时候我们希望对敏感信息进行保护&#xff0c;以防止未经授权的访问。在本文中&#xff0c;我们将使用 Python 和 wxPython 库创建一个简单的图形用户界面&#xff08;GUI&#xff09;应用程序&#xff0c;用于对 PDF 文件进行加密…

Lua之Lua源文件批量转换为luac字节码文件

准备的工具&#xff1a;luac.exe CSDNhttps://mp.csdn.net/mp_download/manage/download/UpDetailed Unity版: using System; using System.Collections; using System.Collections.Generic; using System.IO; using UnityEditor; using UnityEngine;public static class Bat…

前端开发中移动端开发需要注意什么? - 易智编译EaseEditing

在前端开发中&#xff0c;移动端开发具有一些独特的挑战和注意事项。以下是移动端开发时需要注意的一些重要点&#xff1a; 响应式设计&#xff1a; 移动设备的屏幕尺寸和分辨率多样&#xff0c;因此要采用响应式设计&#xff0c;确保你的网站或应用在各种设备上都能良好地呈…

基于学习交流社区的自动化测试实现

一 项目介绍 项目名称 项目展示链接&#xff1a; 学习交流社区 项目介绍 项目介绍&#xff1a; 学习交流社区是一个基于Spring的前后端分离的在线论坛系统。使用了MySQL数据库来存储相关信息&#xff0c;项目完成后使用Xshell将其部署到云服务器上。 前端页面&#xff1a; 前端…

实时云渲染的关键技术是什么

首先&#xff0c;我们需要了解实时云渲染的概念。实时云渲染是指将原本在本地电脑上进行的渲染工作转移到云端服务器上完成&#xff0c;并实时将处理结果返回给终端用户显示。这其实是一个合成词&#xff0c;由实时、云和渲染三个关键词组成。实时表示具有时效性和同步效果&…

09 数据库开发-MySQL

文章目录 1 数据库概述2 MySQL概述2.1 MySQL安装2.1.1 解压&添加环境变量2.1.2 初始化MySQL2.1.3 注册MySQL服务2.1.4 启动MySQL服务2.1.5 修改默认账户密码2.1.6 登录MySQL 2.2 卸载MySQL2.3 连接服务器上部署的数据库2.4 数据模型2.5 SQL简介2.5.1 SQL通用语法2.3.2 分类…

ES搭建集群

一、创建 elasticsearch-cluster 文件夹 创建 elasticsearch-7.8.0-cluster 文件夹&#xff0c;在内部复制三个 elasticsearch 服务。 然后每个文件目录中每个节点的 config/elasticsearch.yml 配置文件 node-1001 节点 #节点 1 的配置信息&#xff1a; #集群名称&#xff0…

用断言更好地实现替换重复出现的单词

断言是指对匹配到的文本位置有要求。也就是说&#xff0c;在有些情况下&#xff0c;我们对要匹配的文本的位置也有一定的要求。为了解决这个问题&#xff0c;正则中提供了一些结构&#xff0c;只用于匹配位置&#xff0c;而不是文本内容本身&#xff0c;这种结构就是断言。常见…

【踩坑日记】STM32 USART 串口与 FreeRTOS 冲突

文章目录 问题描述问题出现的环境问题解决过程第一步第二步第三步第四步第五步第六步第七步第八步 后续验证一些思考类似的问题后记 问题描述 笔者使用 FreeRTOS 创建了两个任务&#xff0c;使两颗 LED 以不同频率闪烁&#xff0c;但是在加入串口 USART 部分代码后&#xff0c…

【Linux】线程篇Ⅰ:线程和task_struct 执行流的理解、相关接口命令、线程异常、线程的私有和共享

线程Ⅰ 一、概念0. 线程1. 线程的优缺点2. 页框和页帧3. 页表的设计、虚拟地址的解析方式、以及块为什么设计成 4kb4. 对进程的一些整体理解 二、一些接口 和 命令1. ps -aL - - 查看执行流2. pthread_create 函数&#xff1a;创建线程3. ptread_join 线程等待4. ptread_exit 线…

简单计算器的实现(含转移表实现)

文章目录 计算器的一般实现使⽤函数指针数组的实现&#xff08;转移表&#xff09; 计算器的一般实现 通过函数的调用&#xff0c;实现加减乘除 # define _CRT_SECURE_NO_WARNINGS#include<stdio.h>int Add(int x, int y) {return x y; }int Sub(int x, int y) {retur…

变频器和plc之间无线MODBUS通讯

在工业现场由PLC远程控制变频器的应用非常常见&#xff0c;如果挖沟布线不便或者变频器在移动设备上&#xff0c;那么采用无线通讯就是最佳方案。 这里我们选用最常用的三菱 FX2N PLC和三菱变频器为例&#xff0c;并结合日系plc专用无线通讯终端DTD435M来说明PLC与变频器之间的…

# Lua与C++交互(二)———— 交互

C 调用lua 基础调用 再来温习一下 myName “beauty girl” C想要获取myName的值&#xff0c;根据规则&#xff0c;它需要把myName压入栈中&#xff0c;这样lua就能看到&#xff1b;lua从堆栈中获取myName的值&#xff0c;此时栈顶为空&#xff1b;lua拿着myName去全局表中查…