关键字: 车牌识别 车型识别 百度AI SpringBoot Vue Java SDK
文章目录
- 1.创建百度AI应用
- 2.通过Access_token密码调用百度AI接口(车型识别)
- 3.Java客户端AipImageClassifyClient调用AI接口
- 4.车型识别接口说明
- 5.Java后端Controller层接收图片
- 6.Java后端Service层访问AI接口
- 7.基于VUE的前端开发车牌识别和车型识别
- 8.总结与展望
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压缩工具包。
然后,新建AipImageClassifyClient
,AipImageClassifyClient
是图像识别的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_KEY
与SECRET_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));
}
车型识别 请求参数详情
参数名称 | 是否必选 | 类型 | 默认值 | 说明 |
image | 是 | mixed | - | 本地图片路径或者图片二进制数据 |
top_num | 否 | String | 5 | 返回预测得分top结果数,默认为5 |
baike_num | 否 | String | 0 | 返回百科信息的结果数,默认不返回 |
车型识别 返回数据参数详情
字段 | 是否必选 | 类型 | 说明 |
log_id | 是 | uint64 | 唯一的log id,用于问题定位 |
color_result | 是 | string | 车身颜色 |
result | 是 | car-result() | 车型识别结果数组 |
+name | 是 | string | 车型名称,示例:宝马x6 |
+score | 是 | double | 置信度,取值范围0-1,示例:0.5321 |
+year | 是 | string | 年份 |
+baike_info | 否 | object | 对应识别结果的百科词条名称 |
++baike_url | 否 | string | 对应识别结果百度百科页面链接 |
++image_url | 否 | string | 对应识别结果百科图片链接 |
++description | 否 | string | 对应识别结果百科内容描述 |
location_result | 是 | string | 车辆在图片中的位置信息 |
+width | 是 | float | 车辆区域的宽度 |
+height | 是 | float | 车辆区域的高度 |
+left | 是 | float | 车辆区域离左边界的距离 |
+top | 是 | float | 车辆区域离上边界的距离 |
车型识别 返回示例
{
"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的方式,前端可以采用该种方式发起请求。在此应用中需要保存用户的图片,因此没有采用该方式。