将图片改为识别的图片

采用微信二维码解码,增加成功率
将图片改为联网图片
物流展会算法
LAPTOP-S9HJSOEB\昊天 3 weeks ago
parent 213ab68aaf
commit d371fb5d1a

@ -55,7 +55,7 @@ public class CategoryController {
String path = appConfig.getPicPath() + formattedDate + "/" + request.getCameraId() + "/" + formattedTime + ".png";
String url = appConfig.getPicUrl() + formattedDate + "/" + request.getCameraId() + "/" + formattedTime + ".png";
String url = appConfig.getPicUrl() + formattedDate + "/" + request.getCameraId() + "/" + formattedTime + ".png"+"detect.jpg";
log.info("开始拍照IP: {}, 路径: {}", request.getCameraId(), path);
String ip = null;
for (AppConfig.Camera camera : appConfig.getHikCamera()) {

@ -283,7 +283,7 @@ public class QtCodeController {
boxCountResponse.setSuccess(true);
boxCountResponse.setResult("Processing");
boxCountResponse.setImagePath(imageUrls.get(0));
boxCountResponse.setImagePath(imageUrls.get(0)+ ".jpg");
boxCountResponse.setTaskId(request.getTaskId());
boxCountResponse.setScanType(request.getScanType());
boxCountResponse.setQrCodeResults(imageUrls);

@ -213,7 +213,10 @@ public class AsyncProcessingService {
/**
*
*
*
* 1. ONNX
* 2. NMS
* 3.
*/
@Async
public void processMultiQrCodeAsync(Integer cameraId, String taskId, List<String> imagePaths,
@ -222,71 +225,107 @@ public class AsyncProcessingService {
try {
log.info("异步多图片识别任务开始,图片数量: {}", imagePaths.size());
// 收集所有图片的二维码结果
List<String> allQrCodeResults = new ArrayList<>();
List<String> allImagePaths = new ArrayList<>();
// ====== Phase 1: 批量检测,收集所有候选框 ======
List<BoundingBox> allDetections = new ArrayList<>();
// 缓存所有图片的 BufferedImage避免重复读取
List<BufferedImage> cachedImages = new ArrayList<>();
for (int i = 0; i < imagePaths.size(); i++) {
String path = imagePaths.get(i);
String url = imageUrls.get(i);
try {
log.info("开始识别第{}/{}张图片: {}", i + 1, imagePaths.size(), path);
log.info("Phase1 - 检测第{}/{}张图片: {}", i + 1, imagePaths.size(), path);
List<BoundingBox> detectResults = onnxServiceNew.detect26(path, "qrCode");
log.info("第{}张图片检测结果: {}", i + 1, detectResults);
// 读取原始图片
BufferedImage originalImage = ImageIO.read(new File(path));
if (originalImage == null) {
log.warn("无法读取图片: {}", path);
continue;
log.info("检测结果: {} 个框", detectResults.size());
allDetections.addAll(detectResults);
// 预加载图片到内存
BufferedImage image = ImageIO.read(new File(path));
if (image != null) {
cachedImages.add(image);
} else {
log.warn("无法读取图片: {}, 用null占位", path);
cachedImages.add(null);
}
} catch (Exception e) {
log.error("第{}张图片检测或加载异常: {}", i + 1, path, e);
cachedImages.add(null);
}
}
log.info("Phase1 完成,共检测到 {} 个候选框", allDetections.size());
if (allDetections.isEmpty()) {
log.warn("所有图片均未检测到二维码候选框,发送 Unknown 结果");
sendQrCodeResult(targetIp, targetPort, targetPath, taskId, "Unknown",
imageUrls.get(0) + ".jpg", new ArrayList<>(), scanType);
return;
}
List<String> qrCodeResults = new ArrayList<>();
List<BoundingBox> validBoxes = new ArrayList<>();
for (int j = 0; j < detectResults.size(); j++) {
BoundingBox box = detectResults.get(j);
try {
BufferedImage croppedImage = cropBoundingBox(originalImage, box, 30);
// 保存裁剪的原始图片用于调试
String debugPath = path + "_crop_" + i + "_" + j + ".jpg";
ImageIO.write(croppedImage, "jpg", new File(debugPath));
log.info("DEBUG: 裁剪图片保存到: {}, 尺寸: {}x{}, 置信度: {}",
debugPath, croppedImage.getWidth(), croppedImage.getHeight(), box.getConfidence());
// 最小尺寸放大
int minSize = 320;
if (croppedImage.getWidth() < minSize || croppedImage.getHeight() < minSize) {
int newWidth = Math.max(croppedImage.getWidth(), minSize);
int newHeight = Math.max(croppedImage.getHeight(), minSize);
croppedImage = resizeImage(croppedImage, newWidth, newHeight);
log.info("DEBUG: 图片已放大到: {}x{}", newWidth, newHeight);
}
// 解码
String qrCodeContent = decodeQrCodeWithRetry(croppedImage, box.getConfidence(), 2);
if (qrCodeContent != null) {
qrCodeResults.add(qrCodeContent);
validBoxes.add(box);
}
} catch (Exception e) {
log.error("第{}张图片解码异常box: {}", i + 1, box, e);
// ====== Phase 2: NMS 过滤重叠框 ======
List<BoundingBox> filteredBoxes = ONNXServiceNew.nonMaxSuppression(allDetections, 0.5f);
log.info("Phase2 NMS完成: {} -> {} 个框", allDetections.size(), filteredBoxes.size());
// ====== Phase 3: 逐框跨图解码 ======
List<String> allQrCodeResults = new ArrayList<>();
List<BoundingBox> validBoxes = new ArrayList<>();
// 用第一张图片的 url 作为标注图
String annotatedImageUrl = imageUrls.get(0) + ".jpg";
for (int boxIdx = 0; boxIdx < filteredBoxes.size(); boxIdx++) {
BoundingBox box = filteredBoxes.get(boxIdx);
boolean decoded = false;
// 遍历每张图片尝试解码当前框
for (int imgIdx = 0; imgIdx < cachedImages.size(); imgIdx++) {
BufferedImage image = cachedImages.get(imgIdx);
if (image == null) continue;
try {
BufferedImage cropped = cropBoundingBox(image, box, 30);
// 保存裁剪图用于调试(仅首张成功前)
if (!decoded) {
String debugPath = imagePaths.get(imgIdx) + "_crop_box" + boxIdx + ".jpg";
ImageIO.write(cropped, "jpg", new File(debugPath));
log.debug("裁剪图保存: {} (来源: 图{})", debugPath, imgIdx + 1);
}
}
// 绘制检测框
opencvService.drawBoundingBoxesOnImage(validBoxes, path, path + ".jpg");
// 最小尺寸放大
int minSize = 320;
if (cropped.getWidth() < minSize || cropped.getHeight() < minSize) {
int newWidth = Math.max(cropped.getWidth(), minSize);
int newHeight = Math.max(cropped.getHeight(), minSize);
cropped = resizeImage(cropped, newWidth, newHeight);
}
String qrCodeContent = decodeQrCodeWithRetry(cropped, box.getConfidence(), 2);
// 收集结果
allQrCodeResults.addAll(qrCodeResults);
allImagePaths.add(url + ".jpg");
if (qrCodeContent != null) {
log.info("框{} 解码成功 (来源图{}): {}", boxIdx, imgIdx + 1, qrCodeContent);
allQrCodeResults.add(qrCodeContent);
validBoxes.add(box);
box.setName(qrCodeContent);
decoded = true;
break; // 本框成功,试下一个框
}
} catch (Exception e) {
log.warn("框{} 图{} 解码异常: {}", boxIdx, imgIdx + 1, e.getMessage());
}
}
if (!decoded) {
log.warn("框{} 所有图片均解码失败, 坐标: ({}, {}), 尺寸: {}x{}, 置信度: {}",
boxIdx, (int) box.getX(), (int) box.getY(), (int) box.getW(), (int) box.getH(), box.getConfidence());
}
}
// 在第一张图片上绘制所有检测框
if (!cachedImages.isEmpty() && cachedImages.get(0) != null) {
try {
String annotatePath = imagePaths.get(0);
opencvService.drawBoundingBoxesOnImage(validBoxes, annotatePath, annotatePath + ".jpg");
} catch (Exception e) {
log.error("第{}张图片处理异常: {}", i + 1, path, e);
log.warn("绘制检测框失败", e);
}
}
@ -301,18 +340,25 @@ public class AsyncProcessingService {
}
// 发送HTTP通知
if (targetIp != null && targetPort != null && targetPath != null) {
httpNotifyService.sendQrCodeResult(targetIp, targetPort, targetPath,
taskId, result, String.join(",", allImagePaths), allQrCodeResults, scanType);
} else {
log.warn("多图片异步任务完成,但未配置目标服务器信息");
}
sendQrCodeResult(targetIp, targetPort, targetPath,
taskId, result, annotatedImageUrl, allQrCodeResults, scanType);
} catch (Exception e) {
log.error("多图片异步任务执行异常", e);
}
}
private void sendQrCodeResult(String targetIp, Integer targetPort, String targetPath,
String taskId, String result, String imagePath,
List<String> qrCodeResults, String scanType) {
if (targetIp != null && targetPort != null && targetPath != null) {
httpNotifyService.sendQrCodeResult(targetIp, targetPort, targetPath,
taskId, result, imagePath, qrCodeResults, scanType);
} else {
log.warn("多图片异步任务完成,但未配置目标服务器信息");
}
}
public static void main(String[] args) {
String path = "D:\\data\\media\\2026-05-15\\1\\2026-05-15-00-07-20-909.png_3.jpg";
BufferedImage croppedImage = null;
@ -405,67 +451,67 @@ public class AsyncProcessingService {
if (result != null) return result;
// 2. 尝试灰度化
try {
log.info("DEBUG: 灰度化开始, image类型={}, 尺寸={}x{}", image.getType(), image.getWidth(), image.getHeight());
Mat mat = opencvService.bufferedImageToMat(image);
log.info("DEBUG: bufferedImageToMat完成, mat类型={}, channels={}, depth={}",
mat.type(), mat.channels(), mat.depth());
Mat gray = opencvService.toGrayscale(mat);
log.info("DEBUG: toGrayscale完成, gray channels={}, depth={}", gray.channels(), gray.depth());
BufferedImage grayImage = opencvService.matToBufferedImage(gray);
log.info("DEBUG: matToBufferedImage完成, grayImage类型={}", grayImage.getType());
mat.release();
gray.release();
result = tryDecode(grayImage, confidence, retryIndex, "灰度");
if (result != null) return result;
} catch (Exception e) {
log.error("灰度预处理失败: type={}, msg={}", e.getClass().getName(), e.getMessage(), e);
}
// 3. 尝试CLAHE增强
try {
log.info("DEBUG: CLAHE开始");
Mat mat = opencvService.bufferedImageToMat(image);
Mat clahe = opencvService.clahe(mat);
log.info("DEBUG: clahe完成, clahe channels={}, depth={}", clahe.channels(), clahe.depth());
BufferedImage claheImage = opencvService.matToBufferedImage(clahe);
mat.release();
clahe.release();
result = tryDecode(claheImage, confidence, retryIndex, "CLAHE");
if (result != null) return result;
} catch (Exception e) {
log.error("CLAHE预处理失败: type={}, msg={}", e.getClass().getName(), e.getMessage(), e);
}
// 4. 尝试二值化
try {
log.info("DEBUG: 二值化开始");
Mat mat = opencvService.bufferedImageToMat(image);
Mat binary = opencvService.adaptiveThreshold(mat);
log.info("DEBUG: adaptiveThreshold完成, binary channels={}, depth={}", binary.channels(), binary.depth());
BufferedImage binaryImage = opencvService.matToBufferedImage(binary);
mat.release();
binary.release();
result = tryDecode(binaryImage, confidence, retryIndex, "二值化");
if (result != null) return result;
} catch (Exception e) {
log.error("二值化预处理失败: type={}, msg={}", e.getClass().getName(), e.getMessage(), e);
}
// 5. 尝试组合预处理(去噪+灰度+CLAHE+二值化)
try {
log.info("DEBUG: 组合预处理开始");
Mat mat = opencvService.bufferedImageToMat(image);
Mat processed = opencvService.preprocessForQrCode(mat);
log.info("DEBUG: preprocessForQrCode完成, processed channels={}, depth={}", processed.channels(), processed.depth());
BufferedImage processedImage = opencvService.matToBufferedImage(processed);
mat.release();
processed.release();
result = tryDecode(processedImage, confidence, retryIndex, "组合预处理");
if (result != null) return result;
} catch (Exception e) {
log.error("组合预处理失败: type={}, msg={}", e.getClass().getName(), e.getMessage(), e);
}
// try {
// log.info("DEBUG: 灰度化开始, image类型={}, 尺寸={}x{}", image.getType(), image.getWidth(), image.getHeight());
// Mat mat = opencvService.bufferedImageToMat(image);
// log.info("DEBUG: bufferedImageToMat完成, mat类型={}, channels={}, depth={}",
// mat.type(), mat.channels(), mat.depth());
// Mat gray = opencvService.toGrayscale(mat);
// log.info("DEBUG: toGrayscale完成, gray channels={}, depth={}", gray.channels(), gray.depth());
// BufferedImage grayImage = opencvService.matToBufferedImage(gray);
// log.info("DEBUG: matToBufferedImage完成, grayImage类型={}", grayImage.getType());
// mat.release();
// gray.release();
// result = tryDecode(grayImage, confidence, retryIndex, "灰度");
// if (result != null) return result;
// } catch (Exception e) {
// log.error("灰度预处理失败: type={}, msg={}", e.getClass().getName(), e.getMessage(), e);
// }
//
// // 3. 尝试CLAHE增强
// try {
// log.info("DEBUG: CLAHE开始");
// Mat mat = opencvService.bufferedImageToMat(image);
// Mat clahe = opencvService.clahe(mat);
// log.info("DEBUG: clahe完成, clahe channels={}, depth={}", clahe.channels(), clahe.depth());
// BufferedImage claheImage = opencvService.matToBufferedImage(clahe);
// mat.release();
// clahe.release();
// result = tryDecode(claheImage, confidence, retryIndex, "CLAHE");
// if (result != null) return result;
// } catch (Exception e) {
// log.error("CLAHE预处理失败: type={}, msg={}", e.getClass().getName(), e.getMessage(), e);
// }
//
// // 4. 尝试二值化
// try {
// log.info("DEBUG: 二值化开始");
// Mat mat = opencvService.bufferedImageToMat(image);
// Mat binary = opencvService.adaptiveThreshold(mat);
// log.info("DEBUG: adaptiveThreshold完成, binary channels={}, depth={}", binary.channels(), binary.depth());
// BufferedImage binaryImage = opencvService.matToBufferedImage(binary);
// mat.release();
// binary.release();
// result = tryDecode(binaryImage, confidence, retryIndex, "二值化");
// if (result != null) return result;
// } catch (Exception e) {
// log.error("二值化预处理失败: type={}, msg={}", e.getClass().getName(), e.getMessage(), e);
// }
//
// // 5. 尝试组合预处理(去噪+灰度+CLAHE+二值化)
// try {
// log.info("DEBUG: 组合预处理开始");
// Mat mat = opencvService.bufferedImageToMat(image);
// Mat processed = opencvService.preprocessForQrCode(mat);
// log.info("DEBUG: preprocessForQrCode完成, processed channels={}, depth={}", processed.channels(), processed.depth());
// BufferedImage processedImage = opencvService.matToBufferedImage(processed);
// mat.release();
// processed.release();
// result = tryDecode(processedImage, confidence, retryIndex, "组合预处理");
// if (result != null) return result;
// } catch (Exception e) {
// log.error("组合预处理失败: type={}, msg={}", e.getClass().getName(), e.getMessage(), e);
// }
return null;
}

@ -21,49 +21,75 @@ import javax.imageio.ImageIO;
import static org.bytedeco.opencv.global.opencv_imgcodecs.imread;
import static org.opencv.core.CvType.CV_8UC3;
import org.bytedeco.javacpp.Loader;
/**
*
*
*/
public class WeChatDeCode {
// ============ 单例模型缓存 ============
private static volatile WeChatQRCode instance;
private static synchronized WeChatQRCode getWeChatQRCode() {
if (instance == null) {
String modelBasePath = "D:/ccd/wechatqrcode/";
// 优先使用 _2021nov 版本
String detectPrototxt = findModel(modelBasePath, "detect_2021nov.prototxt", "detect.prototxt");
String detectCaffemodel = findModel(modelBasePath, "detect_2021nov.caffemodel", "detect.caffemodel");
String srPrototxt = findModel(modelBasePath, "sr_2021nov.prototxt", "sr.prototxt");
String srCaffemodel = findModel(modelBasePath, "sr_2021nov.caffemodel", "sr.caffemodel");
validateModel(detectPrototxt, "detect prototxt");
validateModel(detectCaffemodel, "detect caffemodel");
validateModel(srPrototxt, "sr prototxt");
validateModel(srCaffemodel, "sr caffemodel");
instance = new WeChatQRCode(detectPrototxt, detectCaffemodel, srPrototxt, srCaffemodel);
System.out.println("WeChatQRCode 模型加载成功(单例)");
}
return instance;
}
private static String findModel(String basePath, String preferred, String fallback) {
String path = basePath + preferred;
if (new File(path).exists()) return path;
return basePath + fallback;
}
private static void validateModel(String path, String name) {
if (!new File(path).exists()) {
throw new RuntimeException("WeChatQRCode 模型文件不存在: " + name + " (" + path + ")");
}
}
// ============ 单例模型缓存 end ============
public static void main(String... args) {
String imagePath = "D:\\data\\media\\2026-05-20\\1\\2026-05-20-12-20-05-578_1.png_crop_1_6.jpg";
Mat img = imread(imagePath);
System.out.println(deCode(img));
//下载二维码到本地识别
// String url="";
// System.out.println(openPic(url));
}
public static String openPic(String url) {
String savePath = "d:\\img";
String filename = UUID.randomUUID().toString() + ".jpg";
String re="0";
String re = "0";
try {
dxz(url, savePath, filename);
Mat img = imread(savePath + "\\" + filename);
re=deCode(img);
//如果识别失败那么尝试放大缩小图片尝试识别,提高准确率
if(re.equals("0")) {
re = deCode(img);
if (re.equals("0")) {
ys(savePath, filename);
Mat img1 = imread(savePath + "\\1_" + filename);
re=deCode(img1);
re = deCode(img1);
}
if(re.equals("0")) {
if (re.equals("0")) {
fd(savePath, filename);
Mat img2 = imread(savePath + "\\2_" + filename);
re=deCode(img2);
re = deCode(img2);
}
return re;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return "0";
}
@ -71,13 +97,10 @@ public class WeChatDeCode {
private static void ys(String savePath, String filename) throws Exception {
try {
// 读取原始图片
BufferedImage image = ImageIO.read(new FileInputStream(savePath + "\\" + filename));
System.out.println("Width: " + image.getWidth());
System.out.println("Height: " + image.getHeight());
// 调整图片大小
BufferedImage newImage = resizeImage(image, image.getWidth()*2, image.getHeight()*2);
// 图像缓冲区图片保存为图片文件(文件不存在会自动创建文件保存,文件存在会覆盖原文件保存)
BufferedImage newImage = resizeImage(image, image.getWidth() * 2, image.getHeight() * 2);
ImageIO.write(newImage, "jpg", new File(savePath + "\\1_" + filename));
} catch (Exception e) {
e.printStackTrace();
@ -86,13 +109,10 @@ public class WeChatDeCode {
private static void fd(String savePath, String filename) throws Exception {
try {
// 读取原始图片
BufferedImage image = ImageIO.read(new FileInputStream(savePath + "\\" + filename));
System.out.println("Width: " + image.getWidth());
System.out.println("Height: " + image.getHeight());
// 调整图片大小
BufferedImage newImage = resizeImage(image, 200, 200);
// 图像缓冲区图片保存为图片文件(文件不存在会自动创建文件保存,文件存在会覆盖原文件保存)
ImageIO.write(newImage, "jpg", new File(savePath + "\\2_" + filename));
} catch (Exception e) {
e.printStackTrace();
@ -100,24 +120,17 @@ public class WeChatDeCode {
}
private static void dxz(String urlString, String savePath, String filename) throws Exception {
// 构造URL
URL url = new URL(urlString);
// 打开连接
URLConnection con = url.openConnection();
// 设置请求超时为20s
con.setConnectTimeout(20 * 1000);
// 文件路径不存在 则创建
File sf = new File(savePath);
if (!sf.exists()) {
sf.mkdirs();
}
// jdk 1.7 新特性自动关闭
try (InputStream in = con.getInputStream();
OutputStream out = new FileOutputStream(sf.getPath() + "\\" + filename)) {
// 创建缓冲区
byte[] buff = new byte[1024];
int n;
// 开始读取
while ((n = in.read(buff)) >= 0) {
out.write(buff, 0, n);
}
@ -125,6 +138,7 @@ public class WeChatDeCode {
e.printStackTrace();
}
}
public static String deCode(BufferedImage croppedImage) {
Mat img = bufferedImageToMat(croppedImage);
try {
@ -140,146 +154,52 @@ public class WeChatDeCode {
if (image == null) {
return null;
}
int width = image.getWidth();
int height = image.getHeight();
// 创建 Mat 对象
Mat mat = new Mat(height, width, CV_8UC3);
// 获取像素数据
int[] pixels = new int[width * height];
image.getRGB(0, 0, width, height, pixels, 0, width);
// 将像素数据复制到 Mat
byte[] data = new byte[width * height * 3];
for (int i = 0; i < width * height; i++) {
int pixel = pixels[i];
int r = (pixel >> 16) & 0xFF;
int g = (pixel >> 8) & 0xFF;
int b = pixel & 0xFF;
// OpenCV 使用 BGR 格式
data[i * 3] = (byte) b;
data[i * 3 + 1] = (byte) g;
data[i * 3 + 2] = (byte) r;
}
mat.data().put(data);
return mat;
}
private static String getModelPath(String filename) {
// 对于 detect 和 sr 文件,优先尝试 _2021nov 版本
String altFilename = filename
.replace("detect.", "detect_2021nov.")
.replace("sr.", "sr_2021nov.");
// 模型文件路径(不含中文)
String modelBasePath = "D:/ccd/wechatqrcode/";
File altFile = new File(modelBasePath + altFilename);
File origFile = new File(modelBasePath + filename);
if (altFile.exists()) {
System.out.println("Loading model: " + altFile.getAbsolutePath());
return altFile.getAbsolutePath();
} else if (origFile.exists()) {
System.out.println("Loading model: " + origFile.getAbsolutePath());
return origFile.getAbsolutePath();
}
System.out.println("Model file not found: " + modelBasePath + altFilename);
return modelBasePath + altFilename;
}
public static String deCode(Mat img) {
// 获取模型文件路径
String detectPrototxt = getModelPath("detect_2021nov.prototxt");
String detectCaffemodel = getModelPath("detect_2021nov.caffemodel");
String srPrototxt = getModelPath("sr_2021nov.prototxt");
String srCaffemodel = getModelPath("sr_2021nov.caffemodel");
// 验证所有文件都存在
if (!new File(detectPrototxt).exists()) {
throw new RuntimeException("文件不存在: " + detectPrototxt);
}
if (!new File(detectCaffemodel).exists()) {
throw new RuntimeException("文件不存在: " + detectCaffemodel);
}
if (!new File(srPrototxt).exists()) {
throw new RuntimeException("文件不存在: " + srPrototxt);
}
if (!new File(srCaffemodel).exists()) {
throw new RuntimeException("文件不存在: " + srCaffemodel);
}
System.out.println("=== 模型文件路径 ===");
System.out.println("detectPrototxt: " + detectPrototxt + " (" + new File(detectPrototxt).length() + " bytes)");
System.out.println("detectCaffemodel: " + detectCaffemodel + " (" + new File(detectCaffemodel).length() + " bytes)");
System.out.println("srPrototxt: " + srPrototxt + " (" + new File(srPrototxt).length() + " bytes)");
System.out.println("srCaffemodel: " + srCaffemodel + " (" + new File(srCaffemodel).length() + " bytes)");
System.out.println("====================");
// 微信二维码对象,需要传入模型文件路径
WeChatQRCode we = new WeChatQRCode(detectPrototxt, detectCaffemodel, srPrototxt, srCaffemodel);
// 使用 MatVector 存储坐标点
WeChatQRCode we = getWeChatQRCode();
org.bytedeco.opencv.opencv_core.MatVector points = new org.bytedeco.opencv.opencv_core.MatVector();
// 微信二维码引擎解码返回的valList中存放的是解码后的数据points中存放的是二维码4个角的坐标
StringVector stringVector = we.detectAndDecode(img, points);
if (stringVector.empty()) {
System.out.println("未识别到二维码");
return "0";
}
// 获取第一个二维码内容,并去掉末尾的 null 字符
String qrContent = stringVector.get(0).getString(StandardCharsets.UTF_8);
// 去掉末尾的 null 字符
if (qrContent != null) {
qrContent = qrContent.replaceAll("\\x00+$", "");
}
// 输出所有识别到的二维码信息
System.out.println("========== 识别结果汇总 ==========");
System.out.println("共识别到 " + stringVector.size() + " 个二维码");
System.out.println("二维码 1: " + qrContent);
// 输出第一个二维码四个角的坐标
if (points.size() > 0) {
Mat pointMat = points.get(0);
System.out.println(" 坐标位置:");
for (int j = 0; j < pointMat.rows(); j++) {
float x = pointMat.ptr(j).getFloat(0);
float y = pointMat.ptr(j).getFloat(4);
System.out.println(" 角" + (j + 1) + ": (" + x + ", " + y + ")");
}
}
System.out.println("==================================");
return qrContent;
}
/**
*
* @param originalImage
* @param targetWidth
* @param targetHeight
* @return
*/
private static BufferedImage resizeImage(BufferedImage originalImage, int targetWidth, int targetHeight) {
BufferedImage resizedImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB);
java.awt.Graphics2D g2d = resizedImage.createGraphics();
// 设置渲染质量
g2d.setRenderingHint(java.awt.RenderingHints.KEY_INTERPOLATION, java.awt.RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(java.awt.RenderingHints.KEY_RENDERING, java.awt.RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(java.awt.RenderingHints.KEY_ANTIALIASING, java.awt.RenderingHints.VALUE_ANTIALIAS_ON);
// 绘制缩放后的图片
g2d.drawImage(originalImage, 0, 0, targetWidth, targetHeight, null);
g2d.dispose();
return resizedImage;
}

@ -31,6 +31,12 @@ public class ImageCaptureService {
// SDK初始化状态
private boolean isSdkInitialized = false;
// 相机重连最大重试次数
private static final int RECONNECT_MAX_RETRIES = 3;
// 相机重连间隔(ms)
private static final int RECONNECT_INTERVAL = 3000;
@Resource
private AppConfig appConfig;
/**
@ -242,6 +248,17 @@ public class ImageCaptureService {
* @return
*/
public boolean captureImage(String ip, String savePath) {
return captureImage(ip, savePath, false);
}
/**
* IP
* @param ip IP
* @param savePath
* @param isRetry
* @return
*/
private boolean captureImage(String ip, String savePath, boolean isRetry) {
if (!isSdkInitialized) {
log.error("SDK未初始化");
return false;
@ -251,6 +268,13 @@ public class ImageCaptureService {
Handle hCamera = handleMap.get(ip);
if (hCamera == null) {
log.error("相机 {} 未初始化请先调用init方法", ip);
// 未初始化时也尝试重连
if (!isRetry) {
Handle newHandle = reconnectCamera(ip);
if (newHandle != null) {
return captureImage(ip, savePath, true);
}
}
return false;
}
@ -262,6 +286,12 @@ public class ImageCaptureService {
int nRet = MvCameraControl.MV_CC_GetIntValue(hCamera, "PayloadSize", stParam);
if (MV_OK != nRet) {
log.error("获取PayloadSize失败errcode: [0x{}]", Integer.toHexString(nRet));
if (!isRetry) {
Handle newHandle = reconnectCamera(ip);
if (newHandle != null) {
return captureImage(ip, savePath, true);
}
}
return false;
}
@ -272,6 +302,12 @@ public class ImageCaptureService {
if (MV_OK != nRet) {
log.error("获取图像失败errcode: [0x{}]", Integer.toHexString(nRet));
if (!isRetry) {
Handle newHandle = reconnectCamera(ip);
if (newHandle != null) {
return captureImage(ip, savePath, true);
}
}
return false;
}
@ -283,6 +319,12 @@ public class ImageCaptureService {
} catch (Exception e) {
log.error("拍照异常", e);
if (!isRetry) {
Handle newHandle = reconnectCamera(ip);
if (newHandle != null) {
return captureImage(ip, savePath, true);
}
}
return false;
}
}
@ -405,6 +447,69 @@ public class ImageCaptureService {
return handleMap.containsKey(ip);
}
/**
* map
*/
private void destroyCameraHandle(String ip) {
Handle hCamera = handleMap.remove(ip);
if (hCamera == null) return;
try {
MvCameraControl.MV_CC_StopGrabbing(hCamera);
} catch (Exception e) {
log.warn("相机 {} 停止抓图异常", ip, e);
}
try {
MvCameraControl.MV_CC_CloseDevice(hCamera);
} catch (Exception e) {
log.warn("相机 {} 关闭设备异常", ip, e);
}
try {
MvCameraControl.MV_CC_DestroyHandle(hCamera);
} catch (Exception e) {
log.warn("相机 {} 销毁句柄异常", ip, e);
}
log.info("相机 {} 句柄已销毁", ip);
}
/**
*
* @return null
*/
private Handle reconnectCamera(String ip) {
log.warn("相机 {} 开始重连...", ip);
destroyCameraHandle(ip);
for (int i = 1; i <= RECONNECT_MAX_RETRIES; i++) {
try {
// 重新枚举设备
ArrayList<MV_CC_DEVICE_INFO> stDeviceList = MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE);
if (stDeviceList == null || stDeviceList.isEmpty()) {
log.warn("相机 {} 重连失败(第{}/{}次),未枚举到设备", ip, i, RECONNECT_MAX_RETRIES);
} else {
Handle newHandle = initSingleCamera(ip, stDeviceList);
if (newHandle != null) {
log.info("相机 {} 重连成功(第{}次尝试)", ip, i);
return newHandle;
}
log.warn("相机 {} 重连失败(第{}/{}次),初始化句柄失败", ip, i, RECONNECT_MAX_RETRIES);
}
} catch (Exception e) {
log.warn("相机 {} 重连异常(第{}/{}次)", ip, i, RECONNECT_MAX_RETRIES, e);
}
if (i < RECONNECT_MAX_RETRIES) {
try {
Thread.sleep(RECONNECT_INTERVAL);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
log.error("相机 {} 重连失败,已达最大重试次数 {}", ip, RECONNECT_MAX_RETRIES);
return null;
}
/**
*
* @param ip IP

@ -52,7 +52,7 @@ deleteFile:
- "D://go/deleteFile"
day: 7
picPath: "D:/data/media/"
picUrl: "http://127.0.0.1:9012/pic/"
picUrl: "http://192.168.100.110:9012/pic/"
serve:
ip: 127.0.0.1
@ -76,6 +76,6 @@ yoloModelConfig:
# 图片大小
imgSize: 2048
# 置信度
confThreshold: 0.90
confThreshold: 0.9
names: ['code']

Loading…
Cancel
Save