修改gitee的版本

物流展会算法
LAPTOP-S9HJSOEB\昊天 4 weeks ago
parent 982af4127c
commit 213ab68aaf

@ -1,3 +1,5 @@
# LxCameraApi
蓝芯和hik工业摄像头java实现
蓝芯和hik工业摄像头java实现
采用opencvWechatQrcode 需要将D:\git\测试\lxCameraApi\src\main\resources\wechatqrcode放到指定位置

@ -36,24 +36,24 @@
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacpp</artifactId>
<version>1.5.7</version>
<version>1.5.11</version>
<classifier>windows-x86_64</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>openblas</artifactId>
<version>0.3.19-1.5.7</version>
<version>0.3.28-1.5.11</version>
<classifier>windows-x86_64</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>opencv</artifactId>
<version>4.5.5-1.5.7</version>
<version>4.10.0-1.5.11</version>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>opencv</artifactId>
<version>4.5.5-1.5.7</version>
<version>4.10.0-1.5.11</version>
<classifier>windows-x86_64</classifier>
</dependency>
<dependency>
@ -73,27 +73,17 @@
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacpp</artifactId>
<version>1.5.7</version>
<version>1.5.11</version>
<classifier>windows-x86_64</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>openblas</artifactId>
<version>0.3.19-1.5.7</version>
<classifier>windows-x86_64</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>opencv</artifactId>
<version>4.5.5-1.5.7</version>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>opencv</artifactId>
<version>4.5.5-1.5.7</version>
<version>0.3.28-1.5.11</version>
<classifier>windows-x86_64</classifier>
</dependency>
<dependency>
<groupId>com.microsoft.onnxruntime</groupId>
<artifactId>onnxruntime</artifactId>
@ -228,6 +218,23 @@
</resources>
</configuration>
</execution>
<!-- 复制wechatqrcode模型文件到不含中文的路径 -->
<execution>
<id>copy-wechatqrcode-models</id>
<phase>generate-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<!-- 复制到用户主目录下的模型文件夹,避免中文路径问题 -->
<outputDirectory>${user.home}/.lxCameraApi/models/wechatqrcode</outputDirectory>
<resources>
<resource>
<directory>${project.basedir}/src/main/resources/wechatqrcode</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>

@ -234,8 +234,9 @@ public class QtCodeController {
boxCountResponse.setSuccess(false);
return boxCountResponse;
}
// 默认3次
int count = request.getCount() != null ? request.getCount() : 1;
int count = request.getCount() != null ? request.getCount() : 3;
int intervalMs = request.getIntervalMs() != null ? request.getIntervalMs() : 500;
// 拍照并收集路径
@ -417,10 +418,25 @@ public class QtCodeController {
// System.out.println(deCode(img));
// }
private static String getModelPath(String filename) {
// 从classpath资源加载模型文件
String basePath = "wechatqrcode/";
try {
File resourceFile = new File(Thread.currentThread().getContextClassLoader().getResource(basePath + filename).toURI());
return resourceFile.getAbsolutePath();
} catch (Exception e) {
return basePath + filename;
}
}
private static String deCode(Mat img) {
// 微信二维码对象要返回二维码坐标前2个参数必传后2个在二维码小或不清晰时必传。
WeChatQRCode we = new WeChatQRCode();
// List<Mat> points = new ArrayList<Mat>();
// 微信二维码对象,需要传入模型文件路径
WeChatQRCode we = new WeChatQRCode(
getModelPath("detect.prototxt"),
getModelPath("detect.caffemodel"),
getModelPath("sr.prototxt"),
getModelPath("sr.caffemodel")
);
// 微信二维码引擎解码返回的valList中存放的是解码后的数据points中Mat存放的是二维码4个角的坐标
StringVector stringVector = we.detectAndDecode(img);
if (stringVector.empty()) {

@ -250,6 +250,12 @@ public class AsyncProcessingService {
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;
@ -257,6 +263,7 @@ public class AsyncProcessingService {
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);
}
// 解码
@ -265,8 +272,6 @@ public class AsyncProcessingService {
if (qrCodeContent != null) {
qrCodeResults.add(qrCodeContent);
validBoxes.add(box);
} else {
ImageIO.write(croppedImage, "jpg", new File(path + "_" + j + ".jpg"));
}
} catch (Exception e) {
log.error("第{}张图片解码异常box: {}", i + 1, box, e);
@ -401,54 +406,65 @@ public class AsyncProcessingService {
// 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.debug("灰度预处理失败: {}", e.getMessage());
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.debug("CLAHE预处理失败: {}", e.getMessage());
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.debug("二值化预处理失败: {}", e.getMessage());
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.debug("组合预处理失败: {}", e.getMessage());
log.error("组合预处理失败: type={}, msg={}", e.getClass().getName(), e.getMessage(), e);
}
return null;
@ -509,7 +525,7 @@ public class AsyncProcessingService {
*
*/
private BufferedImage resizeImage(BufferedImage originalImage, int targetWidth, int targetHeight) {
BufferedImage resizedImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB);
BufferedImage resizedImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_3BYTE_BGR);
Graphics2D g2d = resizedImage.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.drawImage(originalImage, 0, 0, targetWidth, targetHeight, null);

@ -21,6 +21,7 @@ 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;
/**
*
@ -28,7 +29,7 @@ import static org.opencv.core.CvType.CV_8UC3;
public class WeChatDeCode {
public static void main(String... args) {
String imagePath = "D:\\data\\media\\2026-04-17\\1\\1776408379598.png_0.jpg";
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));
@ -167,9 +168,59 @@ public class WeChatDeCode {
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) {
// 微信二维码对象要返回二维码坐标前2个参数必传后2个在二维码小或不清晰时必传。
WeChatQRCode we = new WeChatQRCode();
// 获取模型文件路径
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 存储坐标点
org.bytedeco.opencv.opencv_core.MatVector points = new org.bytedeco.opencv.opencv_core.MatVector();

@ -25,7 +25,7 @@ public class MultiImageRequest {
/**
* 1
*/
private Integer count = 1;
private Integer count = 3;
/**
* (ms)500

@ -11,7 +11,9 @@ import org.springframework.stereotype.Service;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferInt;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@ -233,15 +235,17 @@ public class OpencvService {
* BufferedImage Mat
*/
public Mat bufferedImageToMat(BufferedImage image) {
if (image.getType() == BufferedImage.TYPE_INT_RGB) {
return matify(image);
// 统一转换为 TYPE_3BYTE_BGR 以避免 DataBufferInt 问题
BufferedImage bgrImage;
if (image.getType() == BufferedImage.TYPE_3BYTE_BGR) {
bgrImage = image;
} else {
BufferedImage rgbImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics2D g = rgbImage.createGraphics();
bgrImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
Graphics2D g = bgrImage.createGraphics();
g.drawImage(image, 0, 0, null);
g.dispose();
return matify(rgbImage);
}
return matify(bgrImage);
}
private Mat matify(BufferedImage image) {
@ -255,23 +259,47 @@ public class OpencvService {
* Mat BufferedImage
*/
public BufferedImage matToBufferedImage(Mat matrix) {
int type = BufferedImage.TYPE_BYTE_GRAY;
if (matrix.channels() > 1) {
type = BufferedImage.TYPE_3BYTE_BGR;
// 16位图像转8位
Mat byteMat = matrix;
if (matrix.depth() == CvType.CV_16U || matrix.depth() == CvType.CV_16S) {
Mat normalized = new Mat();
Core.normalize(matrix, normalized, 0, 255, Core.NORM_MINMAX, CvType.CV_8UC(matrix.channels()));
byteMat = normalized;
}
int bufferSize = matrix.channels() * matrix.cols() * matrix.rows();
byte[] buffer = new byte[bufferSize];
matrix.get(0, 0, buffer);
BufferedImage image = new BufferedImage(matrix.cols(), matrix.rows(), type);
final byte[] targetPixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
System.arraycopy(buffer, 0, targetPixels, 0, buffer.length);
// 转换为 BGR 或灰度格式
Mat bgrMat = new Mat();
if (byteMat.channels() > 1) {
Imgproc.cvtColor(byteMat, bgrMat, Imgproc.COLOR_BGR2RGB);
} else {
Imgproc.cvtColor(byteMat, bgrMat, Imgproc.COLOR_GRAY2RGB);
}
if (byteMat != matrix) {
byteMat.release();
}
// 使用 JavaCV/ImageIO 方式转换
int width = bgrMat.cols();
int height = bgrMat.rows();
int channels = bgrMat.channels();
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
byte[] pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
bgrMat.get(0, 0, pixels);
bgrMat.release();
return image;
}
/**
* -
* -
*/
public Mat toGrayscale(Mat src) {
// 如果已经是灰度图,直接返回副本
if (src.channels() == 1) {
return src.clone();
}
Mat gray = new Mat();
Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY);
return gray;
@ -304,16 +332,11 @@ public class OpencvService {
* -
*/
public Mat sharpen(Mat src) {
Mat kernel = new Mat(3, 3, CvType.CV_32F);
float[] data = {
0, -1, 0,
-1, 5, -1,
0, -1, 0
};
kernel.put(0, 0, data);
Mat gray = toGrayscale(src);
Mat sharpened = new Mat();
Imgproc.filter2D(src, sharpened, -1, kernel);
kernel.release();
Imgproc.Laplacian(gray, sharpened, CvType.CV_8U, 3);
Core.convertScaleAbs(sharpened, sharpened);
gray.release();
return sharpened;
}
@ -339,6 +362,7 @@ public class OpencvService {
* - CLAHE
*/
public Mat clahe(Mat src) {
// toGrayscale已自动处理单通道图
Mat gray = toGrayscale(src);
CLAHE clahe = Imgproc.createCLAHE(2.0, new Size(8, 8));
Mat result = new Mat();

Loading…
Cancel
Save