@@ -5,14 +5,21 @@ import org.bytedeco.javacpp.indexer.FloatRawIndexer;
import org.bytedeco.opencv.opencv_core.* ;
import org.bytedeco.opencv.opencv_dnn.Net ;
import java.nio.file.* ;
import java.util.* ;
import java.nio.file.Files ;
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 API ( readNetFromModelOptimizer / blobFromImage 等 )
import static org.bytedeco.opencv.global.opencv_core .* ; / / Mat / Size / Scalar / transpose 等
import static org.bytedeco.opencv.global.opencv_imgproc.* ; / / cvtColor 等
import static org.bytedeco.opencv.global.opencv_core .* ;
import static org.bytedeco.opencv.global.opencv_dnn .* ;
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 Net net ;
private final Size input ;
@@ -20,7 +27,15 @@ public final class OpenVinoYoloDetector implements YoloDetector {
private final String [ ] classes ;
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 . input = new Size ( inW , inH ) ;
this . colorBGR = colorBGR ;
@@ -30,26 +45,18 @@ public final class OpenVinoYoloDetector implements YoloDetector {
Path clsPath = dir . resolve ( " classes.txt " ) ;
if ( Files . exists ( clsPath ) ) {
this . classes = Files . readAllLines ( clsPath ) . stream ( ) . map ( String : : trim )
. filter ( s - > ! s . isEmpty ( ) ) . toArray ( String [ ] : : new ) ;
this . classes = Files . readAllLines ( clsPath ) . stream ( )
. map ( String : : trim ) . filter( s - > ! s . isEmpty ( ) )
. toArray ( String [ ] : : new ) ;
} else {
this . classes = new String [ 0 ] ;
}
this . net = readNetFromModelOptimizer ( xml , bin ) ;
boolean set = false ;
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 . setPreferableTarget ( DNN_TARGET_CPU ) ;
}
/ / ✅ 固定使用 OpenCV CPU 后端 ( 不再尝试 OpenVINO )
net . setPreferableBackend ( DNN_BACKEND_OPENCV ) ;
net . setPreferableTarget ( DNN_TARGET_CPU ) ;
}
@Override public String name ( ) { return modelName ; }
@@ -58,7 +65,7 @@ public final class OpenVinoYoloDetector implements YoloDetector {
public List < Detection > detect ( Mat bgr ) {
if ( bgr = = null | | bgr . empty ( ) ) return Collections . emptyList ( ) ;
/ / 统一成 BGR 3 通道 , 避免 blobFromImage 断言失败
/ / 保证 BGR 3通道
if ( bgr . channels ( ) ! = 3 ) {
Mat tmp = new Mat ( ) ;
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 ) ) {
net . setInput ( blob ) ;
/ / = = = = = 多输出兼容 ( Bytedeco 正确写法 ) = = = = =
/ / 兼容单 / 多输出
org . bytedeco . opencv . opencv_core . StringVector outNames = net . getUnconnectedOutLayersNames ( ) ;
List < Mat > outs = new ArrayList < > ( ) ;
if ( outNames = = null | | outNames . size ( ) = = 0 ) {
/ / 只有一个默认输出
Mat out = net . forward ( ) ; / / ← 直接返回 Mat
Mat out = net . forward ( ) ;
outs . add ( out ) ;
} else {
/ / 多输出 : 用 MatVector 承接
org . bytedeco . opencv . opencv_core . MatVector outBlobs =
new org . bytedeco . opencv . opencv_core . MatVector ( outNames . size ( ) ) ;
net . forward ( outBlobs , outNames ) ; / / ← 正确的重载
for ( long i = 0 ; i < outBlobs . size ( ) ; i + + ) {
outs . add ( outBlobs . get ( i ) ) ;
}
net . forward ( outBlobs , outNames ) ;
for ( long i = 0 ; i < outBlobs . size ( ) ; i + + ) outs . add ( outBlobs . get ( i ) ) ;
}
int fw = bgr . cols ( ) , fh = bgr . rows ( ) ;
@@ -98,7 +101,6 @@ public final class OpenVinoYoloDetector implements YoloDetector {
}
if ( boxes . isEmpty ( ) ) return Collections . emptyList ( ) ;
/ / 纯 Java NMS , 避免 MatOf * / Vector API 兼容问题
List < Integer > keep = nmsIndices ( boxes , scores , nmsTh ) ;
List < Detection > result = new ArrayList < > ( keep . size ( ) ) ;
@@ -111,7 +113,6 @@ public final class OpenVinoYoloDetector implements YoloDetector {
}
return result ;
} catch ( Throwable e ) {
/ / 单帧失败不影响整体
return Collections . emptyList ( ) ;
}
}
@@ -123,7 +124,6 @@ public final class OpenVinoYoloDetector implements YoloDetector {
Mat m ;
if ( dims = = 2 ) {
/ / NxC 或 CxN
if ( out . cols ( ) > = 6 ) {
m = out ;
} else {
@@ -132,7 +132,6 @@ public final class OpenVinoYoloDetector implements YoloDetector {
m = tmp ;
}
} else if ( dims = = 3 ) {
/ / [ 1 , N , C ] 或 [ 1 , C , N ]
if ( out . size ( 2 ) > = 6 ) {
m = out . reshape ( 1 , out . size ( 1 ) ) ; / / - > N × C
} else {
@@ -142,7 +141,6 @@ public final class OpenVinoYoloDetector implements YoloDetector {
m = tmp ;
}
} else if ( dims = = 4 ) {
/ / [ 1 , 1 , N , C ] 或 [ 1 , 1 , C , N ]
int a = out . size ( 2 ) , b = out . size ( 3 ) ;
if ( b > = 6 ) {
m = out . reshape ( 1 , a ) . clone ( ) ; / / - > N × C
@@ -153,7 +151,7 @@ public final class OpenVinoYoloDetector implements YoloDetector {
m = tmp . clone ( ) ;
}
} else {
return ; / / 不支持的形状
return ;
}
int N = m . rows ( ) , C = m . cols ( ) ;
@@ -172,7 +170,7 @@ public final class OpenVinoYoloDetector implements YoloDetector {
float conf = obj * bestScore ;
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 by = Math . max ( 0 , Math . round ( cy * fh - ( h * fh ) / 2f ) ) ;
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 ) {
List < Integer > order = new ArrayList < > ( boxes . size ( ) ) ;
for ( int i = 0 ; i < boxes . size ( ) ; i + + ) order . add ( i ) ;
/ / 按分数降序
order . sort ( ( i , j ) - > Float . compare ( scores . get ( j ) , scores . get ( i ) ) ) ;
List < Integer > keep = new ArrayList < > ( ) ;