修改截图逻辑 加入模型测试使用

This commit is contained in:
2025-09-27 19:37:24 +08:00
parent 7527017070
commit 086553b081
4 changed files with 40 additions and 42 deletions

View File

@@ -38,6 +38,7 @@
<version>1.5.10</version> <version>1.5.10</version>
</dependency> </dependency>
<!-- 解析 models.json--> <!-- 解析 models.json-->
<dependency> <dependency>
<groupId>com.fasterxml.jackson.core</groupId> <groupId>com.fasterxml.jackson.core</groupId>

View File

@@ -32,7 +32,7 @@ public final class ModelManager implements AutoCloseable {
int rgb = palette[i % palette.length]; i++; int rgb = palette[i % palette.length]; i++;
int bgr = ((rgb & 0xFF) << 16) | (rgb & 0xFF00) | ((rgb >> 16) & 0xFF); int bgr = ((rgb & 0xFF) << 16) | (rgb & 0xFF00) | ((rgb >> 16) & 0xFF);
YoloDetector det = new OpenVinoYoloDetector(name, dir, w, h, backend, bgr); YoloDetector det = new OpenCvYoloDetector(name, dir, w, h, backend, bgr);
map.put(name, det); map.put(name, det);
} }
} }

View File

@@ -5,14 +5,21 @@ import org.bytedeco.javacpp.indexer.FloatRawIndexer;
import org.bytedeco.opencv.opencv_core.*; import org.bytedeco.opencv.opencv_core.*;
import org.bytedeco.opencv.opencv_dnn.Net; import org.bytedeco.opencv.opencv_dnn.Net;
import java.nio.file.*; import java.nio.file.Files;
import java.util.*; import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.bytedeco.opencv.global.opencv_dnn.*; // DNN APIreadNetFromModelOptimizer / blobFromImage import static org.bytedeco.opencv.global.opencv_core.*;
import static org.bytedeco.opencv.global.opencv_core.*; // Mat/Size/Scalar/transpose import static org.bytedeco.opencv.global.opencv_dnn.*;
import static org.bytedeco.opencv.global.opencv_imgproc.*; // cvtColor import static org.bytedeco.opencv.global.opencv_imgproc.*;
public final class OpenVinoYoloDetector implements YoloDetector { /**
* OpenCV CPU 后端的 YOLO IR 检测器.xml/.bin
* 与原 OpenVinoYoloDetector 代码保持一致只是固定DNN_BACKEND_OPENCV + DNN_TARGET_CPU
*/
public final class OpenCvYoloDetector implements YoloDetector {
private final String modelName; private final String modelName;
private final Net net; private final Net net;
private final Size input; private final Size input;
@@ -20,7 +27,15 @@ public final class OpenVinoYoloDetector implements YoloDetector {
private final String[] classes; private final String[] classes;
private final int colorBGR; private final int colorBGR;
public OpenVinoYoloDetector(String name, Path dir, int inW, int inH, String backend, int colorBGR) throws Exception { /**
* @param name 模型别名
* @param dir 模型目录 model.xml / model.bin / classes.txt
* @param inW 推理输入宽
* @param inH 推理输入高
* @param backend 兼容参数忽略统一使用 OpenCV CPU
* @param colorBGR 画框颜色BGR packed int
*/
public OpenCvYoloDetector(String name, Path dir, int inW, int inH, String backend, int colorBGR) throws Exception {
this.modelName = name; this.modelName = name;
this.input = new Size(inW, inH); this.input = new Size(inW, inH);
this.colorBGR = colorBGR; this.colorBGR = colorBGR;
@@ -30,27 +45,19 @@ public final class OpenVinoYoloDetector implements YoloDetector {
Path clsPath = dir.resolve("classes.txt"); Path clsPath = dir.resolve("classes.txt");
if (Files.exists(clsPath)) { if (Files.exists(clsPath)) {
this.classes = Files.readAllLines(clsPath).stream().map(String::trim) this.classes = Files.readAllLines(clsPath).stream()
.filter(s -> !s.isEmpty()).toArray(String[]::new); .map(String::trim).filter(s -> !s.isEmpty())
.toArray(String[]::new);
} else { } else {
this.classes = new String[0]; this.classes = new String[0];
} }
this.net = readNetFromModelOptimizer(xml, bin); this.net = readNetFromModelOptimizer(xml, bin);
boolean set = false; // 固定使用 OpenCV CPU 后端不再尝试 OpenVINO
if ("openvino".equalsIgnoreCase(backend)) {
try {
net.setPreferableBackend(DNN_BACKEND_INFERENCE_ENGINE);
net.setPreferableTarget(DNN_TARGET_CPU);
set = true;
} catch (Throwable ignore) { /* 回退 */ }
}
if (!set) {
net.setPreferableBackend(DNN_BACKEND_OPENCV); net.setPreferableBackend(DNN_BACKEND_OPENCV);
net.setPreferableTarget(DNN_TARGET_CPU); net.setPreferableTarget(DNN_TARGET_CPU);
} }
}
@Override public String name() { return modelName; } @Override public String name() { return modelName; }
@@ -58,7 +65,7 @@ public final class OpenVinoYoloDetector implements YoloDetector {
public List<Detection> detect(Mat bgr) { public List<Detection> detect(Mat bgr) {
if (bgr == null || bgr.empty()) return Collections.emptyList(); if (bgr == null || bgr.empty()) return Collections.emptyList();
// 统一成 BGR 3 通道避免 blobFromImage 断言失败 // 保证 BGR 3通道
if (bgr.channels() != 3) { if (bgr.channels() != 3) {
Mat tmp = new Mat(); Mat tmp = new Mat();
if (bgr.channels() == 1) cvtColor(bgr, tmp, COLOR_GRAY2BGR); if (bgr.channels() == 1) cvtColor(bgr, tmp, COLOR_GRAY2BGR);
@@ -69,23 +76,19 @@ public final class OpenVinoYoloDetector implements YoloDetector {
try (Mat blob = blobFromImage(bgr, 1.0/255.0, input, new Scalar(0.0), true, false, CV_32F)) { try (Mat blob = blobFromImage(bgr, 1.0/255.0, input, new Scalar(0.0), true, false, CV_32F)) {
net.setInput(blob); net.setInput(blob);
// ===== 多输出兼容Bytedeco 正确写法=====
// 兼容单/多输出
org.bytedeco.opencv.opencv_core.StringVector outNames = net.getUnconnectedOutLayersNames(); org.bytedeco.opencv.opencv_core.StringVector outNames = net.getUnconnectedOutLayersNames();
List<Mat> outs = new ArrayList<>(); List<Mat> outs = new ArrayList<>();
if (outNames == null || outNames.size() == 0) { if (outNames == null || outNames.size() == 0) {
// 只有一个默认输出 Mat out = net.forward();
Mat out = net.forward(); // 直接返回 Mat
outs.add(out); outs.add(out);
} else { } else {
// 多输出 MatVector 承接
org.bytedeco.opencv.opencv_core.MatVector outBlobs = org.bytedeco.opencv.opencv_core.MatVector outBlobs =
new org.bytedeco.opencv.opencv_core.MatVector(outNames.size()); new org.bytedeco.opencv.opencv_core.MatVector(outNames.size());
net.forward(outBlobs, outNames); // 正确的重载 net.forward(outBlobs, outNames);
for (long i = 0; i < outBlobs.size(); i++) outs.add(outBlobs.get(i));
for (long i = 0; i < outBlobs.size(); i++) {
outs.add(outBlobs.get(i));
}
} }
int fw = bgr.cols(), fh = bgr.rows(); int fw = bgr.cols(), fh = bgr.rows();
@@ -98,7 +101,6 @@ public final class OpenVinoYoloDetector implements YoloDetector {
} }
if (boxes.isEmpty()) return Collections.emptyList(); if (boxes.isEmpty()) return Collections.emptyList();
// Java NMS避免 MatOf* / Vector API 兼容问题
List<Integer> keep = nmsIndices(boxes, scores, nmsTh); List<Integer> keep = nmsIndices(boxes, scores, nmsTh);
List<Detection> result = new ArrayList<>(keep.size()); List<Detection> result = new ArrayList<>(keep.size());
@@ -111,7 +113,6 @@ public final class OpenVinoYoloDetector implements YoloDetector {
} }
return result; return result;
} catch (Throwable e) { } catch (Throwable e) {
// 单帧失败不影响整体
return Collections.emptyList(); return Collections.emptyList();
} }
} }
@@ -123,7 +124,6 @@ public final class OpenVinoYoloDetector implements YoloDetector {
Mat m; Mat m;
if (dims == 2) { if (dims == 2) {
// NxC CxN
if (out.cols() >= 6) { if (out.cols() >= 6) {
m = out; m = out;
} else { } else {
@@ -132,7 +132,6 @@ public final class OpenVinoYoloDetector implements YoloDetector {
m = tmp; m = tmp;
} }
} else if (dims == 3) { } else if (dims == 3) {
// [1,N,C] [1,C,N]
if (out.size(2) >= 6) { if (out.size(2) >= 6) {
m = out.reshape(1, out.size(1)); // -> N×C m = out.reshape(1, out.size(1)); // -> N×C
} else { } else {
@@ -142,7 +141,6 @@ public final class OpenVinoYoloDetector implements YoloDetector {
m = tmp; m = tmp;
} }
} else if (dims == 4) { } else if (dims == 4) {
// [1,1,N,C] [1,1,C,N]
int a = out.size(2), b = out.size(3); int a = out.size(2), b = out.size(3);
if (b >= 6) { if (b >= 6) {
m = out.reshape(1, a).clone(); // -> N×C m = out.reshape(1, a).clone(); // -> N×C
@@ -153,7 +151,7 @@ public final class OpenVinoYoloDetector implements YoloDetector {
m = tmp.clone(); m = tmp.clone();
} }
} else { } else {
return; // 不支持的形状 return;
} }
int N = m.rows(), C = m.cols(); int N = m.rows(), C = m.cols();
@@ -172,7 +170,7 @@ public final class OpenVinoYoloDetector implements YoloDetector {
float conf = obj * bestScore; float conf = obj * bestScore;
if (conf < confTh) continue; if (conf < confTh) continue;
// 默认假设归一化中心点格式 (cx,cy,w,h)若你的 IR x1,y1,x2,y2把这里换算改掉 // 假定 (cx,cy,w,h) 为归一化中心点格式 x1,y1,x2,y2按需改这里
int bx = Math.max(0, Math.round(cx * fw - (w * fw) / 2f)); int bx = Math.max(0, Math.round(cx * fw - (w * fw) / 2f));
int by = Math.max(0, Math.round(cy * fh - (h * fh) / 2f)); int by = Math.max(0, Math.round(cy * fh - (h * fh) / 2f));
int bw = Math.min(fw - bx, Math.round(w * fw)); int bw = Math.min(fw - bx, Math.round(w * fw));
@@ -189,7 +187,6 @@ public final class OpenVinoYoloDetector implements YoloDetector {
private List<Integer> nmsIndices(List<Rect2d> boxes, List<Float> scores, float nmsThreshold) { private List<Integer> nmsIndices(List<Rect2d> boxes, List<Float> scores, float nmsThreshold) {
List<Integer> order = new ArrayList<>(boxes.size()); List<Integer> order = new ArrayList<>(boxes.size());
for (int i = 0; i < boxes.size(); i++) order.add(i); for (int i = 0; i < boxes.size(); i++) order.add(i);
// 按分数降序
order.sort((i, j) -> Float.compare(scores.get(j), scores.get(i))); order.sort((i, j) -> Float.compare(scores.get(j), scores.get(i)));
List<Integer> keep = new ArrayList<>(); List<Integer> keep = new ArrayList<>();

View File

@@ -1,4 +1,4 @@
[ [
{"name":"smoke","path":"models/smoke","size":[640,640],"backend":"openvino"}, {"name":"smoke","path":"libs/models/smoke","size":[640,640],"backend":"OpenCV"},
{"name":"garbage","path":"models/garbage","size":[640,640],"backend":"openvino"} {"name":"garbage","path":"libs/models/garbage","size":[640,640],"backend":"OpenCV"}
] ]