- 【人脸检测】新增6个模型(MTCNN、YOLOV5、RetinaFace小尺寸版),大幅提升性能

- 【人脸识别】新增Seetaface6轻量模型
- 【目标检测】支持视频流目标检测(rtsp、视频文件等)
- 【目标检测】支持tensorflow2目标检测模型
- 【目标检测】新增行人检测模型(yolo-person)
- 【通用视觉】新增4个动作识别模型
- 【通用视觉】新增语义分割模型
- 【通用视觉】新增5个实例分割模型(含yolov8-seg、yolov11-seg)
- 【通用视觉】新增yolo-obb11旋转框检测(含yolov11-obb)
- 【通用视觉】新增5个姿态估计模型(含yolov8-pose、yolov11-pose)
This commit is contained in:
dengwenjie
2025-09-07 17:19:19 +08:00
parent 2b044fda29
commit a8e7ce6c4e
102 changed files with 4473 additions and 1368 deletions

View File

@@ -8,14 +8,13 @@ import java.util.List;
/**
* 检测结果
* @author dwj
* @date 2025/4/12
*/
@Data
public class DetectionResponse {
private List<DetectionInfo> detectionInfoList;
private Image drawnImage;
private transient Image drawnImage;
public DetectionResponse() {

View File

@@ -1,11 +1,14 @@
package cn.smartjavaai.common.entity;
import lombok.Data;
import java.util.List;
/**
* 定向边界框 检测结果
* @author dwj
*/
@Data
public class ObbDetInfo {
/**

View File

@@ -1,66 +0,0 @@
package cn.smartjavaai.common.utils;
import ai.djl.modality.cv.Image;
import ai.djl.modality.cv.ImageFactory;
import org.bytedeco.javacpp.BytePointer;
import org.bytedeco.javacv.OpenCVFrameConverter;
import org.bytedeco.opencv.opencv_core.CvMat;
import org.bytedeco.opencv.opencv_core.Mat;
import org.opencv.core.CvType;
import java.awt.image.BufferedImage;
/**
* @author dwj
* @date 2025/8/27
*/
public class FrameConverterUtil {
/**
* 将 Bytedeco Mat 转为 DJL Image
* 支持 1/3/4 通道
*/
public static Image matToDJLImage(Mat cvMat) {
if (cvMat == null || cvMat.empty()) {
return null;
}
int width = cvMat.cols();
int height = cvMat.rows();
int channels = cvMat.channels();
int[] pixels = new int[width * height];
if (channels == 1) { // 灰度图
byte[] data = new byte[width * height];
cvMat.data().get(data);
for (int i = 0; i < width * height; i++) {
int gray = data[i] & 0xFF;
pixels[i] = (0xFF << 24) | (gray << 16) | (gray << 8) | gray;
}
} else if (channels == 3) { // BGR
byte[] data = new byte[width * height * 3];
cvMat.data().get(data);
for (int i = 0; i < width * height; i++) {
int b = data[i * 3] & 0xFF;
int g = data[i * 3 + 1] & 0xFF;
int r = data[i * 3 + 2] & 0xFF;
pixels[i] = (0xFF << 24) | (r << 16) | (g << 8) | b;
}
} else if (channels == 4) { // BGRA
byte[] data = new byte[width * height * 4];
cvMat.data().get(data);
for (int i = 0; i < width * height; i++) {
int b = data[i * 4] & 0xFF;
int g = data[i * 4 + 1] & 0xFF;
int r = data[i * 4 + 2] & 0xFF;
int a = data[i * 4 + 3] & 0xFF;
pixels[i] = (a << 24) | (r << 16) | (g << 8) | b;
}
} else {
throw new IllegalArgumentException("只支持 1/3/4 通道图像");
}
return ImageFactory.getInstance().fromPixels(pixels, width, height);
}
}

View File

@@ -3,8 +3,11 @@ package cn.smartjavaai.common.utils;
import ai.djl.modality.cv.BufferedImageFactory;
import ai.djl.modality.cv.Image;
import ai.djl.modality.cv.ImageFactory;
import ai.djl.modality.cv.output.CategoryMask;
import ai.djl.modality.cv.output.DetectedObjects;
import ai.djl.ndarray.NDArray;
import ai.djl.util.RandomUtils;
import cn.smartjavaai.common.cv.SmartImageFactory;
import cn.smartjavaai.common.entity.DetectionRectangle;
import cn.smartjavaai.common.entity.DetectionResponse;
import org.opencv.core.Mat;
@@ -17,6 +20,7 @@ import java.awt.*;
import java.awt.image.BufferedImage;
//import java.awt.image.ColorConvertOp;
import java.awt.image.ComponentSampleModel;
import java.awt.image.DataBufferByte;
import java.awt.image.ImageObserver;
import java.io.File;
import java.io.IOException;
@@ -500,6 +504,86 @@ public class ImageUtils {
}
/**
* 拷贝图片
* @param src
* @return
*/
public static BufferedImage copyBufferedImage(BufferedImage src) {
BufferedImage copy = new BufferedImage(src.getWidth(), src.getHeight(), src.getType());
Graphics2D g = copy.createGraphics();
g.drawImage(src, 0, 0, null);
g.dispose();
return copy;
}
/**
* 拷贝图片
* @param src
* @return
*/
public static Image copy(Image src) {
Object srcData = src.getWrappedImage();
//当图片未BufferedImageDJL的duplicate会有问题
if (srcData instanceof BufferedImage) {
return SmartImageFactory.getInstance().fromImage(copyBufferedImage((BufferedImage) srcData));
}else{
return src.duplicate();
}
}
/**
* 为不同分类生成不同颜色
* @param background
* @param opacity
* @param classes
* @return
*/
public static int[] generateColors(int background, int opacity, List<String> classes) {
int[] colors = new int[classes.size()];
colors[0] = background;
for (int i = 1; i < classes.size(); i++) {
int red = RandomUtils.nextInt(256);
int green = RandomUtils.nextInt(256);
int blue = RandomUtils.nextInt(256);
colors[i] = opacity << 24 | red << 16 | green << 8 | blue;
}
return colors;
}
/**
* 生成不同颜色遮罩
* @param colors
* @param mask
* @return
*/
public static Image getColorOverlay(int[] colors,int[][] mask) {
int height = mask.length;
int width = mask[0].length;
int[] pixels = new int[width * height];
for (int h = 0; h < height; h++) {
for (int w = 0; w < width; w++) {
int index = mask[h][w];
pixels[h * width + w] = colors[index];
}
}
return SmartImageFactory.getInstance().fromPixels(pixels, width, height);
}
/**
* 绘制遮罩
* @param categoryMask
* @param image
* @param opacity
* @param background
*/
public static void drawMask(CategoryMask categoryMask, Image image, int opacity, int background) {
int[] colors = generateColors(background, opacity, categoryMask.getClasses());
Image maskImage = getColorOverlay(colors, categoryMask.getMask());
image.drawImage(maskImage, true);
}

View File

@@ -239,27 +239,27 @@ public class OpenCVUtils {
* @param src Bytedeco Mat (BGR 或 BGRA)
* @return OpenCV Mat (BGR 或 BGRA)
*/
public static org.opencv.core.Mat convertToOpenCVMat(org.bytedeco.opencv.opencv_core.Mat bMat) {
try {
int width = bMat.cols();
int height = bMat.rows();
int channels = bMat.channels();
// 创建 OpenCV Mat
org.opencv.core.Mat cvMat = new org.opencv.core.Mat(height, width, channels == 3 ? CvType.CV_8UC3 : CvType.CV_8UC1);
// 从 bytedeco Mat 获取像素数据
byte[] data = new byte[width * height * channels];
bMat.data().get(data);
// 填充到 OpenCV Mat
cvMat.put(0, 0, data);
return cvMat;
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}
// public static org.opencv.core.Mat convertToOpenCVMat(org.bytedeco.opencv.opencv_core.Mat bMat) {
//
//
// try {
// int width = bMat.cols();
// int height = bMat.rows();
// int channels = bMat.channels();
//
// // 创建 OpenCV Mat
// org.opencv.core.Mat cvMat = new org.opencv.core.Mat(height, width, channels == 3 ? CvType.CV_8UC3 : CvType.CV_8UC1);
//
// // 从 bytedeco Mat 获取像素数据
// byte[] data = new byte[width * height * channels];
// bMat.data().get(data);
//
// // 填充到 OpenCV Mat
// cvMat.put(0, 0, data);
// return cvMat;
// } catch (Throwable e) {
// e.printStackTrace();
// }
// return null;
// }
}

View File

@@ -1,77 +0,0 @@
package cn.smartjavaai.common.utils;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.OpenCVFrameConverter;
import org.bytedeco.opencv.global.opencv_core;
import org.bytedeco.opencv.opencv_core.Mat;
/**
* 视频工具类
* @author dwj
* @date 2025/7/17
*/
public class VideoUtils {
/**
* 视频旋转
* @param inputPath 输入视频路径
* @param outputPath 输出视频路径
* @param angle 旋转角度
* @param format 视频格式
* @param videoCodec 视频编码器
* @throws FFmpegFrameRecorder.Exception
* @throws FFmpegFrameGrabber.Exception
*/
public static void rotateVideo(String inputPath, String outputPath, int angle, String format, int videoCodec) throws FFmpegFrameRecorder.Exception, FFmpegFrameGrabber.Exception {
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(inputPath);
grabber.start();
int inputWidth = grabber.getImageWidth();
int inputHeight = grabber.getImageHeight();
int outputWidth = inputWidth;
int outputHeight = inputHeight;
if (angle == 90 || angle == 270) {
outputWidth = inputHeight;
outputHeight = inputWidth;
}
FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputPath,
outputWidth, outputHeight, grabber.getAudioChannels());
recorder.setVideoCodec(videoCodec);
recorder.setFormat(format);
recorder.start();
Frame frame;
OpenCVFrameConverter.ToMat converter = new OpenCVFrameConverter.ToMat();
while ((frame = grabber.grab()) != null) {
if (frame.image != null) {
Mat mat = converter.convert(frame);
Mat rotated = new Mat();
switch (angle) {
case 90:
opencv_core.transpose(mat, rotated);
opencv_core.flip(rotated, rotated, 1);
break;
case 180:
opencv_core.flip(mat, rotated, -1);
break;
case 270:
opencv_core.transpose(mat, rotated);
opencv_core.flip(rotated, rotated, 0);
break;
default:
rotated = mat.clone();
break;
}
frame = converter.convert(rotated);
recorder.record(frame);
}
}
recorder.stop();
recorder.release();
grabber.stop();
grabber.release();
}
}