关键字: 车牌识别 车型识别 百度AI SpringBoot Vue Java SDK

1.创建百度AI应用

进入百度AI官网https://ai.baidu.com/,点击右上角控制台,用百度账号登录。如果没有百度账号,需要首先创建一个百度账号。

在控制台可以看到各种百度提供的AI应用接口。找到需要的应用接口,点进去可以创建一个相应的应用。

图像识别提供一个组合API接口,支持多种垂类识别服务的灵活组合调用。

然后会提供给该应用一个API Key 和Secret Key,它是我们调用接口必须的两个参数。

以下是本次开发中用到的车型识别的应用。百度AI的车牌识别需要另外申请一个应用。需要注意的是不同的应用有不同的AppID等参数。

在这里插入图片描述

2.通过Access_token密码调用百度AI接口(车型识别)

车型识别功能检测图片中的车辆,识别具体车型,包括车辆品牌体型号、颜色、年份、位置信息。

车辆识别为例。它的请求url为: https://aip.baidubce.com/rest/2.0/image-classify/v1/car

调用方式一定为POST。同时我们要在后面添加以下参数:

access_token:即前面获取的密码

在请求头中设置Content-Type参数

Content-Type:设置为application/x-www-form-urlencoded

在请求体Body中设置image图片的信息参数

1.image:图片信息参数,它是通过图片路径找到图片转为字节,然后经过Base64编码最后形成的字符串。如下代码

//获取图片的路径

String filePath = Path;

//将图片转为字节数组

byte[] imgData = FileUtil.readFileByBytes(filePath);

//将图片字节数组转为Base64编码的字符串换

String imgStr = Base64Util.encode(imgData);

//将经过Base64编码的图片字符串转为UTF-8编码的字符串。可以直接传给image参数

String imgParam = URLEncoder.encode(imgStr, “UTF-8”);

2.top_num:设置返回的信息的数量,默认5,因为车辆识别会得出多个结果,按几率得到,我们可以获取前n个的数据

3.baike_num:设置返回的百科的信息数,默认不返回

3.Java客户端AipImageClassifyClient调用AI接口

以上方法是采用接口直接调用,下面的方法是使用JAVA客户端进行访问。首先需要安装ImageClassify Java SDK,可以在官方网站下载Java SDK压缩工具包。

在这里插入图片描述

然后,新建AipImageClassifyClientAipImageClassifyClient是图像识别的Java客户端,为使用图像识别的开发人员提供了一系列的交互方法。

用户可以参考如下代码新建一个AipImageClassifyClient,初始化完成后建议单例使用,避免重复获取access_token

public class Sample {
    //设置APPID/AK/SK
    public static final String APP_ID = "你的 App ID";
    public static final String API_KEY = "你的 Api Key";
    public static final String SECRET_KEY = "你的 Secret Key";

    public static void main(String[] args) {
        // 初始化一个AipImageClassifyClient
        AipImageClassifyClient client = new AipImageClassifyClient(APP_ID, API_KEY, SECRET_KEY);

        // 可选:设置网络连接参数
        client.setConnectionTimeoutInMillis(2000);
        client.setSocketTimeoutInMillis(60000);

        // 可选:设置代理服务器地址, http和socket二选一,或者均不设置
        client.setHttpProxy("proxy_host", proxy_port);  // 设置http代理
        client.setSocketProxy("proxy_host", proxy_port);  // 设置socket代理

        // 调用接口
        String path = "test.jpg";
        JSONObject res = client.objectDetect(path, new HashMap<String, String>());
        System.out.println(res.toString(2));
        
    }
}

在上面代码中,常量APP_ID在百度智能云控制台中创建,常量API_KEYSECRET_KEY是在创建完毕应用后,系统分配给用户的,均为字符串,用于标识用户,为访问做签名验证,可在AI服务控制台中的应用列表中查看。

4.车型识别接口说明

识别图片中车辆的具体车型,可识别常见的3000+款车型(小汽车为主),输出车辆的品牌型号、颜色、年份、位置信息;支持返回对应识别结果的百度百科词条信息,包含词条名称、百科页面链接、百科图片链接、百科内容简介。

当前只支持单主体识别,若图片中有多个车辆,则识别目标最大的车辆。

百度官方文档提供了调用的示例,提交参数的方式有本地路径和二进制数组。

public void sample(AipImageClassify client) {
    // 传入可选参数调用接口
    HashMap<String, String> options = new HashMap<String, String>();
    options.put("top_num", "3");
    options.put("baike_num", "5");


    // 参数为本地路径
    String image = "test.jpg";
    JSONObject res = client.carDetect(image, options);
    System.out.println(res.toString(2));

    // 参数为二进制数组
    byte[] file = readFile("test.jpg");
    res = client.carDetect(file, options);
    System.out.println(res.toString(2));
}

车型识别 请求参数详情

参数名称是否必选类型默认值说明
imagemixed-本地图片路径或者图片二进制数据
top_numString5返回预测得分top结果数,默认为5
baike_numString0返回百科信息的结果数,默认不返回

车型识别 返回数据参数详情

字段是否必选类型说明
log_iduint64唯一的log id,用于问题定位
color_resultstring车身颜色
resultcar-result()车型识别结果数组
+namestring车型名称,示例:宝马x6
+scoredouble置信度,取值范围0-1,示例:0.5321
+yearstring年份
+baike_infoobject对应识别结果的百科词条名称
++baike_urlstring对应识别结果百度百科页面链接
++image_urlstring对应识别结果百科图片链接
++descriptionstring对应识别结果百科内容描述
location_resultstring车辆在图片中的位置信息
+widthfloat车辆区域的宽度
+heightfloat车辆区域的高度
+leftfloat车辆区域离左边界的距离
+topfloat车辆区域离上边界的距离

车型识别 返回示例

{
  "log_id": 4086212218842203806,
  "location_result": {
    "width": 447,
    "top": 226,
    "height": 209,
    "left": 188
  },
  "result": [{
    "baike_info": {
      "baike_url": "http://baike.baidu.com/item/%E5%B8%83%E5%8A%A0%E8%BF%AAChiron/20419512",
      "description": "布加迪Chiron是法国跑车品牌布加迪出品的豪华超跑车。配置四涡轮增压发动机,420 公里每小时,有23种颜色的选择,售价高达260万美元。"
    },
    "score": 0.98793351650238,
    "name": "布加迪Chiron",
    "year": "无年份信息"
  },
  {
    "score": 0.0021970034576952,
    "name": "奥迪RS5",
    "year": "2011-2017"
  },
  {
    "score": 0.0021096928976476,
    "name": "奥迪RS4",
    "year": "无年份信息"
  },
  {
    "score": 0.0015581247862428,
    "name": "奥迪RS7",
    "year": "2014-2016"
  },
  {
    "score": 0.00082337751518935,
    "name": "布加迪威航",
    "year": "2004-2015"
  }],
  "color_result": "颜色无法识别"
}

车型识别的错误码可见:https://ai.baidu.com/ai-doc/VEHICLE/Gk3hb3ifo

车牌识别的调用方式和以上车型识别的方式大同小异,这里略过。

5.Java后端Controller层接收图片

本次开发的后端应用基于SpringBoot v2.4.1,Controller层的开发内容如下所示。

/**
 * @Author Jarrett Luo
 * @Date 2021/1/28 18:26
 * @Version 1.0
 */
@RestController
@RequestMapping(value = "/recognition")
public class RecognitionController {
    
    @Resource
    RecognitionService recognitionService;
    
    @CrossOrigin
    @PostMapping
    public ApiResult save(@RequestParam(value="file", required=false) MultipartFile multipartFile)
            throws IOException {
        byte[] imgBytes = multipartFile.getBytes();
        if(imgBytes!=null){
            return recognitionService.find(imgBytes);
        }
        else {
            return ApiResult.error(201,"数据为空!");
        }
    }
}

byte[] imgBytes = multipartFile.getBytes();将前端传递的数据转换为二进制数组,以便于后面进行调用。

必须进行非空判断后才能进入下一步,否则会出现没有返回值的错误。ApiResult是自己写的一个工具,用于返回结果,如果传入的空数据,则返回201的错误码。

6.Java后端Service层访问AI接口

根据百度AI控制页创建的应用,设置每个应用的APP_ID, API_KEY, SECRET_KEY。这里需要注意的是百度AI提交的不同接口需要创建不同的应用。

我们对文件的大小进行了限制,这里仅处理10MB以下的图片。

public ApiResult find(byte[] image) {
        if(image.length>10048575) {
            return ApiResult.error(201, "文件过大!");
        }
        // 初始化一个AipOcr
        AipOcr client = new AipOcr(APP_ID, API_KEY, SECRET_KEY);
        HashMap<String, String> options = new HashMap<String, String>();

        JSONObject res = client.plateLicense(image, options);
        String number;

        if(res.has("words_result")) {
            JSONObject jsonData = res.getJSONObject("words_result");
            number = jsonData.getString("number");
        }else {
            number = null;
        }

        // 初始化一个AipImageClassify
        AipImageClassify client1 = new AipImageClassify(APP_ID_1, API_KEY_1, SECRET_KEY_1);

        // 传入可选参数调用接口
        HashMap<String, String> options1 = new HashMap<String, String>();
        options.put("top_num", "1");

        // 参数为本地路径
        JSONObject res1 = client1.carDetect(image, options1);
        String color;
        String name;
        String year;
        if(res1.has("result")) {
            JSONArray jsonData = res1.getJSONArray("result");
            JSONObject vehicle = jsonData.getJSONObject(0);//获取数组第一个元素
            year = vehicle.getString("year");
            name = vehicle.getString("name");
            color = res1.getString("color_result");
            if(year.equals("无年份信息")){
                year = null;
            }
            if(name.equals("非车类")) {
                name = null;
            }
            if(color.equals("无车辆颜色信息")) {
                color = null;
            }
        }else {
            year = null;
            name = null;
            color = null;
        }

        RecognitionDTO recognitionDTO = new RecognitionDTO(number,
                name,
                year,
                color);
        return ApiResult.success(recognitionDTO);
    }

注意以上代码的ApiResult是自己写的一个结果转换的工具类。关于ApiResult的详细内容可以查看作者github的详细介绍。

7.基于VUE的前端开发车牌识别和车型识别

根据开发需求,在页面中增加一个拍照自动识别按钮,用户点击按钮后可以选择拍照或者相册中的照片。相应的HTML代码如下所示。

在这里插入图片描述

        /**
         * 图片拍照识别上传
         * @author: 罗佳瑞
         */
        uploadFile() {
            var that = this;
            var inputDOM = that.$refs.imageInput;
            var file = inputDOM.files;
            var formData = new FormData();
            formData.append('file', file[0]);
            this.uploadImage(formData)

            Indicator.open({
                text: '识别中...',
                spinnerType: 'fading-circle'
            });
            setTimeout(()=>{
                Indicator.close();
            },15000)
        },

以下是采用异步方式提交数据到后台服务器,提交数据的函数时recorgnizeRequest。如果成功返回数据该函数将回调uploadImageResult,关于该回调函数下文将会介绍。

/**
* 异步提交数据到后台
* @author: 罗佳瑞
*/
async uploadImage(formData) {
      await vehiclePageRequest.recorgnizeRequest(formData)
          .then(res => {
          this.uploadImageResult(res)
      })
          .catch(err => {
          Toast("" + err)
      })
},

采用axios提交数据到相应的URL。VUE中采用Promise提交数据,采用这种方式提交更好的看到提交成功和识别的结果。特别要注意,为了避免axios对formdata进行转换,因此最好加上tansformRequest

function recorgnizeRequest(FormData) {
    return new Promise((resolve, reject) => {
        axios({
            url: RECORGNIZE_URL,
            method: 'POST',
            headers: {
                'Content-Type': "multipart/form-data",
                // token: sessionStorage.getItem('token')
            },
            //这部分非常重要,否则formdata会被转格式
            transformRequest: [function(){
                return FormData;
            }],
            params: {},
            data: FormData,
        })
        .then((res) => {
            // 成功
            resolve(res.data)
        })
        .catch((res) => {
            // 失败
            reject(res)
        })
    })
}

后端返回的数据采用如下代码映射到表单中。由于返回的结果中可能没有识别到车辆,或者图片中没有车辆信息,因此在这一步进行非空判断。

Indicator是前端框架的指示器,用于告知用户当前识别进度以及识别结果。

uploadImageResult(res) {
    if(res.code==200) {
        var recorgnizeResult = res.data
        var rln = recorgnizeResult.licenseNumber
        var rvb = recorgnizeResult.name
        var rvc = recorgnizeResult.color;
        var rvy = recorgnizeResult.year;

        var resultStr = "";
        if(rln!=null){
            this.vehicleInfo.vehiclePlate = rln;
            resultStr = resultStr + "车牌可能是:" + rln + ";\n"
        }
        if(rvb!=null) {
            this.vehicleInfo.vehicleBrand = rvb;
            resultStr = resultStr + "品牌可能是:" + rvb + ";\n"
        }

        if(rvc!=null) {
            this.vehicleInfo.vehicleColor = rvc;
            resultStr = resultStr + "颜色可能是:" + rvc + ";\n"
        }
        if(rvy!=null) {
            var rvyFormat = rvy.substr(0,4)
            resultStr = resultStr + "年代可能是:" + rvyFormat + "年;\n"
        }

        Indicator.close();
        Indicator.open({
            text: "识别成功\n" + resultStr,
            spinnerType: 'fading-circle'
        });
        setTimeout(()=>{
            Indicator.close();
        },7500)

    }else{
        Toast({
            message: "连接超时!"
        })
    }
}

前端页面开发结果如下。用户点击拍照自动识别后,选择拍照或者相册,提交照片等待返回结果。

服务器返回的结果会自动输入到下图中的车牌号,汽车品牌,车身颜色。

在这里插入图片描述

8.总结与展望

  • 本文从访问百度AI接口出发,详细阐述了后端如何调用百度接口,实现一次图片上传得到车牌识别和车型识别的结果。

  • 本文的前端开发作为示例,提供了一种思路,更多的如客户端,安卓,IOS应用等等都可以采用如此方法开发。

  • 采用百度AI提供的接口,可以拓展更多丰富的应用;多种接口的叠加让用户一次上传便得到多种结果。

  • 通过成熟的AI算法,识别的准确度和速度都比较令人满意。

  • 以上过程中存在的不足是,由于用户拍照后直接上传的图片是比较大的,导致在传输过程中比较慢,甚至会出现超时的情况,用户体验不够好。改进的方法是前端页面首先对大文件进行压缩后再上传。

  • 百度AI提供了组合调用AI的方式,前端可以采用该种方式发起请求。在此应用中需要保存用户的图片,因此没有采用该方式。

如果您觉得上面的内容对您有帮助欢迎点赞、评论、转发!
更多内容请查阅作者博客:https://jiaruiblog.com
或者star作者github: https://github.com/Jarrettluo?tab=repositories

Logo

前往低代码交流专区

更多推荐