1.车牌识别

This commit is contained in:
jiangtd 2026-05-19 16:16:20 +08:00
parent 12ba7d8ff4
commit 001fe17de3
3 changed files with 127 additions and 45 deletions

3
.gitignore vendored
View File

@ -264,4 +264,5 @@ build
*/tmp/**.html
/log
.git
.git
/.codebuddy/rules/编码规则.mdc

View File

@ -1,39 +1,36 @@
package com.heyu.api.controller.car;
import com.aliyun.ocr20191230.models.RecognizeLicensePlateResponse;
import com.aliyun.ocr20191230.models.RecognizeLicensePlateResponseBody;
import com.heyu.api.alibaba.handle.common.text.ARecognizeLicensePlateHandle;
import com.heyu.api.alibaba.request.common.text.ARecognizeLicensePlateRequest;
import com.heyu.api.baidu.handle.traffic.BLicensePlateHandle;
import com.heyu.api.baidu.request.traffic.BLicensePlateRequest;
import com.heyu.api.baidu.response.traffic.BLicensePlateResp;
import com.heyu.api.controller.BaseController;
import com.heyu.api.data.annotation.NotIntercept;
import com.heyu.api.data.utils.ApiR;
import com.heyu.api.data.utils.R;
import com.heyu.api.request.car.LicensePlateRecognizeRequest;
import com.heyu.api.resp.car.RecognizeLicensePlateResp;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
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;
/***
* https://next.api.aliyun.com/api/ocr/2019-12-30/RecognizeLicensePlate?useCommon=true&tab=DOC&lang=JAVA&sdkStyle=dara
*
*车牌识别
*
* RecognizeLicensePlate
*
*
* 车牌识别能力可以准确识别出图像中车牌位置输出车牌位置坐标车牌类型车牌号码车牌号码置信度车牌置信度 5 个关键字段信息
*
* 车牌识别能力目前支持的地域Region有上海和深圳当您开通服务时选择的是上海地域推荐使用上海地域的 OSS 链接对于文件在本地或者非上海地域 OSS 链接的情况请参见文件 URL 处理当您开通服务时选择的是深圳地域时仅支持深圳地域 OSS 链接进行调用
*
* 示例值:
* http://viapi-test.oss-cn-shanghai.aliyuncs.com/viapi-3.0domepic/ocr/RecognizeLicensePlate/cpsb1.jpg
*
*
*/
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
/**
* 车牌识别百度OCR
*
* 支持识别中国大陆机动车蓝牌黄牌单双行绿牌大型新能源黄绿领使馆车牌警牌武警牌单双行
* 军牌单双行港澳出入境车牌农用车牌民航车牌非机动车车牌北京地区的地域编号和车牌号
* 并能同时识别图像中的多张车牌
*
* 百度文档https://console.bce.baidu.com/support/#/api?product=AI&project=文字识别&parent=交通场景OCR&api=rest%2F2.0%2Focr%2Fv1%2Flicense_plate&method=post
*/
@Slf4j
@RestController
@RequestMapping("/car/license/plate")
@ -41,41 +38,98 @@ import org.springframework.web.bind.annotation.RestController;
public class RecognizeLicensePlateController extends BaseController {
@Autowired
private ARecognizeLicensePlateHandle aRecognizeLicensePlateHandle;
private BLicensePlateHandle bLicensePlateHandle;
@PostMapping("/recognize")
public R recognize(ARecognizeLicensePlateRequest request) {
if (!hasImage(request)) {
return R.error("imageUrl和imageBase64不能同时为空");
public R recognize(@RequestBody LicensePlateRecognizeRequest request) {
if (request == null) {
return R.error("请求参数不能为空");
}
if (isBlank(request.getImageBase64()) && isBlank(request.getImageUrl())) {
return R.error("imageBase64和imageUrl不能同时为空");
}
ApiR<RecognizeLicensePlateResponse> aR = aRecognizeLicensePlateHandle.handle(request);
if (!isValidAliResponse(aR)) {
return R.error(thirdError(aR));
BLicensePlateRequest bRequest = toBaiduRequest(request);
ApiR<BLicensePlateResp> apiR = bLicensePlateHandle.handle(bRequest);
if (apiR == null || !apiR.isSuccess() || apiR.getData() == null) {
return R.error(thirdError(apiR));
}
RecognizeLicensePlateResponseBody.RecognizeLicensePlateResponseBodyData data =
aR.getData().getBody().getData();
if (CollectionUtils.isEmpty(data.getPlates())) {
BLicensePlateResp bResp = apiR.getData();
List<BLicensePlateResp.WordsResultDTO> plates = bResp.getWordsResult();
if (CollectionUtils.isEmpty(plates)) {
return R.error("未识别到车牌信息");
}
RecognizeLicensePlateResponseBody.RecognizeLicensePlateResponseBodyDataPlates plate =
data.getPlates().get(0);
// 取第一张车牌
BLicensePlateResp.WordsResultDTO plate = plates.get(0);
RecognizeLicensePlateResp resp = new RecognizeLicensePlateResp();
resp.setConfidence(plate.getConfidence());
resp.setPlateTypeConfidence(plate.getPlateTypeConfidence());
resp.setPlateType(plate.getPlateType());
resp.setPlateNumber(plate.getPlateNumber());
resp.setPlateNumber(plate.getNumber());
resp.setPlateType(mapPlateColor(plate.getColor()));
// 百度返回字符级置信度列表取最小值作为整体置信度保守策略平均值作为车牌类型置信度
List<Double> probabilities = plate.getProbability();
if (CollectionUtils.isNotEmpty(probabilities)) {
double minConf = probabilities.stream().mapToDouble(Double::doubleValue).min().orElse(0.0);
resp.setConfidence((float) minConf);
double avgConf = probabilities.stream().mapToDouble(Double::doubleValue).average().orElse(0.0);
resp.setPlateTypeConfidence((float) avgConf);
}
return R.ok().setData(resp);
}
private boolean isValidAliResponse(ApiR<RecognizeLicensePlateResponse> apiR) {
return apiR != null
&& apiR.isSuccess()
&& apiR.getData() != null
&& isSuccessStatusCode(apiR.getData().getStatusCode())
&& apiR.getData().getBody() != null
&& apiR.getData().getBody().getData() != null;
private BLicensePlateRequest toBaiduRequest(LicensePlateRecognizeRequest request) {
BLicensePlateRequest bRequest = new BLicensePlateRequest();
// base64编码后进行urlencode是百度API的要求因为base64的+/=在form-urlencoded中有特殊含义
if (isNotBlank(request.getImageBase64())) {
bRequest.setImageBase64(encodeUrl(request.getImageBase64()));
}
// imageUrl 中可能包含 & 等特殊字符需URL编码后再传给百度API否则会被解析为form分隔符
if (isNotBlank(request.getImageUrl())) {
bRequest.setImageUrl(encodeUrl(request.getImageUrl()));
}
// detectComplete和detectRisk在BLicensePlateRequest中无默认值handle的check()要求必须为true/false
bRequest.setDetectComplete("false");
bRequest.setDetectRisk("false");
return bRequest;
}
private String encodeUrl(String url) {
try {
return URLEncoder.encode(url, "UTF-8");
} catch (UnsupportedEncodingException e) {
log.error("URL编码失败:{}", url, e);
return url;
}
}
private boolean isNotBlank(String value) {
return value != null && value.trim().length() > 0;
}
/**
* 将百度车牌颜色映射为中文车牌类型
*/
private String mapPlateColor(String color) {
if (color == null) {
return null;
}
switch (color.toLowerCase()) {
case "blue":
return "小型汽车";
case "green":
return "新能源车";
case "yellow":
return "大型汽车";
case "white":
return "警车";
case "black":
return "港澳车";
default:
return color;
}
}
}

View File

@ -0,0 +1,27 @@
package com.heyu.api.request.car;
import lombok.Data;
/**
* 车牌识别请求参数百度OCR
*
* 支持识别中国大陆机动车蓝牌黄牌单双行绿牌大型新能源黄绿领使馆车牌警牌武警牌单双行
* 军牌单双行港澳出入境车牌农用车牌民航车牌
*/
@Data
public class LicensePlateRecognizeRequest {
/**
* 图像数据base64编码后进行urlencode要求base64编码和urlencode后大小不超过4M
* 支持jpg/jpeg/png/bmp格式
* 和url二选一
*/
private String imageBase64;
/**
* 图片完整URLURL长度不超过1024字节
* 和imageBase64二选一
*/
private String imageUrl;
}