mirror of
https://github.com/geekwenjie/SmartJavaAI.git
synced 2026-09-10 03:28:49 +00:00
- 【人脸检测】新增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:
@@ -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() {
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
package cn.smartjavaai.common.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 定向边界框 检测结果
|
||||
* @author dwj
|
||||
*/
|
||||
@Data
|
||||
public class ObbDetInfo {
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
//当图片未BufferedImage,DJL的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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user