临时提交
12
README.md
@@ -228,7 +228,8 @@ SmartJavaAI是专为JAVA 开发者打造的一个功能丰富、开箱即用的
|
||||
<td>
|
||||
<div align="left">
|
||||
<p>语音识别</p>
|
||||
- 支持100种语言
|
||||
- 支持100种语言<br>
|
||||
- 支持实时语音识别
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
@@ -343,7 +344,7 @@ SmartJavaAI是专为JAVA 开发者打造的一个功能丰富、开箱即用的
|
||||
<dependency>
|
||||
<groupId>cn.smartjavaai</groupId>
|
||||
<artifactId>smartjavaai-all</artifactId>
|
||||
<version>1.0.23</version>
|
||||
<version>1.0.24</version>
|
||||
</dependency>
|
||||
```
|
||||
### 3、完整示例代码
|
||||
@@ -592,15 +593,20 @@ SmartJavaAI是专为JAVA 开发者打造的一个功能丰富、开箱即用的
|
||||
## 献代码的步骤
|
||||
|
||||
1、在Gitee或者Github/Gitcode上fork项目到自己的repo
|
||||
|
||||
2、把fork过去的项目也就是你的项目clone到你的本地
|
||||
|
||||
3、修改代码(记得一定要修改dev分支)
|
||||
|
||||
4、commit后push到自己的库(dev分支)
|
||||
|
||||
5、登录Gitee或Github/Gitcode在你首页可以看到一个 pull request 按钮,点击它,填写一些说明信息,然后提交即可。
|
||||
|
||||
6、等待维护者合并
|
||||
|
||||
## 近期更新日志
|
||||
|
||||
## [v1.0.23] - 2025-08-09
|
||||
## [v1.0.24] - 2025-08-09
|
||||
- 新增 语音识别模块,集成 OpenAI 开源的 Whisper 和 Vosk
|
||||
- 修复 质量评估模型的 Bug
|
||||
- 修复 OCR 模块 recognizeAndDraw 方法的 Bug
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
<parent>
|
||||
<groupId>cn.smartjavaai</groupId>
|
||||
<artifactId>smartjavaai-parent</artifactId>
|
||||
<version>1.0.23</version>
|
||||
<version>1.0.24</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>smartjavaai-all</artifactId>
|
||||
<version>1.0.23</version>
|
||||
<artifactId>all</artifactId>
|
||||
<version>1.0.24</version>
|
||||
<name>${project.artifactId}</name>
|
||||
<description>SmartJavaAI</description>
|
||||
<url>https://github.com/geekwenjie/SmartJavaAI</url>
|
||||
@@ -33,31 +33,31 @@
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.smartjavaai</groupId>
|
||||
<artifactId>smartjavaai-face</artifactId>
|
||||
<artifactId>face</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.smartjavaai</groupId>
|
||||
<artifactId>smartjavaai-objectdetection</artifactId>
|
||||
<artifactId>vision</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.smartjavaai</groupId>
|
||||
<artifactId>smartjavaai-ocr</artifactId>
|
||||
<artifactId>ocr</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.smartjavaai</groupId>
|
||||
<artifactId>smartjavaai-translate</artifactId>
|
||||
<artifactId>translate</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.smartjavaai</groupId>
|
||||
<artifactId>smartjavaai-speech</artifactId>
|
||||
<artifactId>speech</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
47
all/src/test/java/Test.java
Normal file
@@ -0,0 +1,47 @@
|
||||
import ai.djl.Application;
|
||||
import ai.djl.Model;
|
||||
import ai.djl.repository.zoo.Criteria;
|
||||
import ai.djl.repository.zoo.ModelZoo;
|
||||
import cn.smartjavaai.ocr.config.OcrDetModelConfig;
|
||||
import cn.smartjavaai.ocr.entity.OcrBox;
|
||||
import cn.smartjavaai.ocr.enums.CommonDetModelEnum;
|
||||
import cn.smartjavaai.ocr.factory.OcrModelFactory;
|
||||
import cn.smartjavaai.ocr.model.common.detect.OcrCommonDetModel;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.bytedeco.javacv.FFmpegFrameGrabber;
|
||||
import org.bytedeco.javacv.Frame;
|
||||
import org.bytedeco.javacv.Java2DFrameUtils;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author dwj
|
||||
* @date 2025/4/24
|
||||
*/
|
||||
@Slf4j
|
||||
public class Test {
|
||||
|
||||
|
||||
public static String savePath = "/Users/wenjie/Downloads/";
|
||||
//public static String image1Path = "/Users/wenjie/Documents/idea_workplace/SmartJavaAI-Demo/src/main/resources/5.jpg";
|
||||
public static String image1Path = "/Users/wenjie/Documents/idea_workplace/SmartJavaAI-Demo/src/main/resources/MJ_20250226_172200.png";
|
||||
|
||||
public static String image2Path = "/Users/wenjie/Documents/idea_workplace/SmartJavaAI-Demo/src/main/resources/MJ_20250226_172222.png";
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
// // 加载模型
|
||||
// Model model = ModelZoo.loadModel(Criteria.builder()
|
||||
// .optApplication(Application.NLP.ANY)
|
||||
// .optEngine("PyTorch")
|
||||
// .optModelName("Llama 3")
|
||||
// .optTranslatorFactory(new Llama3TranslatorFactory())
|
||||
// .optTranslatorProvider(() -> new Llama3Translator())
|
||||
// .build());
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -6,12 +6,12 @@
|
||||
<parent>
|
||||
<groupId>cn.smartjavaai</groupId>
|
||||
<artifactId>smartjavaai-parent</artifactId>
|
||||
<version>1.0.23</version>
|
||||
<version>1.0.24</version>
|
||||
</parent>
|
||||
|
||||
<version>1.0.23</version>
|
||||
<artifactId>smartjavaai-bom</artifactId>
|
||||
<name>smartjavaai-bom</name>
|
||||
<version>1.0.24</version>
|
||||
<artifactId>bom</artifactId>
|
||||
<name>sbom</name>
|
||||
<description>统一版本管理的 BOM 包,同时支持 import 和全量依赖</description>
|
||||
|
||||
<properties>
|
||||
@@ -25,27 +25,27 @@
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.smartjavaai</groupId>
|
||||
<artifactId>smartjavaai-face</artifactId>
|
||||
<artifactId>face</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.smartjavaai</groupId>
|
||||
<artifactId>smartjavaai-objectdetection</artifactId>
|
||||
<artifactId>vision</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.smartjavaai</groupId>
|
||||
<artifactId>smartjavaai-ocr</artifactId>
|
||||
<artifactId>ocr</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.smartjavaai</groupId>
|
||||
<artifactId>smartjavaai-translate</artifactId>
|
||||
<artifactId>translate</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.smartjavaai</groupId>
|
||||
<artifactId>smartjavaai-speech</artifactId>
|
||||
<artifactId>speech</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
BIN
build/output/0.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
build/output/0crop.png
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
BIN
build/output/1.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
build/output/10.png
Normal file
|
After Width: | Height: | Size: 163 KiB |
BIN
build/output/10crop.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
build/output/11.png
Normal file
|
After Width: | Height: | Size: 184 KiB |
BIN
build/output/11crop.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
build/output/12.png
Normal file
|
After Width: | Height: | Size: 202 KiB |
BIN
build/output/12crop.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
build/output/13.png
Normal file
|
After Width: | Height: | Size: 223 KiB |
BIN
build/output/13crop.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
build/output/14.png
Normal file
|
After Width: | Height: | Size: 234 KiB |
BIN
build/output/14crop.png
Normal file
|
After Width: | Height: | Size: 8.9 KiB |
BIN
build/output/15.png
Normal file
|
After Width: | Height: | Size: 266 KiB |
BIN
build/output/15crop.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
build/output/1crop.png
Normal file
|
After Width: | Height: | Size: 6.5 KiB |
BIN
build/output/2.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
build/output/2crop.png
Normal file
|
After Width: | Height: | Size: 5.4 KiB |
BIN
build/output/3.png
Normal file
|
After Width: | Height: | Size: 57 KiB |
BIN
build/output/3crop.png
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
BIN
build/output/4.png
Normal file
|
After Width: | Height: | Size: 681 KiB |
BIN
build/output/4crop.png
Normal file
|
After Width: | Height: | Size: 140 KiB |
BIN
build/output/5.png
Normal file
|
After Width: | Height: | Size: 97 KiB |
BIN
build/output/5crop.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
build/output/6.png
Normal file
|
After Width: | Height: | Size: 114 KiB |
BIN
build/output/6crop.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
build/output/7.png
Normal file
|
After Width: | Height: | Size: 130 KiB |
BIN
build/output/7crop.png
Normal file
|
After Width: | Height: | Size: 7.1 KiB |
BIN
build/output/8.png
Normal file
|
After Width: | Height: | Size: 150 KiB |
BIN
build/output/8crop.png
Normal file
|
After Width: | Height: | Size: 9.6 KiB |
BIN
build/output/9.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
build/output/9crop.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
build/output/9rotate.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
build/output/cn_layout_detect_result.png
Normal file
|
After Width: | Height: | Size: 136 KiB |
BIN
build/output/ocr_1_detected.jpg
Normal file
|
After Width: | Height: | Size: 846 KiB |
BIN
build/output/table.jpg
Normal file
|
After Width: | Height: | Size: 269 KiB |
BIN
build/output/yolo_detected.png
Normal file
|
After Width: | Height: | Size: 3.7 MiB |
@@ -6,11 +6,11 @@
|
||||
<parent>
|
||||
<groupId>cn.smartjavaai</groupId>
|
||||
<artifactId>smartjavaai-parent</artifactId>
|
||||
<version>1.0.23</version>
|
||||
<version>1.0.24</version>
|
||||
</parent>
|
||||
|
||||
<name>smartjavaai-common</name>
|
||||
<artifactId>smartjavaai-common</artifactId>
|
||||
<name>common</name>
|
||||
<artifactId>common</artifactId>
|
||||
<description>SmartJavaAI</description>
|
||||
<url>https://github.com/geekwenjie/SmartJavaAI</url>
|
||||
<licenses>
|
||||
@@ -8,6 +8,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
/**
|
||||
* 全局配置
|
||||
@@ -68,16 +69,25 @@ public class Config {
|
||||
String osName = SystemUtil.getOsInfo().getName();
|
||||
log.info("当前操作系统:{}", osName);
|
||||
if(osName.toLowerCase().contains("windows")){
|
||||
cachePath = SystemUtil.getUserInfo().getHomeDir() + CACHE_DIR;
|
||||
cachePath = Paths.get(
|
||||
SystemUtil.getUserInfo().getHomeDir(),
|
||||
"smartjavaai_cache"
|
||||
).toString();
|
||||
FileUtil.mkdir(cachePath);
|
||||
}else if(osName.toLowerCase().contains("linux")){
|
||||
cachePath = "/root/" + CACHE_DIR;
|
||||
FileUtil.mkdir(cachePath);
|
||||
}else if(osName.toLowerCase().contains("mac")){
|
||||
cachePath = SystemUtil.getUserInfo().getHomeDir() + CACHE_DIR;
|
||||
cachePath = Paths.get(
|
||||
SystemUtil.getUserInfo().getHomeDir(),
|
||||
"smartjavaai_cache"
|
||||
).toString();
|
||||
FileUtil.mkdir(cachePath);
|
||||
}else{
|
||||
cachePath = SystemUtil.getUserInfo().getHomeDir() + CACHE_DIR;
|
||||
cachePath = Paths.get(
|
||||
SystemUtil.getUserInfo().getHomeDir(),
|
||||
"smartjavaai_cache"
|
||||
).toString();
|
||||
FileUtil.mkdir(cachePath);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package cn.smartjavaai.common.cv;
|
||||
|
||||
import ai.djl.modality.cv.BufferedImageFactory;
|
||||
import ai.djl.modality.cv.Image;
|
||||
import ai.djl.modality.cv.ImageFactory;
|
||||
import ai.djl.modality.cv.output.DetectedObjects;
|
||||
import ai.djl.modality.cv.util.NDImageUtils;
|
||||
import ai.djl.ndarray.NDArray;
|
||||
import ai.djl.ndarray.types.DataType;
|
||||
import ai.djl.ndarray.types.Shape;
|
||||
import ai.djl.opencv.OpenCVImageFactory;
|
||||
import ai.djl.util.Utils;
|
||||
import cn.smartjavaai.common.utils.Base64ImageUtils;
|
||||
import cn.smartjavaai.common.utils.OpenCVUtils;
|
||||
import nu.pattern.OpenCV;
|
||||
import org.opencv.core.CvType;
|
||||
import org.opencv.core.Mat;
|
||||
import org.opencv.core.MatOfByte;
|
||||
import org.opencv.imgcodecs.Imgcodecs;
|
||||
import org.opencv.imgproc.Imgproc;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* 图片处理工厂类
|
||||
* @author dwj
|
||||
*/
|
||||
public class SmartImageFactory extends BufferedImageFactory {
|
||||
|
||||
private static volatile SmartImageFactory instance;
|
||||
|
||||
public static SmartImageFactory newInstance() {
|
||||
if (instance == null) {
|
||||
synchronized (SmartImageFactory.class) {
|
||||
if (instance == null) {
|
||||
instance = new SmartImageFactory();
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static SmartImageFactory getInstance(){
|
||||
return newInstance();
|
||||
}
|
||||
|
||||
public Image fromBufferedImage(BufferedImage sourceImage){
|
||||
return fromImage(OpenCVUtils.image2Mat(sourceImage));
|
||||
}
|
||||
|
||||
public Image fromBase64(String base64Image) throws IOException {
|
||||
return fromUrl(base64Image);
|
||||
}
|
||||
|
||||
public Image fromBytes(byte[] imageData){
|
||||
return fromImage(new ByteArrayInputStream(imageData));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -32,6 +32,16 @@ public class DetectionInfo {
|
||||
*/
|
||||
private ObjectDetInfo objectDetInfo;
|
||||
|
||||
/**
|
||||
* 目标分割信息
|
||||
*/
|
||||
private InstanceSegInfo instanceSegInfo;
|
||||
|
||||
/**
|
||||
* 旋转框信息
|
||||
*/
|
||||
private ObbDetInfo obbDetInfo;
|
||||
|
||||
|
||||
|
||||
public DetectionInfo() {
|
||||
@@ -1,5 +1,6 @@
|
||||
package cn.smartjavaai.common.entity;
|
||||
|
||||
import ai.djl.modality.cv.Image;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
@@ -14,6 +15,8 @@ public class DetectionResponse {
|
||||
|
||||
private List<DetectionInfo> detectionInfoList;
|
||||
|
||||
private Image drawnImage;
|
||||
|
||||
|
||||
public DetectionResponse() {
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package cn.smartjavaai.common.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 目标分割信息
|
||||
* @author dwj
|
||||
*/
|
||||
@Data
|
||||
public class InstanceSegInfo {
|
||||
|
||||
/**
|
||||
* 类别名称
|
||||
*/
|
||||
private String className;
|
||||
|
||||
/**
|
||||
* 遮罩
|
||||
*/
|
||||
private float[][] mask;
|
||||
|
||||
public InstanceSegInfo() {
|
||||
}
|
||||
|
||||
public InstanceSegInfo(String className, float[][] mask) {
|
||||
this.className = className;
|
||||
this.mask = mask;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package cn.smartjavaai.common.entity;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 定向边界框 检测结果
|
||||
* @author dwj
|
||||
*/
|
||||
public class ObbDetInfo {
|
||||
|
||||
/**
|
||||
* 类别名称
|
||||
*/
|
||||
private String className;
|
||||
|
||||
/**
|
||||
* 检测框坐标
|
||||
*/
|
||||
private RotatedBox rotatedBox;
|
||||
|
||||
public ObbDetInfo() {
|
||||
}
|
||||
|
||||
|
||||
public ObbDetInfo(String className, RotatedBox rotatedBox) {
|
||||
this.className = className;
|
||||
this.rotatedBox = rotatedBox;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package cn.smartjavaai.common.entity;
|
||||
|
||||
/**
|
||||
* 旋转框
|
||||
* @author dwj
|
||||
*/
|
||||
public class RotatedBox {
|
||||
|
||||
/**
|
||||
* 左上角
|
||||
*/
|
||||
private Point topLeft;
|
||||
|
||||
/**
|
||||
* 右上角
|
||||
*/
|
||||
private Point topRight;
|
||||
|
||||
/**
|
||||
* 右下角
|
||||
*/
|
||||
private Point bottomRight;
|
||||
|
||||
/**
|
||||
* 左下角
|
||||
*/
|
||||
private Point bottomLeft;
|
||||
|
||||
public RotatedBox(Point topLeft, Point topRight, Point bottomRight, Point bottomLeft) {
|
||||
this.topLeft = topLeft;
|
||||
this.topRight = topRight;
|
||||
this.bottomRight = bottomRight;
|
||||
this.bottomLeft = bottomLeft;
|
||||
}
|
||||
|
||||
public RotatedBox() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package cn.smartjavaai.common.enums;
|
||||
|
||||
/**
|
||||
* 视频源类型枚举
|
||||
* @author dwj
|
||||
* @date 2025/8/27
|
||||
*/
|
||||
public enum VideoSourceType {
|
||||
STREAM, // RTSP 或 HTTP 流
|
||||
FILE, // 本地视频文件
|
||||
CAMERA; // 本地摄像头
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package cn.smartjavaai.common.utils;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* @author dwj
|
||||
*/
|
||||
public class DJLCommonUtils {
|
||||
|
||||
/**
|
||||
* 检查模型目录中是否存在 "serving.properties" 文件
|
||||
*
|
||||
* @param modelPath 模型目录路径
|
||||
* @return true 表示存在,false 表示不存在
|
||||
*/
|
||||
public static boolean isServingPropertiesExists(Path modelPath) {
|
||||
if (modelPath == null || !Files.exists(modelPath)) {
|
||||
return false;
|
||||
}
|
||||
// 确定目录路径
|
||||
Path dirPath = Files.isDirectory(modelPath) ? modelPath : modelPath.getParent();
|
||||
if (dirPath == null) {
|
||||
return false; // 可能是根目录的文件
|
||||
}
|
||||
|
||||
// 判断目录下的 serving.properties 是否存在
|
||||
Path servingFile = dirPath.resolve("serving.properties");
|
||||
return Files.exists(servingFile);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -139,7 +139,7 @@ public class ImageUtils {
|
||||
* @param width
|
||||
* @param height
|
||||
*/
|
||||
public static void drawImageRect(Image image, int x, int y, int width, int height) {
|
||||
public static void drawBufferedImageRect(Image image, int x, int y, int width, int height) {
|
||||
// 将绘制图像转换为Graphics2D
|
||||
BufferedImage bufferedImage = (BufferedImage)image.getWrappedImage();
|
||||
Graphics2D g = (Graphics2D) bufferedImage.getGraphics();
|
||||
@@ -501,4 +501,7 @@ public class ImageUtils {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package cn.smartjavaai.common.utils;
|
||||
|
||||
import ai.djl.modality.cv.output.Rectangle;
|
||||
import ai.djl.modality.cv.util.NDImageUtils;
|
||||
import ai.djl.ndarray.NDArray;
|
||||
import ai.djl.ndarray.NDManager;
|
||||
@@ -121,4 +122,27 @@ public class LetterBoxUtils {
|
||||
return boxes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复缩放后的 box(左上角坐标)
|
||||
* @param rectangle
|
||||
* @param scale
|
||||
* @param origImageWidth
|
||||
* @param origImageHeight
|
||||
*/
|
||||
public static Rectangle restoreBox(Rectangle rectangle, float scale, int origImageWidth, int origImageHeight, int inputWidth, int inputHeight){
|
||||
double paddingWidth = (inputWidth - origImageWidth * scale) / 2;
|
||||
double paddingHeight = (inputHeight - origImageHeight * scale) / 2;
|
||||
|
||||
// 去掉 padding
|
||||
double x_noPad = rectangle.getX() - paddingWidth;
|
||||
double y_noPad = rectangle.getY() - paddingHeight;
|
||||
|
||||
//模型输出就是原图坐标
|
||||
double x1 = x_noPad / scale / origImageWidth;
|
||||
double y1 = y_noPad / scale / origImageHeight;
|
||||
double boxW = rectangle.getWidth() / scale / origImageWidth ;
|
||||
double boxH = rectangle.getHeight() / scale / origImageHeight;
|
||||
return new Rectangle(x1, y1, boxW, boxH);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
package cn.smartjavaai.common.utils;
|
||||
|
||||
import ai.djl.ndarray.NDArray;
|
||||
import ai.djl.ndarray.NDArrays;
|
||||
import ai.djl.ndarray.NDList;
|
||||
import ai.djl.ndarray.NDManager;
|
||||
import ai.djl.ndarray.types.DataType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -64,4 +68,43 @@ public class NMSUtils {
|
||||
return keep.stream().mapToInt(i -> i).toArray();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 批量执行 NMS,输入 NDArray 形式的 boxes、scores 和 idxs,返回保留的索引列表
|
||||
*
|
||||
* @param boxes NDArray 形状为 (N, 4),格式为 [x1, y1, x2, y2]
|
||||
* @param scores NDArray 形状为 (N,) 或 (N,1),每个 box 的置信度
|
||||
* @param idxs NDArray 形状为 (N,),每个 box 对应的 batch id
|
||||
* @param iouThreshold IOU 阈值,超过该阈值则认为有
|
||||
* @return 批量保留框的索引列表
|
||||
*
|
||||
*/
|
||||
public static NDArray batchedNms(NDArray boxes, NDArray scores, NDArray idxs, float iouThreshold, NDManager manager) {
|
||||
List<NDArray> keepList = new ArrayList<>();
|
||||
// 获取唯一 batch id
|
||||
NDArray uniqueIdxs = idxs.unique().get(0);
|
||||
for (long batchId : uniqueIdxs.toLongArray()) {
|
||||
// 找出当前 batch 的框
|
||||
NDArray mask = idxs.eq(batchId);
|
||||
NDArray batchBoxes = boxes.get(mask);
|
||||
NDArray batchScores = scores.get(mask);
|
||||
// 执行单 batch NMS
|
||||
int[] keepIndices = nms(batchBoxes, batchScores, iouThreshold);
|
||||
if (keepIndices.length > 0) {
|
||||
// 将局部索引映射回全局索引
|
||||
NDArray globalIndices = manager.arange(boxes.getShape().get(0))
|
||||
.get(mask)
|
||||
.toType(DataType.INT64, false)
|
||||
.get(manager.create(keepIndices));
|
||||
|
||||
keepList.add(globalIndices);
|
||||
}
|
||||
}
|
||||
if (keepList.isEmpty()) {
|
||||
return manager.create(new long[0]);
|
||||
}
|
||||
return NDArrays.concat(new NDList(keepList));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
package cn.smartjavaai.common.utils;
|
||||
|
||||
import ai.djl.modality.cv.Image;
|
||||
import ai.djl.modality.cv.ImageFactory;
|
||||
import ai.djl.ndarray.NDArray;
|
||||
import ai.djl.ndarray.NDManager;
|
||||
import ai.djl.util.RandomUtils;
|
||||
import cn.smartjavaai.common.entity.DetectionInfo;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.opencv.core.*;
|
||||
import org.opencv.core.Point;
|
||||
import org.opencv.imgproc.Imgproc;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.DataBufferByte;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* OpenCV 工具类
|
||||
*/
|
||||
public class OpenCVUtils {
|
||||
/**
|
||||
* canny算法,边缘检测
|
||||
*
|
||||
* @param src
|
||||
* @return
|
||||
*/
|
||||
public static Mat canny(Mat src) {
|
||||
Mat mat = src.clone();
|
||||
Imgproc.Canny(src, mat, 100, 200);
|
||||
return mat;
|
||||
}
|
||||
|
||||
/**
|
||||
* 画线
|
||||
*
|
||||
* @param mat
|
||||
* @param point1
|
||||
* @param point2
|
||||
*/
|
||||
public static void line(Mat mat, Point point1, Point point2) {
|
||||
Imgproc.line(mat, point1, point2, new Scalar(255, 255, 255), 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* NDArray to opencv_core.Mat
|
||||
*
|
||||
* @param manager
|
||||
* @param srcPoints
|
||||
* @param dstPoints
|
||||
* @return
|
||||
*/
|
||||
public static Mat toOpenCVMat(NDManager manager, NDArray srcPoints, NDArray dstPoints) {
|
||||
NDArray svdMat = SVDUtils.transformationFromPoints(manager, srcPoints, dstPoints);
|
||||
double[] doubleArray = svdMat.toDoubleArray();
|
||||
Mat newSvdMat = new Mat(2, 3, CvType.CV_64F);
|
||||
for (int i = 0; i < 2; i++) {
|
||||
for (int j = 0; j < 3; j++) {
|
||||
newSvdMat.put(i, j, doubleArray[i * 3 + j]);
|
||||
}
|
||||
}
|
||||
return newSvdMat;
|
||||
}
|
||||
|
||||
/**
|
||||
* double[][] points array to Mat
|
||||
* @param points
|
||||
* @return
|
||||
*/
|
||||
public static Mat toOpenCVMat(double[][] points) {
|
||||
Mat mat = new Mat(5, 2, CvType.CV_64F);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
for (int j = 0; j < 2; j++) {
|
||||
mat.put(i, j, points[i * 5 + j]);
|
||||
}
|
||||
}
|
||||
return mat;
|
||||
}
|
||||
|
||||
/**
|
||||
* 变换矩阵的逆矩阵
|
||||
*
|
||||
* @param src
|
||||
* @return
|
||||
*/
|
||||
public static Mat invertAffineTransform(Mat src) {
|
||||
Mat dst = src.clone();
|
||||
Imgproc.invertAffineTransform(src, dst);
|
||||
return dst;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mat to BufferedImage
|
||||
*
|
||||
* @param mat
|
||||
* @return
|
||||
*/
|
||||
public static BufferedImage mat2Image(Mat mat) {
|
||||
int width = mat.width();
|
||||
int height = mat.height();
|
||||
byte[] data = new byte[width * height * (int) mat.elemSize()];
|
||||
Imgproc.cvtColor(mat, mat, 4);
|
||||
mat.get(0, 0, data);
|
||||
BufferedImage ret = new BufferedImage(width, height, 5);
|
||||
ret.getRaster().setDataElements(0, 0, width, height, data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* BufferedImage to Mat
|
||||
*
|
||||
* @param img
|
||||
* @return
|
||||
*/
|
||||
public static Mat image2Mat(BufferedImage img) {
|
||||
int width = img.getWidth();
|
||||
int height = img.getHeight();
|
||||
|
||||
// 强制转换为 TYPE_3BYTE_BGR,自动去除透明通道
|
||||
BufferedImage convertedImg = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
|
||||
Graphics2D g2d = convertedImg.createGraphics();
|
||||
g2d.drawImage(img, 0, 0, null);
|
||||
g2d.dispose();
|
||||
|
||||
byte[] data = ((DataBufferByte) convertedImg.getRaster().getDataBuffer()).getData();
|
||||
Mat mat = new Mat(height, width, CvType.CV_8UC3);
|
||||
mat.put(0, 0, data);
|
||||
return mat;
|
||||
}
|
||||
|
||||
/**
|
||||
* 透视变换
|
||||
*
|
||||
* @param src
|
||||
* @param srcPoints
|
||||
* @param dstPoints
|
||||
* @return
|
||||
*/
|
||||
public static Mat perspectiveTransform(Mat src, Mat srcPoints, Mat dstPoints) {
|
||||
Mat dst = src.clone();
|
||||
Mat warp_mat = Imgproc.getPerspectiveTransform(srcPoints, dstPoints);
|
||||
Imgproc.warpPerspective(src, dst, warp_mat, dst.size());
|
||||
warp_mat.release();
|
||||
return dst;
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制矩形框和文字
|
||||
*
|
||||
* @param image
|
||||
* @param detectionInfoList
|
||||
*/
|
||||
public static void drawRectAndText(Image image, List<DetectionInfo> detectionInfoList) {
|
||||
if(CollectionUtils.isEmpty(detectionInfoList))
|
||||
return;
|
||||
for(DetectionInfo detectionInfo : detectionInfoList){
|
||||
drawRectAndText(image, detectionInfo);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 绘制矩形框和文字
|
||||
*
|
||||
* @param image
|
||||
* @param detectionInfo
|
||||
*/
|
||||
public static void drawRectAndText(Image image, DetectionInfo detectionInfo) {
|
||||
|
||||
|
||||
Mat mat = (Mat)image.getWrappedImage();
|
||||
if (image == null) return;
|
||||
int x = detectionInfo.getDetectionRectangle().getX();
|
||||
int y = detectionInfo.getDetectionRectangle().getY();
|
||||
int width = detectionInfo.getDetectionRectangle().getWidth();
|
||||
int height = detectionInfo.getDetectionRectangle().getHeight();
|
||||
|
||||
Scalar rectangleColor = new Scalar((double)RandomUtils.nextInt(178), (double)RandomUtils.nextInt(178), (double)RandomUtils.nextInt(178));
|
||||
|
||||
// 绘制矩形框
|
||||
Point pt1 = new Point(x, y);
|
||||
Point pt2 = new Point(x + width, y + height);
|
||||
Imgproc.rectangle(mat, pt1, pt2, rectangleColor, 2);
|
||||
|
||||
// 绘制文字
|
||||
if (Objects.nonNull(detectionInfo.getObjectDetInfo()) && StringUtils.isNotBlank(detectionInfo.getObjectDetInfo().getClassName())) {
|
||||
String className = detectionInfo.getObjectDetInfo().getClassName();
|
||||
Size size = Imgproc.getTextSize(className, 1, 1.3, 1, (int[])null);
|
||||
Point br = new Point((double)x + size.width + 4.0, (double)y + size.height + 4.0);
|
||||
Imgproc.rectangle(mat, pt1, br, rectangleColor, -1);
|
||||
Point point = new Point((double)x, (double)y + size.height + 2.0);
|
||||
Scalar color = new Scalar(255.0, 255.0, 255.0);
|
||||
Imgproc.putText(mat, className, point, 1, 1.3, color, 1);
|
||||
}
|
||||
image = ImageFactory.getInstance().fromImage(mat);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在Mat上绘制矩形框和文字
|
||||
*
|
||||
* @param mat 待绘制的Mat
|
||||
* @param x 矩形左上角X
|
||||
* @param y 矩形左上角Y
|
||||
* @param width 矩形宽度
|
||||
* @param height 矩形高度
|
||||
* @param color 框的颜色,例如 new Scalar(0, 255, 0) 绿色
|
||||
* @param thickness 框线宽度
|
||||
* @param text 需要绘制的文字,可以为null或空
|
||||
* @param fontScale 文字缩放比例
|
||||
* @param textColor 文字颜色
|
||||
*/
|
||||
public static void drawRectAndText(Mat mat,
|
||||
int x, int y, int width, int height,
|
||||
Scalar color, int thickness,
|
||||
String text, double fontScale, Scalar textColor) {
|
||||
|
||||
if (mat == null || mat.empty()) return;
|
||||
|
||||
// 绘制矩形框
|
||||
Point pt1 = new Point(x, y);
|
||||
Point pt2 = new Point(x + width, y + height);
|
||||
Imgproc.rectangle(mat, pt1, pt2, color, thickness);
|
||||
|
||||
// 绘制文字
|
||||
if (text != null && !text.isEmpty()) {
|
||||
int baseline[] = new int[1];
|
||||
Size textSize = Imgproc.getTextSize(text, Imgproc.FONT_HERSHEY_SIMPLEX, fontScale, thickness, baseline);
|
||||
// 保证文字不超出矩形
|
||||
Point textOrg = new Point(x, y - 5 < 0 ? y + textSize.height + 5 : y - 5);
|
||||
Imgproc.putText(mat, text, textOrg, Imgproc.FONT_HERSHEY_SIMPLEX, fontScale, textColor, thickness);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Bytedeco 的 Mat 转换为 OpenCV 官方的 Mat
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<smartjavaai.version>1.0.23</smartjavaai.version>
|
||||
<smartjavaai.version>1.0.24</smartjavaai.version>
|
||||
<!--如果打包运行,需要替换成你的main-->
|
||||
<exec.mainClass>smartai.examples.face.facedet.FaceDetDemo</exec.mainClass>
|
||||
|
||||
@@ -255,6 +255,14 @@
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>ai.djl.pytorch</groupId>
|
||||
<artifactId>pytorch-native-cpu</artifactId>
|
||||
<classifier>linux-aarch64</classifier>
|
||||
<scope>runtime</scope>
|
||||
<version>2.5.1</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
|
||||
* with the License. A copy of the License is located at
|
||||
*
|
||||
* http://aws.amazon.com/apache2.0/
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
|
||||
* OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*/
|
||||
package smartai.examples.face;
|
||||
|
||||
import ai.djl.ModelException;
|
||||
import ai.djl.inference.Predictor;
|
||||
import ai.djl.modality.Classifications;
|
||||
import ai.djl.modality.Input;
|
||||
import ai.djl.modality.Output;
|
||||
import ai.djl.modality.cv.Image;
|
||||
import ai.djl.ndarray.NDList;
|
||||
import ai.djl.repository.zoo.Criteria;
|
||||
import ai.djl.repository.zoo.ZooModel;
|
||||
import ai.djl.translate.NoBatchifyTranslator;
|
||||
import ai.djl.translate.TranslateException;
|
||||
import ai.djl.translate.TranslatorContext;
|
||||
import ai.djl.util.JsonUtils;
|
||||
import ai.djl.util.Utils;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Type;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class PythonTranslator implements NoBatchifyTranslator<byte[], Classifications> {
|
||||
|
||||
private ZooModel<Input, Output> model;
|
||||
private Predictor<Input, Output> predictor;
|
||||
|
||||
@Override
|
||||
public void prepare(TranslatorContext ctx) throws ModelException, IOException {
|
||||
if (predictor == null) {
|
||||
Criteria<Input, Output> criteria =
|
||||
Criteria.builder()
|
||||
.setTypes(Input.class, Output.class)
|
||||
.optModelPath(Paths.get("src/test/python"))
|
||||
.optEngine("Python")
|
||||
.build();
|
||||
model = criteria.loadModel();
|
||||
predictor = model.newPredictor();
|
||||
}
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public NDList processInput(TranslatorContext ctx, String url)
|
||||
// throws IOException, TranslateException {
|
||||
// Input input = new Input();
|
||||
// try (InputStream is = new URL(url).openStream()) {
|
||||
// input.add("data", Utils.toByteArray(is));
|
||||
// }
|
||||
// input.addProperty("Content-Type", "image/jpeg");
|
||||
// // calling preprocess() function in model.py
|
||||
// input.addProperty("handler", "preprocess");
|
||||
// Output output = predictor.predict(input);
|
||||
// if (output.getCode() != 200) {
|
||||
// throw new TranslateException("Python preprocess() failed: " + output.getMessage());
|
||||
// }
|
||||
//
|
||||
// return output.getDataAsNDList(ctx.getNDManager());
|
||||
// }
|
||||
|
||||
@Override
|
||||
public NDList processInput(TranslatorContext ctx, byte[] image)
|
||||
throws IOException, TranslateException {
|
||||
Input input = new Input();
|
||||
input.add("data", image);
|
||||
input.addProperty("Content-Type", "image/jpeg");
|
||||
// calling preprocess() function in model.py
|
||||
input.addProperty("handler", "preprocess");
|
||||
Output output = predictor.predict(input);
|
||||
if (output.getCode() != 200) {
|
||||
throw new TranslateException("Python preprocess() failed: " + output.getMessage());
|
||||
}
|
||||
return output.getDataAsNDList(ctx.getNDManager());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Classifications processOutput(TranslatorContext ctx, NDList list)
|
||||
throws TranslateException {
|
||||
Input input = new Input();
|
||||
input.add("data", list);
|
||||
// calling postprocess() function in processing.py
|
||||
input.addProperty("handler", "postprocess");
|
||||
Output output = predictor.predict(input);
|
||||
if (output.getCode() != 200) {
|
||||
throw new TranslateException("Python postprocess() failed: " + output.getMessage());
|
||||
}
|
||||
|
||||
String json = output.getData().getAsString();
|
||||
System.out.println("json:" + json);
|
||||
return null;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (predictor != null) {
|
||||
predictor.close();
|
||||
model.close();
|
||||
predictor = null;
|
||||
model = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package smartai.examples.face;
|
||||
|
||||
import ai.djl.Application;
|
||||
import ai.djl.Device;
|
||||
import ai.djl.MalformedModelException;
|
||||
import ai.djl.inference.Predictor;
|
||||
import ai.djl.modality.Classifications;
|
||||
import ai.djl.modality.audio.Audio;
|
||||
import ai.djl.modality.audio.AudioFactory;
|
||||
import ai.djl.modality.audio.translator.SpeechRecognitionTranslatorFactory;
|
||||
import ai.djl.repository.Artifact;
|
||||
import ai.djl.repository.MRL;
|
||||
import ai.djl.repository.zoo.Criteria;
|
||||
import ai.djl.repository.zoo.ModelNotFoundException;
|
||||
import ai.djl.repository.zoo.ModelZoo;
|
||||
import ai.djl.repository.zoo.ZooModel;
|
||||
import ai.djl.translate.TranslateException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author dwj
|
||||
* @date 2025/7/29
|
||||
*/
|
||||
@Slf4j
|
||||
public class Test {
|
||||
|
||||
public static void main(String[] args) throws ModelNotFoundException, MalformedModelException, IOException, TranslateException {
|
||||
// PythonTranslator translator = new PythonTranslator();
|
||||
// Criteria<byte[], Classifications> criteria =
|
||||
// Criteria.builder()
|
||||
// .setTypes(byte[].class, Classifications.class)
|
||||
// .optModelPath(Paths.get("/Users/wenjie/Documents/develop/model/arcfaceresnet100-11-int8.onnx"))
|
||||
// .optEngine("OnnxRuntime")
|
||||
// .optTranslator(translator)
|
||||
// .build();
|
||||
// String path = "/Users/wenjie/Downloads/facetest/jsy.jpg";
|
||||
// try (ZooModel<byte[], Classifications> model = criteria.loadModel();
|
||||
// Predictor<byte[], Classifications> predictor = model.newPredictor()) {
|
||||
// byte[] data = Files.readAllBytes(Paths.get(path));
|
||||
// Classifications ret = predictor.predict(data);
|
||||
// System.out.println(ret);
|
||||
// }
|
||||
//
|
||||
// // unload python model
|
||||
// translator.close();
|
||||
|
||||
|
||||
// Load model.
|
||||
// Wav2Vec2 model is a speech model that accepts a float array corresponding to the raw
|
||||
// waveform of the speech signal.
|
||||
|
||||
// String url = "/Users/wenjie/Downloads/20210601_u2++_conformer_exp/final.pt";
|
||||
// Criteria<Audio, String> criteria =
|
||||
// Criteria.builder()
|
||||
// .setTypes(Audio.class, String.class)
|
||||
//// .optModelUrls(url)
|
||||
// .optModelPath(Paths.get(url))
|
||||
// .optDevice(Device.cpu()) // torchscript model only support CPU
|
||||
// .optTranslatorFactory(new SpeechRecognitionTranslatorFactory())
|
||||
//// .optModelName("data.pkl")
|
||||
// .optEngine("PyTorch")
|
||||
// .build();
|
||||
//
|
||||
// // Read in audio file
|
||||
// String wave = "https://resources.djl.ai/audios/speech.wav";
|
||||
// Audio audio = AudioFactory.newInstance().fromUrl(wave);
|
||||
// try (ZooModel<Audio, String> model = criteria.loadModel();
|
||||
// Predictor<Audio, String> predictor = model.newPredictor()) {
|
||||
// String result = predictor.predict(audio);
|
||||
// log.info("Result: {}", result);
|
||||
// }
|
||||
|
||||
boolean withArtifacts =
|
||||
args.length > 0 && ("--artifact".equals(args[0]) || "-a".equals(args[0]));
|
||||
if (!withArtifacts) {
|
||||
log.info("============================================================");
|
||||
log.info("user ./gradlew listModel --args='-a' to show artifact detail");
|
||||
log.info("============================================================");
|
||||
}
|
||||
Map<Application, List<Artifact>> models = ModelZoo.listModels();
|
||||
for (Map.Entry<Application, List<Artifact>> entry : models.entrySet()) {
|
||||
String appName = entry.getKey().toString();
|
||||
for (Artifact artifact : entry.getValue()) {
|
||||
if (withArtifacts) {
|
||||
log.info("{} djl://{}", appName, artifact);
|
||||
} else {
|
||||
log.info("{} {}", appName, artifact);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,7 +67,7 @@ public class FaceDetDemo {
|
||||
//高精度模型,速度慢
|
||||
config.setModelEnum(FaceDetModelEnum.RETINA_FACE);//人脸检测模型
|
||||
//下载模型并替换本地路径,下载地址:https://pan.baidu.com/s/10l22x5fRz_gwLr8EAHa1Jg?pwd=1234 提取码: 1234
|
||||
config.setModelPath("/Users/xxx/Documents/develop/model/retinaface.pt");
|
||||
config.setModelPath("/Users/wenjie/Documents/develop/face_model/retinaface.pt");
|
||||
config.setConfidenceThreshold(FaceDetectConstant.DEFAULT_CONFIDENCE_THRESHOLD);//只返回相似度大于该值的人脸
|
||||
config.setNmsThresh(FaceDetectConstant.NMS_THRESHOLD);//用于去除重复的人脸框,当两个框的重叠度超过该值时,只保留一个
|
||||
return FaceDetModelFactory.getInstance().getModel(config);
|
||||
@@ -95,12 +95,21 @@ public class FaceDetDemo {
|
||||
@Test
|
||||
public void testFaceDetect(){
|
||||
try {
|
||||
FaceDetModel faceModel = FaceDetModelFactory.getInstance().getModel();
|
||||
FaceDetModel faceModel = getFaceDetModel();
|
||||
R<DetectionResponse> detectedResult = faceModel.detect(imgPath);
|
||||
if(detectedResult.isSuccess()){
|
||||
log.info("人脸检测结果:{}", JSONObject.toJSONString(detectedResult.getData()));
|
||||
// if(detectedResult.isSuccess()){
|
||||
// log.info("人脸检测结果:{}", JSONObject.toJSONString(detectedResult.getData()));
|
||||
// }else{
|
||||
// log.info("人脸检测失败:{}", detectedResult.getMessage());
|
||||
// }
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
R<DetectionResponse> detectedResult2 = faceModel.detect("/Users/wenjie/Downloads/facetest/surprise.png");
|
||||
log.info("耗时:{}", System.currentTimeMillis() - start);
|
||||
if(detectedResult2.isSuccess()){
|
||||
log.info("人脸检测结果:{}", JSONObject.toJSONString(detectedResult2.getData()));
|
||||
}else{
|
||||
log.info("人脸检测失败:{}", detectedResult.getMessage());
|
||||
log.info("人脸检测失败:{}", detectedResult2.getMessage());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
||||
@@ -63,7 +63,7 @@ public class FaceRecDemo {
|
||||
//高精度模型,速度慢
|
||||
config.setModelEnum(FaceDetModelEnum.RETINA_FACE);//人脸检测模型
|
||||
//下载模型并替换本地路径,下载地址:https://pan.baidu.com/s/10l22x5fRz_gwLr8EAHa1Jg?pwd=1234 提取码: 1234
|
||||
config.setModelPath("/Users/xxx/Documents/develop/model/retinaface.pt");
|
||||
// config.setModelPath("/Users/wenjie/Documents/develop/model/retinaface.pt");
|
||||
config.setConfidenceThreshold(FaceDetectConstant.DEFAULT_CONFIDENCE_THRESHOLD);//只返回相似度大于该值的人脸
|
||||
config.setNmsThresh(FaceDetectConstant.NMS_THRESHOLD);//用于去除重复的人脸框,当两个框的重叠度超过该值时,只保留一个
|
||||
config.setDevice(device);
|
||||
@@ -170,7 +170,7 @@ public class FaceRecDemo {
|
||||
FaceRecConfig config = new FaceRecConfig();
|
||||
//高精度模型,速度慢, 追求速度请更换高速模型,具体其他模型参数可以查看文档:http://doc.smartjavaai.cn/face.html
|
||||
config.setModelEnum(FaceRecModelEnum.ELASTIC_FACE_MODEL);//人脸检测模型
|
||||
config.setModelPath("/Users/xxx/Documents/develop/model/elasticface.pt");
|
||||
config.setModelPath("/Users/wenjie/Documents/develop/model/elasticface.pt");
|
||||
//裁剪人脸:如果图片已经是裁剪过的,则请将此参数设置为false
|
||||
config.setCropFace(true);
|
||||
//开启人脸对齐:适用于人脸不正的场景,开启将提升人脸特征准确度,关闭可以提升性能
|
||||
|
||||
126
examples/face-example/src/test/python/model.py
Normal file
@@ -0,0 +1,126 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file
|
||||
# except in compliance with the License. A copy of the License is located at
|
||||
#
|
||||
# http://aws.amazon.com/apache2.0/
|
||||
#
|
||||
# or in the "LICENSE.txt" file accompanying this file. This file is distributed on an "AS IS"
|
||||
# BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied. See the License for
|
||||
# the specific language governing permissions and limitations under the License.
|
||||
"""
|
||||
PyTorch resnet18 pre/post processing example.
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
from typing import Optional, Any
|
||||
import sklearn
|
||||
|
||||
import torch
|
||||
import torch.nn.functional as F
|
||||
from torchvision import transforms
|
||||
|
||||
from djl_python import Input
|
||||
from djl_python import Output
|
||||
|
||||
|
||||
class Processing(object):
|
||||
|
||||
def __init__(self):
|
||||
self.topK = 5
|
||||
self.image_processing = None
|
||||
self.mapping = None
|
||||
self.initialized = False
|
||||
|
||||
def initialize(self, properties: dict):
|
||||
"""
|
||||
Initialize model.
|
||||
"""
|
||||
self.image_processing = transforms.Compose([
|
||||
transforms.Resize(112),
|
||||
transforms.CenterCrop(112),
|
||||
transforms.ToTensor(),
|
||||
transforms.Normalize(mean=[0.485, 0.456, 0.406],
|
||||
std=[0.229, 0.224, 0.225])
|
||||
])
|
||||
#self.mapping = self.load_label_mapping("index_to_name.json")
|
||||
self.initialized = True
|
||||
|
||||
def preprocess(self, inputs: Input) -> Output:
|
||||
outputs = Output()
|
||||
try:
|
||||
batch = inputs.get_batches()
|
||||
images = []
|
||||
for i, item in enumerate(batch):
|
||||
image = self.image_processing(item.get_as_image())
|
||||
images.append(image)
|
||||
images = torch.stack(images)
|
||||
outputs.add_as_numpy(images.detach().numpy())
|
||||
outputs.add_property("content-type", "tensor/ndlist")
|
||||
except Exception as e:
|
||||
logging.exception("pre-process failed")
|
||||
# error handling
|
||||
outputs = Output().error(str(e))
|
||||
|
||||
return outputs
|
||||
|
||||
def postprocess(self, inputs: Input) -> Output:
|
||||
outputs = Output()
|
||||
try:
|
||||
data = inputs.get_as_numpy(0)[0]
|
||||
item = torch.from_numpy(data)
|
||||
print("data shape:", item.shape)
|
||||
embedding = sklearn.preprocessing.normalize(item).flatten()
|
||||
outputs.add(embedding)
|
||||
except Exception as e:
|
||||
logging.exception("post-process failed")
|
||||
# error handling
|
||||
outputs = Output().error(str(e))
|
||||
|
||||
return outputs
|
||||
|
||||
@staticmethod
|
||||
def load_label_mapping(mapping_file_path: Any) -> dict:
|
||||
if not os.path.isfile(mapping_file_path):
|
||||
raise Exception('mapping file not found: ' + mapping_file_path)
|
||||
|
||||
with open(mapping_file_path) as f:
|
||||
mapping = json.load(f)
|
||||
if not isinstance(mapping, dict):
|
||||
raise Exception('mapping file should be in "class":"label" format')
|
||||
|
||||
for key, value in mapping.items():
|
||||
new_value = value
|
||||
if isinstance(new_value, list):
|
||||
new_value = value[-1]
|
||||
if not isinstance(new_value, str):
|
||||
raise Exception(
|
||||
'labels in mapping must be either str or [str]')
|
||||
mapping[key] = new_value
|
||||
return mapping
|
||||
|
||||
|
||||
_service = Processing()
|
||||
|
||||
|
||||
def preprocess(inputs: Input) -> Output:
|
||||
return _service.preprocess(inputs)
|
||||
|
||||
|
||||
def postprocess(inputs: Input) -> Output:
|
||||
return _service.postprocess(inputs)
|
||||
|
||||
|
||||
def handle(inputs: Input) -> Optional[Output]:
|
||||
"""
|
||||
Default handler function
|
||||
"""
|
||||
if not _service.initialized:
|
||||
# stateful model
|
||||
_service.initialize(inputs.get_properties())
|
||||
|
||||
return None
|
||||
|
After Width: | Height: | Size: 1.8 MiB |
@@ -12,7 +12,7 @@
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<smartjavaai.version>1.0.23</smartjavaai.version>
|
||||
<smartjavaai.version>1.0.24</smartjavaai.version>
|
||||
<!--如果打包运行,需要替换成你的main-->
|
||||
<exec.mainClass>smartai.examples.objectdetection.ObjectDetection</exec.mainClass>
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.smartjavaai</groupId>
|
||||
<artifactId>smartjavaai-bom</artifactId>
|
||||
<artifactId>bom</artifactId>
|
||||
<version>${smartjavaai.version}</version>
|
||||
<type>pom</type>
|
||||
<!-- 注意这里是import -->
|
||||
@@ -94,7 +94,7 @@
|
||||
<!--目标检测模块-->
|
||||
<dependency>
|
||||
<groupId>cn.smartjavaai</groupId>
|
||||
<artifactId>smartjavaai-objectdetection</artifactId>
|
||||
<artifactId>vision</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
@@ -2,6 +2,9 @@ package smartai.examples.objectdetection;
|
||||
|
||||
import ai.djl.Application;
|
||||
import ai.djl.MalformedModelException;
|
||||
import ai.djl.ModelException;
|
||||
import ai.djl.inference.Predictor;
|
||||
import ai.djl.modality.Classifications;
|
||||
import ai.djl.modality.cv.Image;
|
||||
import ai.djl.modality.cv.ImageFactory;
|
||||
import ai.djl.modality.cv.output.*;
|
||||
@@ -11,6 +14,7 @@ import ai.djl.repository.zoo.ModelNotFoundException;
|
||||
import ai.djl.repository.zoo.ModelZoo;
|
||||
import ai.djl.repository.zoo.ZooModel;
|
||||
import ai.djl.training.util.ProgressBar;
|
||||
import ai.djl.translate.TranslateException;
|
||||
import cn.smartjavaai.common.config.Config;
|
||||
import cn.smartjavaai.common.entity.DetectionInfo;
|
||||
import cn.smartjavaai.common.entity.DetectionRectangle;
|
||||
@@ -42,6 +46,7 @@ import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
@@ -64,6 +69,32 @@ public class ObjectDetection {
|
||||
//设备类型
|
||||
public static DeviceEnum device = DeviceEnum.CPU;
|
||||
|
||||
public static void main(String[] args) throws ModelException, TranslateException, IOException {
|
||||
Classifications classification = predict();
|
||||
log.info("{}", classification);
|
||||
}
|
||||
|
||||
|
||||
public static Classifications predict() throws IOException, ModelException, TranslateException {
|
||||
|
||||
Config.setCachePath("/Users/wenjie/smartjavaai_cache");
|
||||
URL url = new URL("https://resources.djl.ai/images/action_dance.jpg");
|
||||
// Use DJL PyTorch model zoo model
|
||||
Criteria<URL, Classifications> criteria =
|
||||
Criteria.builder()
|
||||
.setTypes(URL.class, Classifications.class)
|
||||
.optModelUrls(
|
||||
"djl://ai.djl.mxnet/action_recognition")
|
||||
.optEngine("MXNet")
|
||||
.optProgress(new ProgressBar())
|
||||
.build();
|
||||
|
||||
try (ZooModel<URL, Classifications> inception = criteria.loadModel();
|
||||
Predictor<URL, Classifications> action = inception.newPredictor()) {
|
||||
return action.predict(url);
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeAll() throws IOException {
|
||||
//修改缓存路径
|
||||
@@ -95,6 +126,8 @@ public class ObjectDetection {
|
||||
try {
|
||||
DetectorModelConfig config = new DetectorModelConfig();
|
||||
config.setModelEnum(DetectorModelEnum.SSD_300_RESNET50);//检测模型,目前支持19种预置模型
|
||||
config.setModelEnum(DetectorModelEnum.YOLOV12_OFFICIAL);
|
||||
config.setModelPath("yolov11s");
|
||||
// 指定允许的类别
|
||||
// config.setAllowedClasses(Arrays.asList("person"));
|
||||
//指定返回检测数量
|
||||
@@ -205,6 +238,32 @@ public class ObjectDetection {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tensorflow目标检测
|
||||
*/
|
||||
@Test
|
||||
public void objectDetection3(){
|
||||
try {
|
||||
DetectorModelConfig config = new DetectorModelConfig();
|
||||
config.setModelEnum(DetectorModelEnum.TENSORFLOW2_OFFICIAL);
|
||||
config.setModelPath("/Users/wenjie/Documents/develop/model/tensorflow/ssd_mobilenet_v2_320x320_coco17_tpu-8");
|
||||
// config.putCustomParam("synsetUrl", "https://raw.githubusercontent.com/tensorflow/models/master/research/object_detection/data/mscoco_label_map.pbtxt");
|
||||
// config.putCustomParam("synsetPath", "/Users/wenjie/Downloads/mscoco_label_map.pbtxt.txt");
|
||||
config.putCustomParam("synsetFileName", "mscoco.pbtxt");
|
||||
// 指定允许的类别
|
||||
// config.setAllowedClasses(Arrays.asList("person"));
|
||||
//指定返回检测数量
|
||||
config.setTopK(100);
|
||||
config.setDevice(device);
|
||||
DetectorModel detectorModel = ObjectDetectionModelFactory.getInstance().getModel(config);
|
||||
DetectionResponse detectionResponse = detectorModel.detect("src/main/resources/dog_bike_car.jpg");
|
||||
detectorModel.detectAndDraw("src/main/resources/dog_bike_car.jpg", "output/dog_bike_car_detect.jpg");
|
||||
log.info("目标检测结果:{}", JSONObject.toJSONString(detectionResponse));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 摄像头目标检测
|
||||
|
||||
BIN
examples/ocr-examples/output/ocr_4_recognized.jpg
Normal file
|
After Width: | Height: | Size: 682 KiB |
BIN
examples/ocr-examples/output/plate_recognized.jpg
Normal file
|
After Width: | Height: | Size: 143 KiB |
BIN
examples/ocr-examples/output/plate_recognized2.jpg
Normal file
|
After Width: | Height: | Size: 86 KiB |
5
examples/ocr-examples/output/table_ch2_result.html
Normal file
@@ -0,0 +1,5 @@
|
||||
<style>
|
||||
table { border-collapse: collapse; }
|
||||
td, th, table { border: 1px solid black; padding: 5px; }
|
||||
</style>
|
||||
<html><body><table><thead><tr><td>主要财务比率</td><td>2020</td><td>2021</td><td>2022E</td><td>2023E</td><td>2024E</td></tr></thead><tbody><tr><td>成长能力</td><td></td><td></td><td></td><td></td><td></td></tr><tr><td>营业收入</td><td>97.08%</td><td>33.28%</td><td>65.00%</td><td>42.10%</td><td>21.00%</td></tr><tr><td>营业利润</td><td>165.21%</td><td>22.38%</td><td>31.65%</td><td>64.55%</td><td>36.68%</td></tr><tr><td>归属於母公司净利润</td><td>164.75%</td><td>24.17%</td><td>39.44%</td><td>64.13%</td><td>38.63%</td></tr><tr><td>获利能力</td><td></td><td></td><td></td><td></td><td></td></tr><tr><td>毛利率</td><td>25.45%</td><td>23.01%</td><td>16.80%</td><td>17.00%</td><td>18.00%</td></tr><tr><td>净利率</td><td>13.98%</td><td>13.03%</td><td>11.01%</td><td>12.72%</td><td>14.57%</td></tr><tr><td>ROE</td><td>19.29%</td><td>19.25%</td><td>20.77%</td><td>47.11%</td><td>35.24%</td></tr><tr><td>ROIC</td><td>44.53%</td><td>41.55%</td><td>44.21%</td><td>32.59%</td><td>62.14%</td></tr><tr><td>偿债能力</td><td></td><td></td><td></td><td></td><td></td></tr><tr><td>资产负债率</td><td>48.28%</td><td>54.90%</td><td>57.79%</td><td>65.62%</td><td>58.84%</td></tr><tr><td>净负债率</td><td>-39.12%</td><td>-36.03%</td><td>6.62%</td><td>8.70%</td><td>5.28%</td></tr><tr><td>流动比率</td><td>1.77</td><td>1.74</td><td>1.60</td><td>1.41</td><td>1.65</td></tr><tr><td>速动比率</td><td>1.26</td><td>1.07</td><td>0.85</td><td>0.62</td><td>0.81</td></tr><tr><td>营运能力</td><td></td><td></td><td></td><td></td><td></td></tr><tr><td>应收账款周转率</td><td>5.16</td><td>4.59</td><td>4.11</td><td>5.24</td><td>5.24</td></tr><tr><td>存货周转率</td><td>3.48</td><td>2.89</td><td>2.55</td><td>2.77</td><td>2.63</td></tr><tr><td>总资产周转率</td><td>0.80</td><td>0.78</td><td>0.93</td><td>1.21</td><td>1.22</td></tr><tr><td>每股指标(元)</td><td></td><td></td><td></td><td></td><td></td></tr><tr><td>每股收益</td><td>0.84</td><td>1.04</td><td>1.45</td><td>2.38</td><td>3.30</td></tr><tr><td>每股经营现金流</td><td>0.03</td><td>0.04</td><td>-2.54</td><td>4.28</td><td>-1.13</td></tr><tr><td>每股净资产</td><td>4.34</td><td>5.40</td><td>6.97</td><td>5.05</td><td>9.35</td></tr><tr><td>估值比率</td><td></td><td></td><td></td><td></td><td></td></tr><tr><td>市盈率</td><td>41.30</td><td>33.26</td><td>23.85</td><td>14.53</td><td>10.48</td></tr><tr><td>市净率</td><td>7.97</td><td>6.40</td><td>4.95</td><td>6.85</td><td>3.69</td></tr><tr><td>EV/EBITDA</td><td>5.08</td><td>22.72</td><td>23.65</td><td>14.40</td><td>10.60</td></tr><tr><td>EV/EBIT</td><td>5.33</td><td>24.19</td><td>25.45</td><td>15.05</td><td>10.95</td></tr></tbody></table></body></html>
|
||||
BIN
examples/ocr-examples/output/table_ch2_result.jpg
Normal file
|
After Width: | Height: | Size: 276 KiB |