mirror of
https://github.com/geekwenjie/SmartJavaAI.git
synced 2026-09-10 03:28:49 +00:00
【人脸识别】 新增多种人脸识别模型
【底层优化】 支持自由选择 OpenCV 或 BufferedImage 作为图像引擎 【通用图像】 全部模型启用 Image 输入,支持各类图片格式与 Image 的互转 【模型管理】 优化模型生命周期,关闭后可重新创建 【人脸识别】 支持在人脸查询结果中绘制姓名标注 【人脸检测】 新增人脸裁剪功能 【修复】 修复若干已知问题,提升系统稳定性
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>cn.smartjavaai</groupId>
|
||||
<artifactId>smartjavaai-parent</artifactId>
|
||||
<version>1.0.24</version>
|
||||
<version>1.0.25</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>ocr</artifactId>
|
||||
@@ -42,7 +42,7 @@
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<version>1.0.24</version>
|
||||
<version>1.0.25</version>
|
||||
<name>ocr</name>
|
||||
<description>SmartJavaAI</description>
|
||||
<url>https://github.com/geekwenjie/SmartJavaAI</url>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package cn.smartjavaai.ocr.entity;
|
||||
|
||||
import ai.djl.modality.cv.Image;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -20,7 +21,7 @@ public class OcrInfo {
|
||||
|
||||
private String fullText;
|
||||
|
||||
private String base64Img;
|
||||
private transient Image drawnImage;
|
||||
|
||||
|
||||
public OcrInfo(List<List<OcrItem>> lineList, String fullText) {
|
||||
|
||||
@@ -158,6 +158,7 @@ public class OcrModelFactory {
|
||||
throw new OcrException(e);
|
||||
}
|
||||
model.loadModel(config);
|
||||
model.setFromFactory(true);
|
||||
return model;
|
||||
}
|
||||
|
||||
@@ -179,6 +180,7 @@ public class OcrModelFactory {
|
||||
throw new OcrException(e);
|
||||
}
|
||||
model.loadModel(config);
|
||||
model.setFromFactory(true);
|
||||
return model;
|
||||
}
|
||||
|
||||
@@ -199,6 +201,7 @@ public class OcrModelFactory {
|
||||
throw new OcrException(e);
|
||||
}
|
||||
model.loadModel(config);
|
||||
model.setFromFactory(true);
|
||||
return model;
|
||||
}
|
||||
|
||||
@@ -220,4 +223,61 @@ public class OcrModelFactory {
|
||||
log.debug("缓存目录:{}", Config.getCachePath());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 关闭所有已加载的模型
|
||||
*/
|
||||
public void closeAll() {
|
||||
commonDetModelMap.values().forEach(model -> {
|
||||
try {
|
||||
model.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
commonDetModelMap.clear();
|
||||
|
||||
commonRecModelMap.values().forEach(model -> {
|
||||
try {
|
||||
model.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
commonRecModelMap.clear();
|
||||
|
||||
directionModelMap.values().forEach(model -> {
|
||||
try {
|
||||
model.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
directionModelMap.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除缓存的检测模型
|
||||
* @param modelEnum
|
||||
*/
|
||||
public static void removeDetModelFromCache(CommonDetModelEnum modelEnum) {
|
||||
commonDetModelMap.remove(modelEnum);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除缓存的识别模型
|
||||
* @param modelEnum
|
||||
*/
|
||||
public static void removeRecModelFromCache(CommonRecModelEnum modelEnum) {
|
||||
commonRecModelMap.remove(modelEnum);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除缓存的方向分类模型
|
||||
* @param modelEnum
|
||||
*/
|
||||
public static void removeDirectionModelFromCache(DirectionModelEnum modelEnum) {
|
||||
directionModelMap.remove(modelEnum);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,9 +4,7 @@ import cn.smartjavaai.common.config.Config;
|
||||
import cn.smartjavaai.ocr.config.PlateDetModelConfig;
|
||||
import cn.smartjavaai.ocr.config.PlateRecModelConfig;
|
||||
import cn.smartjavaai.ocr.config.TableStructureConfig;
|
||||
import cn.smartjavaai.ocr.enums.PlateDetModelEnum;
|
||||
import cn.smartjavaai.ocr.enums.PlateRecModelEnum;
|
||||
import cn.smartjavaai.ocr.enums.TableStructureModelEnum;
|
||||
import cn.smartjavaai.ocr.enums.*;
|
||||
import cn.smartjavaai.ocr.exception.OcrException;
|
||||
import cn.smartjavaai.ocr.model.plate.CRNNPlateRecModel;
|
||||
import cn.smartjavaai.ocr.model.plate.PlateDetModel;
|
||||
@@ -133,6 +131,7 @@ public class PlateModelFactory {
|
||||
throw new OcrException(e);
|
||||
}
|
||||
model.loadModel(config);
|
||||
model.setFromFactory(true);
|
||||
return model;
|
||||
}
|
||||
|
||||
@@ -153,6 +152,7 @@ public class PlateModelFactory {
|
||||
throw new OcrException(e);
|
||||
}
|
||||
model.loadModel(config);
|
||||
model.setFromFactory(true);
|
||||
return model;
|
||||
}
|
||||
|
||||
@@ -165,4 +165,44 @@ public class PlateModelFactory {
|
||||
log.debug("缓存目录:{}", Config.getCachePath());
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭所有已加载的模型
|
||||
*/
|
||||
public void closeAll() {
|
||||
detModelMap.values().forEach(model -> {
|
||||
try {
|
||||
model.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
detModelMap.clear();
|
||||
|
||||
recModelMap.values().forEach(model -> {
|
||||
try {
|
||||
model.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
recModelMap.clear();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除缓存的检测模型
|
||||
* @param modelEnum
|
||||
*/
|
||||
public static void removeDetModelFromCache(PlateDetModelEnum modelEnum) {
|
||||
detModelMap.remove(modelEnum);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除缓存的识别模型
|
||||
* @param modelEnum
|
||||
*/
|
||||
public static void removeRecModelFromCache(PlateRecModelEnum modelEnum) {
|
||||
recModelMap.remove(modelEnum);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -103,6 +103,7 @@ public class TableRecModelFactory {
|
||||
throw new OcrException(e);
|
||||
}
|
||||
model.loadModel(config);
|
||||
model.setFromFactory(true);
|
||||
return model;
|
||||
}
|
||||
|
||||
@@ -116,4 +117,26 @@ public class TableRecModelFactory {
|
||||
log.debug("缓存目录:{}", Config.getCachePath());
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭所有已加载的模型
|
||||
*/
|
||||
public void closeAll() {
|
||||
tableStructureModelMap.values().forEach(model -> {
|
||||
try {
|
||||
model.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
tableStructureModelMap.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除缓存的模型
|
||||
* @param modelEnum
|
||||
*/
|
||||
public static void removeFromCache(TableStructureModelEnum modelEnum) {
|
||||
tableStructureModelMap.remove(modelEnum);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@ public interface OcrCommonDetModel extends AutoCloseable{
|
||||
* @param imagePath 图片路径
|
||||
* @return
|
||||
*/
|
||||
|
||||
@Deprecated
|
||||
default List<OcrBox> detect(String imagePath) {
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
@@ -37,6 +39,7 @@ public interface OcrCommonDetModel extends AutoCloseable{
|
||||
* @param image BufferedImage
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
default List<OcrBox> detect(BufferedImage image) {
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
@@ -47,6 +50,7 @@ public interface OcrCommonDetModel extends AutoCloseable{
|
||||
* @param imageData 图片字节数组
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
default List<OcrBox> detect(byte[] imageData) {
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
@@ -74,15 +78,26 @@ public interface OcrCommonDetModel extends AutoCloseable{
|
||||
* @param sourceImage
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
default BufferedImage detectAndDraw(BufferedImage sourceImage){
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测并绘制结果
|
||||
* @param sourceImage
|
||||
* @return
|
||||
*/
|
||||
default Image detectAndDraw(Image sourceImage){
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
|
||||
/**
|
||||
* 文本检测(批量)
|
||||
* @param imageList BufferedImage
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
default List<List<OcrBox>> batchDetect(List<BufferedImage> imageList) {
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
@@ -100,5 +115,9 @@ public interface OcrCommonDetModel extends AutoCloseable{
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
|
||||
default void setFromFactory(boolean fromFactory){
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -11,13 +11,15 @@ 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 cn.smartjavaai.common.cv.SmartImageFactory;
|
||||
import cn.smartjavaai.common.pool.PredictorFactory;
|
||||
import cn.smartjavaai.common.utils.BufferedImageUtils;
|
||||
import cn.smartjavaai.common.utils.FileUtils;
|
||||
import cn.smartjavaai.common.utils.ImageUtils;
|
||||
import cn.smartjavaai.common.utils.OpenCVUtils;
|
||||
import cn.smartjavaai.ocr.config.OcrDetModelConfig;
|
||||
import cn.smartjavaai.ocr.entity.OcrBox;
|
||||
import cn.smartjavaai.ocr.exception.OcrException;
|
||||
import cn.smartjavaai.ocr.factory.OcrModelFactory;
|
||||
import cn.smartjavaai.ocr.model.common.detect.criteria.OcrCommonDetCriterialFactory;
|
||||
import cn.smartjavaai.ocr.utils.OcrUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -81,12 +83,12 @@ public class OcrCommonDetModelImpl implements OcrCommonDetModel{
|
||||
}
|
||||
Image img = null;
|
||||
try {
|
||||
img = ImageFactory.getInstance().fromFile(Paths.get(imagePath));
|
||||
img = SmartImageFactory.getInstance().fromFile(Paths.get(imagePath));
|
||||
} catch (IOException e) {
|
||||
throw new OcrException("无效的图片", e);
|
||||
}
|
||||
List<OcrBox> ocrBoxList = detect(img);
|
||||
((Mat)img.getWrappedImage()).release();
|
||||
ImageUtils.releaseOpenCVMat(img);
|
||||
return ocrBoxList;
|
||||
}
|
||||
|
||||
@@ -103,16 +105,15 @@ public class OcrCommonDetModelImpl implements OcrCommonDetModel{
|
||||
throw new OcrException("图像文件不存在");
|
||||
}
|
||||
try {
|
||||
Image img = ImageFactory.getInstance().fromFile(Paths.get(imagePath));
|
||||
Image img = SmartImageFactory.getInstance().fromFile(Paths.get(imagePath));
|
||||
List<OcrBox> boxList = detect(img);
|
||||
if(Objects.isNull(boxList) || boxList.isEmpty()){
|
||||
throw new OcrException("未检测到文字");
|
||||
}
|
||||
OcrUtils.drawRect((Mat)img.getWrappedImage(), boxList);
|
||||
Path output = Paths.get(outputPath);
|
||||
log.debug("Saving to {}", output.toAbsolutePath().toString());
|
||||
img.save(Files.newOutputStream(output), "png");
|
||||
((Mat) img.getWrappedImage()).release();
|
||||
OcrUtils.drawOcrDetResult(img, boxList, 12);
|
||||
//4通道保存jpg会有问题
|
||||
ImageUtils.save(img, Paths.get(outputPath), "png");
|
||||
ImageUtils.releaseOpenCVMat(img);
|
||||
} catch (IOException e) {
|
||||
throw new OcrException(e);
|
||||
}
|
||||
@@ -121,12 +122,12 @@ public class OcrCommonDetModelImpl implements OcrCommonDetModel{
|
||||
|
||||
@Override
|
||||
public List<OcrBox> detect(BufferedImage image) {
|
||||
if(!ImageUtils.isImageValid(image)){
|
||||
if(!BufferedImageUtils.isImageValid(image)){
|
||||
throw new OcrException("图像无效");
|
||||
}
|
||||
Image img = ImageFactory.getInstance().fromImage(OpenCVUtils.image2Mat(image));
|
||||
Image img = SmartImageFactory.getInstance().fromBufferedImage(image);
|
||||
List<OcrBox> ocrBoxList = detect(img);
|
||||
((Mat)img.getWrappedImage()).release();
|
||||
ImageUtils.releaseOpenCVMat(img);
|
||||
return ocrBoxList;
|
||||
}
|
||||
|
||||
@@ -145,29 +146,30 @@ public class OcrCommonDetModelImpl implements OcrCommonDetModel{
|
||||
|
||||
@Override
|
||||
public BufferedImage detectAndDraw(BufferedImage sourceImage) {
|
||||
if(!ImageUtils.isImageValid(sourceImage)){
|
||||
if(!BufferedImageUtils.isImageValid(sourceImage)){
|
||||
throw new OcrException("图像无效");
|
||||
}
|
||||
Image img = ImageFactory.getInstance().fromImage(OpenCVUtils.image2Mat(sourceImage));
|
||||
Image img = SmartImageFactory.getInstance().fromBufferedImage(sourceImage);
|
||||
List<OcrBox> ocrBoxList = detect(img);
|
||||
if(Objects.isNull(ocrBoxList) || ocrBoxList.isEmpty()){
|
||||
throw new OcrException("未检测到文字");
|
||||
}
|
||||
OcrUtils.drawRect((Mat)img.getWrappedImage(), ocrBoxList);
|
||||
try {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
// 调用 save 方法将 Image 写入字节流
|
||||
img.save(outputStream, "png");
|
||||
// 将字节流转换为 BufferedImage
|
||||
byte[] imageBytes = outputStream.toByteArray();
|
||||
return ImageIO.read(new ByteArrayInputStream(imageBytes));
|
||||
} catch (IOException e) {
|
||||
throw new OcrException("导出图片失败", e);
|
||||
} finally {
|
||||
if (img != null){
|
||||
((Mat) img.getWrappedImage()).release();
|
||||
}
|
||||
OcrUtils.drawOcrDetResult(img, ocrBoxList, 12);
|
||||
BufferedImage bufferedImage = ImageUtils.toBufferedImage(img);
|
||||
ImageUtils.releaseOpenCVMat(img);
|
||||
return bufferedImage;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Image detectAndDraw(Image sourceImage) {
|
||||
List<OcrBox> ocrBoxList = detect(sourceImage);
|
||||
if(Objects.isNull(ocrBoxList) || ocrBoxList.isEmpty()){
|
||||
throw new OcrException("未检测到文字");
|
||||
}
|
||||
Image img = ImageUtils.copy(sourceImage);
|
||||
OcrUtils.drawOcrDetResult(img, ocrBoxList, 12);
|
||||
return img;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -175,13 +177,13 @@ public class OcrCommonDetModelImpl implements OcrCommonDetModel{
|
||||
List<Image> djlImageList = new ArrayList<>(imageList.size());
|
||||
try {
|
||||
for (BufferedImage bufferedImage : imageList) {
|
||||
djlImageList.add(ImageFactory.getInstance().fromImage(OpenCVUtils.image2Mat(bufferedImage)));
|
||||
djlImageList.add(SmartImageFactory.getInstance().fromBufferedImage(bufferedImage));
|
||||
}
|
||||
return batchDetectDJLImage(djlImageList);
|
||||
} catch (Exception e) {
|
||||
throw new OcrException(e);
|
||||
} finally {
|
||||
djlImageList.forEach(image -> ((Mat)image.getWrappedImage()).release());
|
||||
djlImageList.forEach(image -> ImageUtils.releaseOpenCVMat(image));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,6 +217,10 @@ public class OcrCommonDetModelImpl implements OcrCommonDetModel{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public GenericObjectPool<Predictor<Image, NDList>> getPool() {
|
||||
return detPredictorPool;
|
||||
@@ -222,6 +228,9 @@ public class OcrCommonDetModelImpl implements OcrCommonDetModel{
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
if (fromFactory) {
|
||||
OcrModelFactory.removeDetModelFromCache(config.getModelEnum());
|
||||
}
|
||||
try {
|
||||
if (detPredictorPool != null) {
|
||||
detPredictorPool.close();
|
||||
@@ -238,5 +247,15 @@ public class OcrCommonDetModelImpl implements OcrCommonDetModel{
|
||||
}
|
||||
}
|
||||
|
||||
private boolean fromFactory = false;
|
||||
|
||||
@Override
|
||||
public void setFromFactory(boolean fromFactory) {
|
||||
this.fromFactory = fromFactory;
|
||||
}
|
||||
public boolean isFromFactory() {
|
||||
return fromFactory;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@ import ai.djl.ndarray.types.Shape;
|
||||
import ai.djl.translate.Batchifier;
|
||||
import ai.djl.translate.Translator;
|
||||
import ai.djl.translate.TranslatorContext;
|
||||
import cn.smartjavaai.ocr.opencv.OcrNDArrayUtils;
|
||||
import cn.smartjavaai.common.utils.DJLCommonUtils;
|
||||
import cn.smartjavaai.common.utils.OpenCVUtils;
|
||||
import org.opencv.core.*;
|
||||
import org.opencv.imgproc.Imgproc;
|
||||
|
||||
@@ -98,10 +99,10 @@ public class PPOCRDetTranslator implements Translator<Image, NDList> {
|
||||
if (this.use_dilation) {
|
||||
Mat mask = new Mat();
|
||||
//convert from NDArray to Mat
|
||||
Mat srcMat = OcrNDArrayUtils.uint8NDArrayToMat(segmentation);
|
||||
Mat srcMat = DJLCommonUtils.uint8NDArrayToMat(segmentation);
|
||||
// size 越小,腐蚀的单位越小,图片越接近原图
|
||||
// Mat dilation_kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(2, 2));
|
||||
Mat dilation_kernel = OcrNDArrayUtils.uint8ArrayToMat(new byte[][]{{1, 1}, {1, 1}});
|
||||
Mat dilation_kernel = OpenCVUtils.uint8ArrayToMat(new byte[][]{{1, 1}, {1, 1}});
|
||||
/**
|
||||
* 膨胀说明: 图像的一部分区域与指定的核进行卷积, 求核的最`大`值并赋值给指定区域。 膨胀可以理解为图像中`高亮区域`的'领域扩大'。
|
||||
* 意思是高亮部分会侵蚀不是高亮的部分,使高亮部分越来越多。
|
||||
@@ -115,7 +116,7 @@ public class PPOCRDetTranslator implements Translator<Image, NDList> {
|
||||
srcMat.release();
|
||||
dilation_kernel.release();
|
||||
} else {
|
||||
Mat srcMat = OcrNDArrayUtils.uint8NDArrayToMat(segmentation);
|
||||
Mat srcMat = DJLCommonUtils.uint8NDArrayToMat(segmentation);
|
||||
//destination Matrix
|
||||
Scalar scalar = new Scalar(255);
|
||||
Core.multiply(srcMat, scalar, newMask);
|
||||
@@ -462,20 +463,20 @@ public class PPOCRDetTranslator implements Translator<Image, NDList> {
|
||||
box.set(new NDIndex(":, 1"), box.get(":, 1").sub(ymin));
|
||||
|
||||
//mask - convert from NDArray to Mat
|
||||
Mat maskMat = OcrNDArrayUtils.uint8NDArrayToMat(mask);
|
||||
Mat maskMat = DJLCommonUtils.uint8NDArrayToMat(mask);
|
||||
|
||||
//mask - convert from NDArray to Mat - 4 rows, 2 cols
|
||||
Mat boxMat = OcrNDArrayUtils.floatNDArrayToMat(box, CvType.CV_32S);
|
||||
Mat boxMat = DJLCommonUtils.floatNDArrayToMat(box, CvType.CV_32S);
|
||||
|
||||
// boxMat.reshape(1, new int[]{1, 4, 2});
|
||||
List<MatOfPoint> pts = new ArrayList<>();
|
||||
MatOfPoint matOfPoint = OcrNDArrayUtils.matToMatOfPoint(boxMat); // new MatOfPoint(boxMat);
|
||||
MatOfPoint matOfPoint = OpenCVUtils.matToMatOfPoint(boxMat); // new MatOfPoint(boxMat);
|
||||
pts.add(matOfPoint);
|
||||
Imgproc.fillPoly(maskMat, pts, new Scalar(1));
|
||||
|
||||
|
||||
NDArray subBitMap = bitmap.get(ymin + ":" + (ymax + 1) + "," + xmin + ":" + (xmax + 1));
|
||||
Mat bitMapMat = OcrNDArrayUtils.floatNDArrayToMat(subBitMap);
|
||||
Mat bitMapMat = DJLCommonUtils.floatNDArrayToMat(subBitMap);
|
||||
|
||||
Scalar score = Core.mean(bitMapMat, maskMat);
|
||||
float scoreValue = (float) score.val[0];
|
||||
|
||||
@@ -40,6 +40,7 @@ public interface OcrDirectionModel extends AutoCloseable{
|
||||
* @param imagePath 图片路径
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
default List<OcrItem> detect(String imagePath) {
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
@@ -50,6 +51,7 @@ public interface OcrDirectionModel extends AutoCloseable{
|
||||
* @param image BufferedImage
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
default List<OcrItem> detect(BufferedImage image) {
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
@@ -60,6 +62,7 @@ public interface OcrDirectionModel extends AutoCloseable{
|
||||
* @param imageData 图片字节数组
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
default List<OcrItem> detect(byte[] imageData) {
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
@@ -103,12 +106,26 @@ public interface OcrDirectionModel extends AutoCloseable{
|
||||
* @param sourceImage
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
default BufferedImage detectAndDraw(BufferedImage sourceImage){
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测并绘制结果
|
||||
* @param sourceImage
|
||||
* @return
|
||||
*/
|
||||
default Image detectAndDraw(Image sourceImage){
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
|
||||
default GenericObjectPool<Predictor<Image, DirectionInfo>> getPool() {
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
|
||||
default void setFromFactory(boolean fromFactory){
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,44 +1,35 @@
|
||||
package cn.smartjavaai.ocr.model.common.direction;
|
||||
|
||||
import ai.djl.Device;
|
||||
import ai.djl.MalformedModelException;
|
||||
import ai.djl.engine.Engine;
|
||||
import ai.djl.inference.Predictor;
|
||||
import ai.djl.modality.cv.Image;
|
||||
import ai.djl.modality.cv.ImageFactory;
|
||||
import ai.djl.ndarray.NDManager;
|
||||
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.training.util.ProgressBar;
|
||||
import cn.smartjavaai.common.enums.DeviceEnum;
|
||||
import cn.smartjavaai.common.cv.SmartImageFactory;
|
||||
import cn.smartjavaai.common.pool.PredictorFactory;
|
||||
import cn.smartjavaai.common.utils.BufferedImageUtils;
|
||||
import cn.smartjavaai.common.utils.FileUtils;
|
||||
import cn.smartjavaai.common.utils.ImageUtils;
|
||||
import cn.smartjavaai.common.utils.OpenCVUtils;
|
||||
import cn.smartjavaai.ocr.config.DirectionModelConfig;
|
||||
import cn.smartjavaai.ocr.entity.*;
|
||||
import cn.smartjavaai.ocr.enums.AngleEnum;
|
||||
import cn.smartjavaai.ocr.exception.OcrException;
|
||||
import cn.smartjavaai.ocr.factory.OcrModelFactory;
|
||||
import cn.smartjavaai.ocr.model.common.detect.OcrCommonDetModel;
|
||||
import cn.smartjavaai.ocr.model.common.direction.criteria.DirectionCriteriaFactory;
|
||||
import cn.smartjavaai.ocr.model.common.direction.translator.PpWordRotateTranslator;
|
||||
import cn.smartjavaai.ocr.utils.OcrUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.pool2.ObjectPool;
|
||||
import org.apache.commons.pool2.impl.GenericObjectPool;
|
||||
import org.opencv.core.Mat;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -63,6 +54,8 @@ public class PPOCRMobileV2ClsModel implements OcrDirectionModel {
|
||||
|
||||
private OcrCommonDetModel textDetModel;
|
||||
|
||||
public static final int FONT_SIZE = 45;
|
||||
|
||||
|
||||
@Override
|
||||
public void loadModel(DirectionModelConfig config){
|
||||
@@ -100,14 +93,12 @@ public class PPOCRMobileV2ClsModel implements OcrDirectionModel {
|
||||
}
|
||||
Image img = null;
|
||||
try {
|
||||
img = ImageFactory.getInstance().fromFile(Paths.get(imagePath));
|
||||
img = SmartImageFactory.getInstance().fromFile(Paths.get(imagePath));
|
||||
return detect(img);
|
||||
} catch (IOException e) {
|
||||
throw new OcrException("无效的图片", e);
|
||||
}finally {
|
||||
if(img != null){
|
||||
((Mat)img.getWrappedImage()).release();
|
||||
}
|
||||
ImageUtils.releaseOpenCVMat(img);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +113,7 @@ public class PPOCRMobileV2ClsModel implements OcrDirectionModel {
|
||||
if(Objects.isNull(boxeList) || boxeList.isEmpty()){
|
||||
throw new OcrException("未检测到文本");
|
||||
}
|
||||
Mat srcMat = (Mat) image.getWrappedImage();
|
||||
Mat srcMat = ImageUtils.toMat(image);
|
||||
return detect(boxeList, srcMat);
|
||||
}
|
||||
|
||||
@@ -171,7 +162,7 @@ public class PPOCRMobileV2ClsModel implements OcrDirectionModel {
|
||||
// }
|
||||
|
||||
@Override
|
||||
public List<OcrItem> detect(List<OcrBox> boxList,Mat srcMat){
|
||||
public List<OcrItem> detect(List<OcrBox> boxList, Mat srcMat){
|
||||
if(Objects.isNull(boxList) || boxList.isEmpty()){
|
||||
throw new OcrException("boxList为空");
|
||||
}
|
||||
@@ -189,32 +180,30 @@ public class PPOCRMobileV2ClsModel implements OcrDirectionModel {
|
||||
}
|
||||
Image img = null;
|
||||
try {
|
||||
img = ImageFactory.getInstance().fromFile(Paths.get(imagePath));
|
||||
img = SmartImageFactory.getInstance().fromFile(Paths.get(imagePath));
|
||||
List<OcrItem> itemList = detect(img);
|
||||
if(Objects.isNull(itemList) || itemList.isEmpty()){
|
||||
throw new OcrException("未检测到文字");
|
||||
}
|
||||
OcrUtils.drawRectWithText((Mat) img.getWrappedImage(), itemList);
|
||||
Path output = Paths.get(outputPath);
|
||||
log.debug("Saving to {}", output.toAbsolutePath().toString());
|
||||
img.save(Files.newOutputStream(output), "png");
|
||||
BufferedImage bufferedImage = ImageUtils.toBufferedImage(img);
|
||||
OcrUtils.drawOcrResult(bufferedImage, itemList, FONT_SIZE);
|
||||
log.debug("Saving to {}", outputPath);
|
||||
BufferedImageUtils.saveImage(bufferedImage, outputPath);
|
||||
} catch (IOException e) {
|
||||
throw new OcrException(e);
|
||||
} finally {
|
||||
if (img != null){
|
||||
((Mat)img.getWrappedImage()).release();
|
||||
}
|
||||
ImageUtils.releaseOpenCVMat(img);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<OcrItem> detect(BufferedImage image) {
|
||||
if(!ImageUtils.isImageValid(image)){
|
||||
if(!BufferedImageUtils.isImageValid(image)){
|
||||
throw new OcrException("图像无效");
|
||||
}
|
||||
Image img = ImageFactory.getInstance().fromImage(OpenCVUtils.image2Mat(image));
|
||||
Image img = SmartImageFactory.getInstance().fromBufferedImage(image);
|
||||
List<OcrItem> ocrItemList = detect(img);
|
||||
((Mat)img.getWrappedImage()).release();
|
||||
ImageUtils.releaseOpenCVMat(img);
|
||||
return ocrItemList;
|
||||
}
|
||||
|
||||
@@ -223,39 +212,41 @@ public class PPOCRMobileV2ClsModel implements OcrDirectionModel {
|
||||
if(Objects.isNull(imageData)){
|
||||
throw new OcrException("图像无效");
|
||||
}
|
||||
Image img = null;
|
||||
try {
|
||||
BufferedImage image = ImageIO.read(new ByteArrayInputStream(imageData));
|
||||
return detect(image);
|
||||
img = SmartImageFactory.getInstance().fromBytes(imageData);
|
||||
} catch (IOException e) {
|
||||
throw new OcrException("错误的图像", e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
List<OcrItem> ocrItemList = detect(img);
|
||||
ImageUtils.releaseOpenCVMat(img);
|
||||
return ocrItemList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedImage detectAndDraw(BufferedImage sourceImage) {
|
||||
if(!ImageUtils.isImageValid(sourceImage)){
|
||||
if(!BufferedImageUtils.isImageValid(sourceImage)){
|
||||
throw new OcrException("图像无效");
|
||||
}
|
||||
Image img = ImageFactory.getInstance().fromImage(OpenCVUtils.image2Mat(sourceImage));
|
||||
Image img = SmartImageFactory.getInstance().fromBufferedImage(sourceImage);
|
||||
List<OcrItem> ocrItemList = detect(img);
|
||||
if(Objects.isNull(ocrItemList) || ocrItemList.isEmpty()){
|
||||
throw new OcrException("未检测到文字");
|
||||
}
|
||||
OcrUtils.drawRectWithText((Mat) img.getWrappedImage(), ocrItemList);
|
||||
try {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
// 调用 save 方法将 Image 写入字节流
|
||||
img.save(outputStream, "png");
|
||||
// 将字节流转换为 BufferedImage
|
||||
byte[] imageBytes = outputStream.toByteArray();
|
||||
return ImageIO.read(new ByteArrayInputStream(imageBytes));
|
||||
} catch (IOException e) {
|
||||
throw new OcrException("导出图片失败", e);
|
||||
} finally {
|
||||
if (img != null){
|
||||
((Mat) img.getWrappedImage()).release();
|
||||
}
|
||||
BufferedImage drawImage = BufferedImageUtils.copyBufferedImage(sourceImage);
|
||||
OcrUtils.drawOcrResult(drawImage, ocrItemList, FONT_SIZE);
|
||||
return drawImage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Image detectAndDraw(Image sourceImage) {
|
||||
List<OcrItem> ocrItemList = detect(sourceImage);
|
||||
if(Objects.isNull(ocrItemList) || ocrItemList.isEmpty()){
|
||||
throw new OcrException("未检测到文字");
|
||||
}
|
||||
Image drawImage = ImageUtils.copy(sourceImage);
|
||||
OcrUtils.drawOcrResult(drawImage, ocrItemList, FONT_SIZE);
|
||||
return drawImage;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -291,7 +282,7 @@ public class PPOCRMobileV2ClsModel implements OcrDirectionModel {
|
||||
//高宽比 > 1.5 纵向
|
||||
if (subImg.getHeight() * 1.0 / subImg.getWidth() > 1.5) {
|
||||
//旋转图片90度
|
||||
subImg = OcrUtils.rotateImg(manager, subImg);
|
||||
subImg = ImageUtils.rotateImg(manager, subImg);
|
||||
isRotatedList.add(true);
|
||||
imageList.add(subImg);
|
||||
}else{
|
||||
@@ -303,6 +294,8 @@ public class PPOCRMobileV2ClsModel implements OcrDirectionModel {
|
||||
}
|
||||
List<List<OcrItem>> result = new ArrayList<>();
|
||||
List<DirectionInfo> directionInfos = batchDetect(imageList);
|
||||
//释放
|
||||
imageList.forEach(image -> ImageUtils.releaseOpenCVMat(image));
|
||||
if(CollectionUtils.isEmpty(directionInfos)){
|
||||
throw new OcrException("方向检测失败");
|
||||
}
|
||||
@@ -378,6 +371,9 @@ public class PPOCRMobileV2ClsModel implements OcrDirectionModel {
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
if (fromFactory) {
|
||||
OcrModelFactory.removeDirectionModelFromCache(config.getModelEnum());
|
||||
}
|
||||
try {
|
||||
if (predictorPool != null) {
|
||||
predictorPool.close();
|
||||
@@ -393,4 +389,14 @@ public class PPOCRMobileV2ClsModel implements OcrDirectionModel {
|
||||
log.warn("关闭 model 失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean fromFactory = false;
|
||||
|
||||
@Override
|
||||
public void setFromFactory(boolean fromFactory) {
|
||||
this.fromFactory = fromFactory;
|
||||
}
|
||||
public boolean isFromFactory() {
|
||||
return fromFactory;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ public interface OcrCommonRecModel extends AutoCloseable{
|
||||
* @param imagePath 图片路径
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
default OcrInfo recognize(String imagePath, OcrRecOptions options) {
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
@@ -66,6 +67,7 @@ public interface OcrCommonRecModel extends AutoCloseable{
|
||||
* @param image BufferedImage
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
default OcrInfo recognize(BufferedImage image, OcrRecOptions options) {
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
@@ -76,6 +78,7 @@ public interface OcrCommonRecModel extends AutoCloseable{
|
||||
* @param imageData 图片字节数组
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
default OcrInfo recognize(byte[] imageData, OcrRecOptions options) {
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
@@ -90,11 +93,22 @@ public interface OcrCommonRecModel extends AutoCloseable{
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
|
||||
/**
|
||||
* 识别并绘制结果
|
||||
* @param image
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
default OcrInfo recognizeAndDraw(Image image, int fontSize, OcrRecOptions options){
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
|
||||
/**
|
||||
* 识别并绘制结果
|
||||
* @param sourceImage
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
default BufferedImage recognizeAndDraw(BufferedImage sourceImage, int fontSize, OcrRecOptions options){
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
@@ -103,6 +117,7 @@ public interface OcrCommonRecModel extends AutoCloseable{
|
||||
* @param imageData 图片字节数组
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
default String recognizeAndDrawToBase64(byte[] imageData, int fontSize, OcrRecOptions options){
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
@@ -112,10 +127,12 @@ public interface OcrCommonRecModel extends AutoCloseable{
|
||||
* @param imageData 图片字节数组
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
default OcrInfo recognizeAndDraw(byte[] imageData, int fontSize, OcrRecOptions options){
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
default List<OcrInfo> batchRecognize(List<BufferedImage> imageList, OcrRecOptions options) {
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
@@ -128,4 +145,8 @@ public interface OcrCommonRecModel extends AutoCloseable{
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
|
||||
default void setFromFactory(boolean fromFactory){
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import ai.djl.MalformedModelException;
|
||||
import ai.djl.engine.Engine;
|
||||
import ai.djl.inference.Predictor;
|
||||
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.repository.zoo.Criteria;
|
||||
@@ -12,14 +11,16 @@ import ai.djl.repository.zoo.ModelNotFoundException;
|
||||
import ai.djl.repository.zoo.ModelZoo;
|
||||
import ai.djl.repository.zoo.ZooModel;
|
||||
import cn.hutool.core.img.ImgUtil;
|
||||
import cn.smartjavaai.common.cv.SmartImageFactory;
|
||||
import cn.smartjavaai.common.pool.PredictorFactory;
|
||||
import cn.smartjavaai.common.utils.BufferedImageUtils;
|
||||
import cn.smartjavaai.common.utils.FileUtils;
|
||||
import cn.smartjavaai.common.utils.ImageUtils;
|
||||
import cn.smartjavaai.common.utils.OpenCVUtils;
|
||||
import cn.smartjavaai.ocr.config.OcrRecModelConfig;
|
||||
import cn.smartjavaai.ocr.config.OcrRecOptions;
|
||||
import cn.smartjavaai.ocr.entity.*;
|
||||
import cn.smartjavaai.ocr.exception.OcrException;
|
||||
import cn.smartjavaai.ocr.factory.OcrModelFactory;
|
||||
import cn.smartjavaai.ocr.model.common.detect.OcrCommonDetModel;
|
||||
import cn.smartjavaai.ocr.model.common.direction.OcrDirectionModel;
|
||||
import cn.smartjavaai.ocr.model.common.recognize.criteria.OcrCommonRecCriterialFactory;
|
||||
@@ -33,7 +34,6 @@ import org.opencv.core.Mat;
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
@@ -95,14 +95,12 @@ public class OcrCommonRecModelImpl implements OcrCommonRecModel {
|
||||
}
|
||||
Image img = null;
|
||||
try {
|
||||
img = ImageFactory.getInstance().fromFile(Paths.get(imagePath));
|
||||
img = SmartImageFactory.getInstance().fromFile(Paths.get(imagePath));
|
||||
return recognize(img, options);
|
||||
} catch (IOException e) {
|
||||
throw new OcrException("无效的图片", e);
|
||||
} finally {
|
||||
if (img != null) {
|
||||
((Mat) img.getWrappedImage()).release();
|
||||
}
|
||||
ImageUtils.releaseOpenCVMat(img);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,7 +136,7 @@ public class OcrCommonRecModelImpl implements OcrCommonRecModel {
|
||||
//高宽比 > 1.5
|
||||
if (subImg.getHeight() * 1.0 / subImg.getWidth() > 1.5) {
|
||||
//旋转图片90度
|
||||
subImg = OcrUtils.rotateImg(manager, subImg);
|
||||
subImg = ImageUtils.rotateImg(manager, subImg);
|
||||
//ImageUtils.saveImage(subImg, i + "rotate.png", "build/output");
|
||||
}
|
||||
imageList.add(subImg);
|
||||
@@ -236,30 +234,31 @@ public class OcrCommonRecModelImpl implements OcrCommonRecModel {
|
||||
if (!FileUtils.isFileExists(imagePath)) {
|
||||
throw new OcrException("图像文件不存在");
|
||||
}
|
||||
Image img = null;
|
||||
try {
|
||||
Image img = ImageFactory.getInstance().fromFile(Paths.get(imagePath));
|
||||
img = SmartImageFactory.getInstance().fromFile(Paths.get(imagePath));
|
||||
OcrInfo ocrInfo = recognize(img, options);
|
||||
if (Objects.isNull(ocrInfo) || Objects.isNull(ocrInfo.getLineList()) || ocrInfo.getLineList().isEmpty()) {
|
||||
throw new OcrException("未检测到文字");
|
||||
}
|
||||
Mat wrappedImage = (Mat) img.getWrappedImage();
|
||||
BufferedImage bufferedImage = OpenCVUtils.mat2Image(wrappedImage);
|
||||
OcrUtils.drawRectWithText(bufferedImage, ocrInfo, fontSize);
|
||||
ImageUtils.saveImage(bufferedImage, outputPath);
|
||||
wrappedImage.release();
|
||||
BufferedImage bufferedImage = ImageUtils.toBufferedImage(img);
|
||||
OcrUtils.drawOcrResult(bufferedImage, ocrInfo, fontSize);
|
||||
BufferedImageUtils.saveImage(bufferedImage, outputPath);
|
||||
} catch (IOException e) {
|
||||
throw new OcrException(e);
|
||||
}finally {
|
||||
ImageUtils.releaseOpenCVMat(img);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public OcrInfo recognize(BufferedImage image, OcrRecOptions options) {
|
||||
if (!ImageUtils.isImageValid(image)) {
|
||||
if (!BufferedImageUtils.isImageValid(image)) {
|
||||
throw new OcrException("图像无效");
|
||||
}
|
||||
Image img = ImageFactory.getInstance().fromImage(OpenCVUtils.image2Mat(image));
|
||||
Image img = SmartImageFactory.getInstance().fromBufferedImage(image);
|
||||
OcrInfo ocrInfo = recognize(img, options);
|
||||
((Mat) img.getWrappedImage()).release();
|
||||
ImageUtils.releaseOpenCVMat(img);
|
||||
return ocrInfo;
|
||||
}
|
||||
|
||||
@@ -278,15 +277,15 @@ public class OcrCommonRecModelImpl implements OcrCommonRecModel {
|
||||
|
||||
@Override
|
||||
public BufferedImage recognizeAndDraw(BufferedImage sourceImage, int fontSize, OcrRecOptions options) {
|
||||
if (!ImageUtils.isImageValid(sourceImage)) {
|
||||
if (!BufferedImageUtils.isImageValid(sourceImage)) {
|
||||
throw new OcrException("图像无效");
|
||||
}
|
||||
Image img = ImageFactory.getInstance().fromImage(OpenCVUtils.image2Mat(sourceImage));
|
||||
Image img = SmartImageFactory.getInstance().fromBufferedImage(sourceImage);
|
||||
OcrInfo ocrInfo = recognize(img, options);
|
||||
if (Objects.isNull(ocrInfo) || Objects.isNull(ocrInfo.getLineList()) || ocrInfo.getLineList().isEmpty()) {
|
||||
throw new OcrException("未检测到文字");
|
||||
}
|
||||
OcrUtils.drawRectWithText(sourceImage, ocrInfo, fontSize);
|
||||
OcrUtils.drawOcrResult(sourceImage, ocrInfo, fontSize);
|
||||
return sourceImage;
|
||||
}
|
||||
|
||||
@@ -301,7 +300,7 @@ public class OcrCommonRecModelImpl implements OcrCommonRecModel {
|
||||
}
|
||||
try {
|
||||
BufferedImage sourceImage = ImageIO.read(new ByteArrayInputStream(imageData));
|
||||
OcrUtils.drawRectWithText(sourceImage, ocrInfo, fontSize);
|
||||
OcrUtils.drawOcrResult(sourceImage, ocrInfo, fontSize);
|
||||
return ImgUtil.toBase64(sourceImage, "png");
|
||||
} catch (IOException e) {
|
||||
throw new OcrException("导出图片失败", e);
|
||||
@@ -313,18 +312,21 @@ public class OcrCommonRecModelImpl implements OcrCommonRecModel {
|
||||
if (Objects.isNull(imageData)) {
|
||||
throw new OcrException("图像无效");
|
||||
}
|
||||
OcrInfo ocrInfo = recognize(imageData, options);
|
||||
Image img = null;
|
||||
try {
|
||||
img = SmartImageFactory.getInstance().fromBytes(imageData);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
OcrInfo ocrInfo = recognize(img, options);
|
||||
if (Objects.isNull(ocrInfo) || Objects.isNull(ocrInfo.getLineList()) || ocrInfo.getLineList().isEmpty()) {
|
||||
throw new OcrException("未检测到文字");
|
||||
}
|
||||
try {
|
||||
BufferedImage sourceImage = ImageIO.read(new ByteArrayInputStream(imageData));
|
||||
OcrUtils.drawRectWithText(sourceImage, ocrInfo, fontSize);
|
||||
ocrInfo.setBase64Img(ImgUtil.toBase64(sourceImage, "png"));
|
||||
return ocrInfo;
|
||||
} catch (IOException e) {
|
||||
throw new OcrException("导出图片失败", e);
|
||||
}
|
||||
//opencv中文乱码,使用BufferedImage
|
||||
BufferedImage sourceImage = ImageUtils.toBufferedImage(img);
|
||||
OcrUtils.drawOcrResult(sourceImage, ocrInfo, fontSize);
|
||||
ocrInfo.setDrawnImage(SmartImageFactory.getInstance().fromBufferedImage(sourceImage));
|
||||
return ocrInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -332,13 +334,13 @@ public class OcrCommonRecModelImpl implements OcrCommonRecModel {
|
||||
List<Image> djlImageList = new ArrayList<>(imageList.size());
|
||||
try {
|
||||
for (BufferedImage bufferedImage : imageList) {
|
||||
djlImageList.add(ImageFactory.getInstance().fromImage(OpenCVUtils.image2Mat(bufferedImage)));
|
||||
djlImageList.add(SmartImageFactory.getInstance().fromBufferedImage(bufferedImage));
|
||||
}
|
||||
return batchRecognizeDJLImage(djlImageList, options);
|
||||
} catch (Exception e) {
|
||||
throw new OcrException(e);
|
||||
} finally {
|
||||
djlImageList.forEach(image -> ((Mat) image.getWrappedImage()).release());
|
||||
djlImageList.forEach(image -> ImageUtils.releaseOpenCVMat(image));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -370,7 +372,7 @@ public class OcrCommonRecModelImpl implements OcrCommonRecModel {
|
||||
throw new OcrException("请配置方向模型");
|
||||
}
|
||||
List<Mat> matList = imageList.stream()
|
||||
.map(image -> (Mat) image.getWrappedImage())
|
||||
.map(image -> ImageUtils.toMat(image))
|
||||
.collect(Collectors.toList());
|
||||
List<List<OcrItem>> ocrItemList = directionModel.batchDetect(boxeList, matList);
|
||||
if (CollectionUtils.isEmpty(ocrItemList) || ocrItemList.size() != imageList.size()) {
|
||||
@@ -378,7 +380,7 @@ public class OcrCommonRecModelImpl implements OcrCommonRecModel {
|
||||
}
|
||||
allImageAlignList = new ArrayList<Image>();
|
||||
for (int i = 0; i < ocrItemList.size(); i++) {
|
||||
Mat srcMat = (Mat) imageList.get(i).getWrappedImage();
|
||||
Mat srcMat = ImageUtils.toMat(imageList.get(i));
|
||||
List<Image> imageAlignList = batchAlignWithDirection(ocrItemList.get(i), srcMat, manager);
|
||||
// for(int j = 0; j < imageAlignList.size(); j++){
|
||||
// ImageUtils.saveImage(imageAlignList.get(j),"dir-"+i+"-"+j+".png","/Users/xxx/Downloads/testing33");
|
||||
@@ -387,10 +389,10 @@ public class OcrCommonRecModelImpl implements OcrCommonRecModel {
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < boxeList.size(); i++) {
|
||||
Mat srcMat = (Mat) imageList.get(i).getWrappedImage();
|
||||
Mat srcMat = ImageUtils.toMat(imageList.get(i));
|
||||
List<Image> imageAlignList = batchAlign(boxeList.get(i), srcMat, manager);
|
||||
// for(int j = 0; j < imageAlignList.size(); j++){
|
||||
// ImageUtils.saveImage(imageAlignList.get(j),i+"-"+j+".png","/Users/xxx/Downloads/testing33");
|
||||
// ImageUtils.saveImage(imageAlignList.get(j),i+"-"+j+".png","/Users/wenjie/Downloads/testing33");
|
||||
// }
|
||||
allImageAlignList.addAll(imageAlignList);
|
||||
}
|
||||
@@ -435,7 +437,7 @@ public class OcrCommonRecModelImpl implements OcrCommonRecModel {
|
||||
try {
|
||||
predictor = recPredictorPool.borrowObject();
|
||||
List<String> textList = predictor.batchPredict(imageAlignList);
|
||||
imageAlignList.forEach(subImg -> ((Mat) subImg.getWrappedImage()).release());
|
||||
imageAlignList.forEach(subImg -> ImageUtils.releaseOpenCVMat(subImg));
|
||||
return textList;
|
||||
} catch (Exception e) {
|
||||
throw new OcrException("OCR检测错误", e);
|
||||
@@ -455,6 +457,19 @@ public class OcrCommonRecModelImpl implements OcrCommonRecModel {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public OcrInfo recognizeAndDraw(Image image, int fontSize, OcrRecOptions options) {
|
||||
OcrInfo ocrInfo = recognize(image, options);
|
||||
if (Objects.isNull(ocrInfo) || Objects.isNull(ocrInfo.getLineList()) || ocrInfo.getLineList().isEmpty()) {
|
||||
throw new OcrException("未检测到文字");
|
||||
}
|
||||
BufferedImage sourceImage = ImageUtils.toBufferedImage(image);
|
||||
OcrUtils.drawOcrResult(sourceImage, ocrInfo, fontSize);
|
||||
ocrInfo.setDrawnImage(SmartImageFactory.getInstance().fromBufferedImage(sourceImage));
|
||||
return ocrInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTextDetModel(OcrCommonDetModel detModel) {
|
||||
this.textDetModel = detModel;
|
||||
@@ -482,6 +497,9 @@ public class OcrCommonRecModelImpl implements OcrCommonRecModel {
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
if (fromFactory) {
|
||||
OcrModelFactory.removeRecModelFromCache(config.getRecModelEnum());
|
||||
}
|
||||
try {
|
||||
if (recPredictorPool != null) {
|
||||
recPredictorPool.close();
|
||||
@@ -497,4 +515,14 @@ public class OcrCommonRecModelImpl implements OcrCommonRecModel {
|
||||
log.warn("关闭 model 失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean fromFactory = false;
|
||||
|
||||
@Override
|
||||
public void setFromFactory(boolean fromFactory) {
|
||||
this.fromFactory = fromFactory;
|
||||
}
|
||||
public boolean isFromFactory() {
|
||||
return fromFactory;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import ai.djl.MalformedModelException;
|
||||
import ai.djl.engine.Engine;
|
||||
import ai.djl.inference.Predictor;
|
||||
import ai.djl.modality.cv.Image;
|
||||
import ai.djl.modality.cv.ImageFactory;
|
||||
import ai.djl.modality.cv.output.BoundingBox;
|
||||
import ai.djl.modality.cv.output.DetectedObjects;
|
||||
import ai.djl.repository.zoo.Criteria;
|
||||
@@ -13,19 +12,18 @@ import ai.djl.repository.zoo.ModelZoo;
|
||||
import ai.djl.repository.zoo.ZooModel;
|
||||
import cn.hutool.core.lang.UUID;
|
||||
import cn.hutool.core.lang.generator.UUIDGenerator;
|
||||
import cn.smartjavaai.common.cv.SmartImageFactory;
|
||||
import cn.smartjavaai.common.entity.DetectionRectangle;
|
||||
import cn.smartjavaai.common.entity.R;
|
||||
import cn.smartjavaai.common.pool.PredictorFactory;
|
||||
import cn.smartjavaai.common.utils.Base64ImageUtils;
|
||||
import cn.smartjavaai.common.utils.FileUtils;
|
||||
import cn.smartjavaai.common.utils.ImageUtils;
|
||||
import cn.smartjavaai.common.utils.OpenCVUtils;
|
||||
import cn.smartjavaai.common.utils.*;
|
||||
import cn.smartjavaai.ocr.config.PlateDetModelConfig;
|
||||
import cn.smartjavaai.ocr.config.PlateRecModelConfig;
|
||||
import cn.smartjavaai.ocr.entity.PlateInfo;
|
||||
import cn.smartjavaai.ocr.entity.PlateResult;
|
||||
import cn.smartjavaai.ocr.enums.PlateType;
|
||||
import cn.smartjavaai.ocr.exception.OcrException;
|
||||
import cn.smartjavaai.ocr.factory.PlateModelFactory;
|
||||
import cn.smartjavaai.ocr.model.plate.criteria.PlateDetCriterialFactory;
|
||||
import cn.smartjavaai.ocr.model.plate.criteria.PlateRecCriterialFactory;
|
||||
import cn.smartjavaai.ocr.utils.OcrUtils;
|
||||
@@ -97,13 +95,13 @@ public class CRNNPlateRecModel implements PlateRecModel{
|
||||
}
|
||||
Image img = null;
|
||||
try {
|
||||
img = ImageFactory.getInstance().fromFile(Paths.get(imagePath));
|
||||
img = SmartImageFactory.getInstance().fromFile(Paths.get(imagePath));
|
||||
R<List<PlateInfo>> plateResult = recognize(img);
|
||||
return plateResult;
|
||||
} catch (IOException e) {
|
||||
throw new OcrException("无效的图片", e);
|
||||
} finally {
|
||||
((Mat)img.getWrappedImage()).release();
|
||||
ImageUtils.releaseOpenCVMat(img);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,12 +116,12 @@ public class CRNNPlateRecModel implements PlateRecModel{
|
||||
|
||||
@Override
|
||||
public R<List<PlateInfo>> recognize(BufferedImage image) {
|
||||
if(!ImageUtils.isImageValid(image)){
|
||||
if(!BufferedImageUtils.isImageValid(image)){
|
||||
return R.fail(R.Status.INVALID_IMAGE);
|
||||
}
|
||||
Image img = ImageFactory.getInstance().fromImage(OpenCVUtils.image2Mat(image));
|
||||
Image img = SmartImageFactory.getInstance().fromBufferedImage(image);
|
||||
R<List<PlateInfo>> plateResult = recognize(img);
|
||||
((Mat)img.getWrappedImage()).release();
|
||||
ImageUtils.releaseOpenCVMat(img);
|
||||
return plateResult;
|
||||
}
|
||||
|
||||
@@ -140,7 +138,7 @@ public class CRNNPlateRecModel implements PlateRecModel{
|
||||
if(Objects.isNull(config.getPlateDetModel())){
|
||||
return R.fail(R.Status.PARAM_ERROR.getCode(), "未指定车牌检测模型");
|
||||
}
|
||||
DetectedObjects detectedObjects = config.getPlateDetModel().detect(image);
|
||||
DetectedObjects detectedObjects = config.getPlateDetModel().detectCore(image);
|
||||
if(Objects.isNull(detectedObjects) || detectedObjects.getNumberOfObjects() == 0){
|
||||
return R.fail(R.Status.NO_OBJECT_DETECTED);
|
||||
}
|
||||
@@ -151,23 +149,26 @@ public class CRNNPlateRecModel implements PlateRecModel{
|
||||
for (PlateInfo plateInfo : plateInfoList){
|
||||
DetectionRectangle detectionRectangle = plateInfo.getDetectionRectangle();
|
||||
// Image subImage = image.getSubImage(detectionRectangle.getX(), detectionRectangle.getY(), detectionRectangle.getWidth(), detectionRectangle.getHeight());
|
||||
Mat imageMat = ImageUtils.toMat(image);
|
||||
//透视变换
|
||||
Image subImage = OcrUtils.transformAndCrop((Mat)image.getWrappedImage(), plateInfo.getBox());
|
||||
Mat subMat = OcrUtils.transformAndCropToMat(imageMat, plateInfo.getBox());
|
||||
//双层车牌
|
||||
if(plateInfo.getPlateType() == PlateType.DOUBLE){
|
||||
Mat mergeImage = getSplitMerge((Mat)subImage.getWrappedImage());
|
||||
subImage = ImageFactory.getInstance().fromImage(mergeImage);
|
||||
subMat = getSplitMerge(subMat);
|
||||
}
|
||||
Image subImage = SmartImageFactory.getInstance().fromMat(subMat);
|
||||
PlateResult plateResult = predictor.predict(subImage);
|
||||
if(Objects.nonNull(plateResult)){
|
||||
plateInfo.setPlateNumber(plateResult.getPlateNo());
|
||||
plateInfo.setPlateColor(plateResult.getPlateColor());
|
||||
}
|
||||
ImageUtils.releaseOpenCVMat(subImage);
|
||||
}
|
||||
return R.ok(plateInfoList);
|
||||
} catch (Exception e) {
|
||||
throw new OcrException("车牌识别错误", e);
|
||||
}finally {
|
||||
|
||||
if (predictor != null) {
|
||||
try {
|
||||
recPredictorPool.returnObject(predictor); //归还
|
||||
@@ -247,14 +248,12 @@ public class CRNNPlateRecModel implements PlateRecModel{
|
||||
}
|
||||
Image img = null;
|
||||
try {
|
||||
img = ImageFactory.getInstance().fromInputStream(inputStream);
|
||||
img = SmartImageFactory.getInstance().fromInputStream(inputStream);
|
||||
return recognize(img);
|
||||
} catch (IOException e) {
|
||||
throw new OcrException("无效图片输入流", e);
|
||||
} finally {
|
||||
if (img != null){
|
||||
((Mat)img.getWrappedImage()).release();
|
||||
}
|
||||
ImageUtils.releaseOpenCVMat(img);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,7 +264,7 @@ public class CRNNPlateRecModel implements PlateRecModel{
|
||||
}
|
||||
Image img = null;
|
||||
try {
|
||||
img = ImageFactory.getInstance().fromFile(Paths.get(imagePath));
|
||||
img = SmartImageFactory.getInstance().fromFile(Paths.get(imagePath));
|
||||
R<List<PlateInfo>> plateResult = recognize(img);
|
||||
if(!plateResult.isSuccess()){
|
||||
return R.fail(plateResult.getCode(), plateResult.getMessage());
|
||||
@@ -273,22 +272,20 @@ public class CRNNPlateRecModel implements PlateRecModel{
|
||||
if(CollectionUtils.isEmpty(plateResult.getData())){
|
||||
return R.fail(R.Status.NO_OBJECT_DETECTED);
|
||||
}
|
||||
BufferedImage bufferedImage = OpenCVUtils.mat2Image((Mat)img.getWrappedImage());
|
||||
BufferedImage bufferedImage = ImageUtils.toBufferedImage(img);
|
||||
OcrUtils.drawPlateInfo(bufferedImage, plateResult.getData());
|
||||
ImageIO.write(bufferedImage, "png", new File(outputPath));
|
||||
return R.ok();
|
||||
} catch (IOException e) {
|
||||
throw new OcrException(e);
|
||||
} finally {
|
||||
if (img != null){
|
||||
((Mat)img.getWrappedImage()).release();
|
||||
}
|
||||
ImageUtils.releaseOpenCVMat(img);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<BufferedImage> recognizeAndDraw(BufferedImage sourceImage) {
|
||||
if(!ImageUtils.isImageValid(sourceImage)){
|
||||
if(!BufferedImageUtils.isImageValid(sourceImage)){
|
||||
return R.fail(R.Status.INVALID_IMAGE);
|
||||
}
|
||||
try {
|
||||
@@ -306,6 +303,25 @@ public class CRNNPlateRecModel implements PlateRecModel{
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<Image> recognizeAndDraw(Image image) {
|
||||
try {
|
||||
R<List<PlateInfo>> plateResult = recognize(image);
|
||||
if(!plateResult.isSuccess()){
|
||||
return R.fail(plateResult.getCode(), plateResult.getMessage());
|
||||
}
|
||||
if(CollectionUtils.isEmpty(plateResult.getData())){
|
||||
return R.fail(R.Status.NO_OBJECT_DETECTED);
|
||||
}
|
||||
//opencv中文乱码,使用BufferedImage
|
||||
BufferedImage sourceImage = ImageUtils.toBufferedImage(image);
|
||||
OcrUtils.drawPlateInfo(sourceImage, plateResult.getData());
|
||||
Image drawImage = SmartImageFactory.getInstance().fromBufferedImage(sourceImage);
|
||||
return R.ok(drawImage);
|
||||
} catch (Exception e) {
|
||||
throw new OcrException("导出图片失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public GenericObjectPool<Predictor<Image, PlateResult>> getPool() {
|
||||
@@ -314,6 +330,9 @@ public class CRNNPlateRecModel implements PlateRecModel{
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
if (fromFactory) {
|
||||
PlateModelFactory.removeRecModelFromCache(config.getModelEnum());
|
||||
}
|
||||
try {
|
||||
if (recPredictorPool != null) {
|
||||
recPredictorPool.close();
|
||||
@@ -329,4 +348,14 @@ public class CRNNPlateRecModel implements PlateRecModel{
|
||||
log.warn("关闭 model 失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean fromFactory = false;
|
||||
|
||||
@Override
|
||||
public void setFromFactory(boolean fromFactory) {
|
||||
this.fromFactory = fromFactory;
|
||||
}
|
||||
public boolean isFromFactory() {
|
||||
return fromFactory;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ public interface PlateDetModel extends AutoCloseable{
|
||||
* @param imagePath 图片路径
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
default R<List<PlateInfo>> detect(String imagePath) {
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
@@ -42,6 +43,7 @@ public interface PlateDetModel extends AutoCloseable{
|
||||
* @param inputStream
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
default R<List<PlateInfo>> detect(InputStream inputStream) {
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
@@ -52,6 +54,7 @@ public interface PlateDetModel extends AutoCloseable{
|
||||
* @param base64Image
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
default R<List<PlateInfo>> detectBase64(String base64Image){
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
@@ -62,6 +65,7 @@ public interface PlateDetModel extends AutoCloseable{
|
||||
* @param image BufferedImage
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
default R<List<PlateInfo>> detect(BufferedImage image) {
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
@@ -72,6 +76,7 @@ public interface PlateDetModel extends AutoCloseable{
|
||||
* @param imageData 图片字节数组
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
default R<List<PlateInfo>> detect(byte[] imageData) {
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
@@ -81,7 +86,17 @@ public interface PlateDetModel extends AutoCloseable{
|
||||
* @param image DJL Image
|
||||
* @return
|
||||
*/
|
||||
default DetectedObjects detect(Image image){
|
||||
default DetectedObjects detectCore(Image image){
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 车牌检测
|
||||
* @param image DJL Image
|
||||
* @return
|
||||
*/
|
||||
default R<List<PlateInfo>> detect(Image image){
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
|
||||
@@ -100,16 +115,28 @@ public interface PlateDetModel extends AutoCloseable{
|
||||
* @param sourceImage
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
default R<BufferedImage> detectAndDraw(BufferedImage sourceImage){
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测并绘制结果
|
||||
* @param image
|
||||
* @return
|
||||
*/
|
||||
default Image detectAndDraw(Image image){
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
|
||||
default GenericObjectPool<Predictor<Image, DetectedObjects>> getPool(){
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
|
||||
|
||||
|
||||
default void setFromFactory(boolean fromFactory){
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ public interface PlateRecModel extends AutoCloseable{
|
||||
* @param imagePath 图片路径
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
default R<List<PlateInfo>> recognize(String imagePath) {
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
@@ -41,6 +42,7 @@ public interface PlateRecModel extends AutoCloseable{
|
||||
* @param inputStream
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
default R<List<PlateInfo>> recognize(InputStream inputStream) {
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
@@ -51,6 +53,7 @@ public interface PlateRecModel extends AutoCloseable{
|
||||
* @param base64Image
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
default R<List<PlateInfo>> recognizeBase64(String base64Image){
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
@@ -61,6 +64,7 @@ public interface PlateRecModel extends AutoCloseable{
|
||||
* @param image BufferedImage
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
default R<List<PlateInfo>> recognize(BufferedImage image) {
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
@@ -71,6 +75,7 @@ public interface PlateRecModel extends AutoCloseable{
|
||||
* @param imageData 图片字节数组
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
default R<List<PlateInfo>> recognize(byte[] imageData) {
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
@@ -107,12 +112,21 @@ public interface PlateRecModel extends AutoCloseable{
|
||||
* @param sourceImage
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
default R<BufferedImage> recognizeAndDraw(BufferedImage sourceImage){
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
|
||||
default R<Image> recognizeAndDraw(Image image){
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
|
||||
default GenericObjectPool<Predictor<Image, PlateResult>> getPool() {
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
|
||||
default void setFromFactory(boolean fromFactory){
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import ai.djl.MalformedModelException;
|
||||
import ai.djl.engine.Engine;
|
||||
import ai.djl.inference.Predictor;
|
||||
import ai.djl.modality.cv.Image;
|
||||
import ai.djl.modality.cv.ImageFactory;
|
||||
import ai.djl.modality.cv.output.DetectedObjects;
|
||||
import ai.djl.ndarray.NDList;
|
||||
import ai.djl.ndarray.NDManager;
|
||||
@@ -12,17 +11,17 @@ 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 cn.smartjavaai.common.cv.SmartImageFactory;
|
||||
import cn.smartjavaai.common.entity.R;
|
||||
import cn.smartjavaai.common.pool.PredictorFactory;
|
||||
import cn.smartjavaai.common.utils.Base64ImageUtils;
|
||||
import cn.smartjavaai.common.utils.FileUtils;
|
||||
import cn.smartjavaai.common.utils.ImageUtils;
|
||||
import cn.smartjavaai.common.utils.OpenCVUtils;
|
||||
import cn.smartjavaai.common.utils.*;
|
||||
import cn.smartjavaai.ocr.config.OcrDetModelConfig;
|
||||
import cn.smartjavaai.ocr.config.PlateDetModelConfig;
|
||||
import cn.smartjavaai.ocr.entity.OcrBox;
|
||||
import cn.smartjavaai.ocr.entity.PlateInfo;
|
||||
import cn.smartjavaai.ocr.exception.OcrException;
|
||||
import cn.smartjavaai.ocr.factory.OcrModelFactory;
|
||||
import cn.smartjavaai.ocr.factory.PlateModelFactory;
|
||||
import cn.smartjavaai.ocr.model.common.detect.criteria.OcrCommonDetCriterialFactory;
|
||||
import cn.smartjavaai.ocr.model.plate.criteria.PlateDetCriterialFactory;
|
||||
import cn.smartjavaai.ocr.utils.OcrUtils;
|
||||
@@ -90,17 +89,15 @@ public class Yolov5PlateDetModel implements PlateDetModel{
|
||||
}
|
||||
Image img = null;
|
||||
try {
|
||||
img = ImageFactory.getInstance().fromFile(Paths.get(imagePath));
|
||||
img = SmartImageFactory.getInstance().fromFile(Paths.get(imagePath));
|
||||
R<List<PlateInfo>> plateInfoList = detect(img);
|
||||
return plateInfoList;
|
||||
} catch (IOException e) {
|
||||
throw new OcrException("无效的图片", e);
|
||||
} finally {
|
||||
ImageUtils.releaseOpenCVMat(img);
|
||||
}
|
||||
DetectedObjects detectedObjects = detect(img);
|
||||
if (Objects.isNull(detectedObjects) || detectedObjects.getNumberOfObjects() == 0){
|
||||
return R.fail(R.Status.NO_OBJECT_DETECTED);
|
||||
}
|
||||
List<PlateInfo> plateInfoList = OcrUtils.convertToPlateInfo(detectedObjects, img);
|
||||
((Mat)img.getWrappedImage()).release();
|
||||
return R.ok(plateInfoList);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -114,17 +111,13 @@ public class Yolov5PlateDetModel implements PlateDetModel{
|
||||
|
||||
@Override
|
||||
public R<List<PlateInfo>> detect(BufferedImage image) {
|
||||
if(!ImageUtils.isImageValid(image)){
|
||||
if(!BufferedImageUtils.isImageValid(image)){
|
||||
return R.fail(R.Status.INVALID_IMAGE);
|
||||
}
|
||||
Image img = ImageFactory.getInstance().fromImage(OpenCVUtils.image2Mat(image));
|
||||
DetectedObjects detectedObjects = detect(img);
|
||||
if (Objects.isNull(detectedObjects) || detectedObjects.getNumberOfObjects() == 0){
|
||||
return R.fail(R.Status.NO_OBJECT_DETECTED);
|
||||
}
|
||||
List<PlateInfo> plateInfoList = OcrUtils.convertToPlateInfo(detectedObjects, img);
|
||||
((Mat)img.getWrappedImage()).release();
|
||||
return R.ok(plateInfoList);
|
||||
Image img = SmartImageFactory.getInstance().fromBufferedImage(image);
|
||||
R<List<PlateInfo>> plateInfoList = detect(img);
|
||||
ImageUtils.releaseOpenCVMat(img);
|
||||
return plateInfoList;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -136,7 +129,7 @@ public class Yolov5PlateDetModel implements PlateDetModel{
|
||||
}
|
||||
|
||||
@Override
|
||||
public DetectedObjects detect(Image image) {
|
||||
public DetectedObjects detectCore(Image image) {
|
||||
Predictor<Image, DetectedObjects> predictor = null;
|
||||
try {
|
||||
predictor = detPredictorPool.borrowObject();
|
||||
@@ -164,14 +157,15 @@ public class Yolov5PlateDetModel implements PlateDetModel{
|
||||
if(Objects.isNull(inputStream)){
|
||||
return R.fail(R.Status.INVALID_IMAGE);
|
||||
}
|
||||
Image img = null;
|
||||
try {
|
||||
Image img = ImageFactory.getInstance().fromInputStream(inputStream);
|
||||
DetectedObjects detection = detect(img);
|
||||
List<PlateInfo> plateInfoList = OcrUtils.convertToPlateInfo(detection, img);
|
||||
((Mat)img.getWrappedImage()).release();
|
||||
return R.ok(plateInfoList);
|
||||
img = SmartImageFactory.getInstance().fromInputStream(inputStream);
|
||||
R<List<PlateInfo>> plateInfoList = detect(img);
|
||||
return plateInfoList;
|
||||
} catch (IOException e) {
|
||||
throw new OcrException("无效图片输入流", e);
|
||||
} finally {
|
||||
ImageUtils.releaseOpenCVMat(img);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,8 +175,8 @@ public class Yolov5PlateDetModel implements PlateDetModel{
|
||||
return R.fail(R.Status.FILE_NOT_FOUND);
|
||||
}
|
||||
try {
|
||||
Image img = ImageFactory.getInstance().fromFile(Paths.get(imagePath));
|
||||
DetectedObjects detectedObjects = detect(img);
|
||||
Image img = SmartImageFactory.getInstance().fromFile(Paths.get(imagePath));
|
||||
DetectedObjects detectedObjects = detectCore(img);
|
||||
if(Objects.isNull(detectedObjects) || detectedObjects.getNumberOfObjects() == 0){
|
||||
return R.fail(R.Status.NO_FACE_DETECTED);
|
||||
}
|
||||
@@ -198,11 +192,11 @@ public class Yolov5PlateDetModel implements PlateDetModel{
|
||||
|
||||
@Override
|
||||
public R<BufferedImage> detectAndDraw(BufferedImage sourceImage) {
|
||||
if(!ImageUtils.isImageValid(sourceImage)){
|
||||
if(!BufferedImageUtils.isImageValid(sourceImage)){
|
||||
return R.fail(R.Status.INVALID_IMAGE);
|
||||
}
|
||||
Image img = ImageFactory.getInstance().fromImage(OpenCVUtils.image2Mat(sourceImage));
|
||||
DetectedObjects detectedObjects = detect(img);
|
||||
Image img = SmartImageFactory.getInstance().fromBufferedImage(sourceImage);
|
||||
DetectedObjects detectedObjects = detectCore(img);
|
||||
if(Objects.isNull(detectedObjects) || detectedObjects.getNumberOfObjects() == 0){
|
||||
return R.fail(R.Status.NO_FACE_DETECTED);
|
||||
}
|
||||
@@ -219,6 +213,27 @@ public class Yolov5PlateDetModel implements PlateDetModel{
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<List<PlateInfo>> detect(Image image) {
|
||||
DetectedObjects detectedObjects = detectCore(image);
|
||||
if (Objects.isNull(detectedObjects) || detectedObjects.getNumberOfObjects() == 0){
|
||||
return R.fail(R.Status.NO_OBJECT_DETECTED);
|
||||
}
|
||||
List<PlateInfo> plateInfoList = OcrUtils.convertToPlateInfo(detectedObjects, image);
|
||||
return R.ok(plateInfoList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Image detectAndDraw(Image image) {
|
||||
DetectedObjects detectedObjects = detectCore(image);
|
||||
if (Objects.isNull(detectedObjects) || detectedObjects.getNumberOfObjects() == 0){
|
||||
throw new OcrException("未检测到车牌");
|
||||
}
|
||||
Image img = ImageUtils.copy(image);
|
||||
img.drawBoundingBoxes(detectedObjects);
|
||||
return img;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GenericObjectPool<Predictor<Image, DetectedObjects>> getPool() {
|
||||
return detPredictorPool;
|
||||
@@ -226,6 +241,9 @@ public class Yolov5PlateDetModel implements PlateDetModel{
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
if (fromFactory) {
|
||||
PlateModelFactory.removeDetModelFromCache(config.getModelEnum());
|
||||
}
|
||||
try {
|
||||
if (detPredictorPool != null) {
|
||||
detPredictorPool.close();
|
||||
@@ -241,4 +259,14 @@ public class Yolov5PlateDetModel implements PlateDetModel{
|
||||
log.warn("关闭 model 失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean fromFactory = false;
|
||||
|
||||
@Override
|
||||
public void setFromFactory(boolean fromFactory) {
|
||||
this.fromFactory = fromFactory;
|
||||
}
|
||||
public boolean isFromFactory() {
|
||||
return fromFactory;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,10 @@ 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 cn.smartjavaai.common.cv.SmartImageFactory;
|
||||
import cn.smartjavaai.common.entity.R;
|
||||
import cn.smartjavaai.common.pool.PredictorFactory;
|
||||
import cn.smartjavaai.common.utils.BufferedImageUtils;
|
||||
import cn.smartjavaai.common.utils.FileUtils;
|
||||
import cn.smartjavaai.common.utils.ImageUtils;
|
||||
import cn.smartjavaai.common.utils.OpenCVUtils;
|
||||
@@ -22,6 +24,8 @@ import cn.smartjavaai.ocr.entity.OcrBox;
|
||||
import cn.smartjavaai.ocr.entity.OcrItem;
|
||||
import cn.smartjavaai.ocr.entity.TableStructureResult;
|
||||
import cn.smartjavaai.ocr.exception.OcrException;
|
||||
import cn.smartjavaai.ocr.factory.PlateModelFactory;
|
||||
import cn.smartjavaai.ocr.factory.TableRecModelFactory;
|
||||
import cn.smartjavaai.ocr.model.table.criteria.StructureCriteriaFactory;
|
||||
import cn.smartjavaai.ocr.utils.OcrUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -49,8 +53,11 @@ public class CommonTableStructureModel implements TableStructureModel{
|
||||
|
||||
private GenericObjectPool<Predictor<Image, TableStructureResult>> predictorPool;
|
||||
|
||||
private TableStructureConfig config;
|
||||
|
||||
@Override
|
||||
public void loadModel(TableStructureConfig config) {
|
||||
this.config = config;
|
||||
if(StringUtils.isBlank(config.getModelPath())){
|
||||
throw new OcrException("modelPath is null");
|
||||
}
|
||||
@@ -74,19 +81,17 @@ public class CommonTableStructureModel implements TableStructureModel{
|
||||
|
||||
@Override
|
||||
public R<TableStructureResult> detect(BufferedImage image) {
|
||||
if(!ImageUtils.isImageValid(image)){
|
||||
if(!BufferedImageUtils.isImageValid(image)){
|
||||
return R.fail(R.Status.INVALID_IMAGE);
|
||||
}
|
||||
Image img = null;
|
||||
try {
|
||||
img = ImageFactory.getInstance().fromImage(OpenCVUtils.image2Mat(image));
|
||||
img = SmartImageFactory.getInstance().fromBufferedImage(image);
|
||||
return detect(img);
|
||||
} catch (Exception e) {
|
||||
throw new OcrException(e);
|
||||
} finally {
|
||||
if(Objects.nonNull(img)){
|
||||
((Mat)img.getWrappedImage()).release();
|
||||
}
|
||||
ImageUtils.releaseOpenCVMat(img);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,14 +102,12 @@ public class CommonTableStructureModel implements TableStructureModel{
|
||||
}
|
||||
Image img = null;
|
||||
try {
|
||||
img = ImageFactory.getInstance().fromFile(Paths.get(imagePath));
|
||||
img = SmartImageFactory.getInstance().fromFile(Paths.get(imagePath));
|
||||
return detect(img);
|
||||
} catch (IOException e) {
|
||||
throw new OcrException("无效的图片", e);
|
||||
} finally {
|
||||
if (Objects.nonNull(img)){
|
||||
((Mat)img.getWrappedImage()).release();
|
||||
}
|
||||
ImageUtils.releaseOpenCVMat(img);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,6 +156,9 @@ public class CommonTableStructureModel implements TableStructureModel{
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
if (fromFactory) {
|
||||
TableRecModelFactory.removeFromCache(config.getModelEnum());
|
||||
}
|
||||
try {
|
||||
if (predictorPool != null) {
|
||||
predictorPool.close();
|
||||
@@ -168,4 +174,14 @@ public class CommonTableStructureModel implements TableStructureModel{
|
||||
log.warn("关闭 model 失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean fromFactory = false;
|
||||
|
||||
@Override
|
||||
public void setFromFactory(boolean fromFactory) {
|
||||
this.fromFactory = fromFactory;
|
||||
}
|
||||
public boolean isFromFactory() {
|
||||
return fromFactory;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
package cn.smartjavaai.ocr.model.table;
|
||||
|
||||
import ai.djl.modality.cv.Image;
|
||||
import ai.djl.modality.cv.ImageFactory;
|
||||
import ai.djl.modality.cv.output.BoundingBox;
|
||||
import ai.djl.modality.cv.output.DetectedObjects;
|
||||
import ai.djl.modality.cv.output.Rectangle;
|
||||
import ai.djl.translate.TranslateException;
|
||||
import ai.djl.util.JsonUtils;
|
||||
import cn.smartjavaai.common.cv.SmartImageFactory;
|
||||
import cn.smartjavaai.common.entity.DetectionRectangle;
|
||||
import cn.smartjavaai.common.entity.R;
|
||||
import cn.smartjavaai.common.utils.BufferedImageUtils;
|
||||
import cn.smartjavaai.common.utils.FileUtils;
|
||||
import cn.smartjavaai.common.utils.ImageUtils;
|
||||
import cn.smartjavaai.common.utils.OpenCVUtils;
|
||||
import cn.smartjavaai.ocr.config.OcrRecModelConfig;
|
||||
import cn.smartjavaai.ocr.config.OcrRecOptions;
|
||||
import cn.smartjavaai.ocr.entity.OcrBox;
|
||||
import cn.smartjavaai.ocr.entity.OcrInfo;
|
||||
@@ -28,7 +22,6 @@ import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
import org.opencv.core.Mat;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
@@ -107,19 +100,17 @@ public class TableRecognizer {
|
||||
* @return
|
||||
*/
|
||||
public R<TableStructureResult> recognize(BufferedImage image) {
|
||||
if(!ImageUtils.isImageValid(image)){
|
||||
if(!BufferedImageUtils.isImageValid(image)){
|
||||
return R.fail(R.Status.INVALID_IMAGE);
|
||||
}
|
||||
Image img = null;
|
||||
try {
|
||||
img = ImageFactory.getInstance().fromImage(OpenCVUtils.image2Mat(image));
|
||||
img = SmartImageFactory.getInstance().fromBufferedImage(image);
|
||||
return recognize(img);
|
||||
} catch (Exception e) {
|
||||
throw new OcrException(e);
|
||||
} finally {
|
||||
if(Objects.nonNull(img)){
|
||||
((Mat)img.getWrappedImage()).release();
|
||||
}
|
||||
ImageUtils.releaseOpenCVMat(img);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,14 +125,12 @@ public class TableRecognizer {
|
||||
}
|
||||
Image img = null;
|
||||
try {
|
||||
img = ImageFactory.getInstance().fromFile(Paths.get(imagePath));
|
||||
img = SmartImageFactory.getInstance().fromFile(Paths.get(imagePath));
|
||||
return recognize(img);
|
||||
} catch (IOException e) {
|
||||
throw new OcrException("无效的图片", e);
|
||||
} finally {
|
||||
if (Objects.nonNull(img)){
|
||||
((Mat)img.getWrappedImage()).release();
|
||||
}
|
||||
ImageUtils.releaseOpenCVMat(img);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,9 +165,13 @@ public class TableRecognizer {
|
||||
for (int i = 0; i < tableStructureResult.getOcrItemList().size(); i++){
|
||||
OcrItem item = tableStructureResult.getOcrItemList().get(i);
|
||||
DetectionRectangle detectionRectangle = item.getOcrBox().toDetectionRectangle();
|
||||
ImageUtils.drawImageRectWithText(image, detectionRectangle, i + "", Color.RED);
|
||||
BufferedImageUtils.drawRectAndText(image, detectionRectangle, i + "", Color.RED);
|
||||
}
|
||||
try {
|
||||
BufferedImageUtils.saveImage(image, savePath);
|
||||
} catch (IOException e) {
|
||||
throw new OcrException(e);
|
||||
}
|
||||
ImageUtils.saveImage(image, savePath);
|
||||
}
|
||||
|
||||
|
||||
@@ -195,7 +188,25 @@ public class TableRecognizer {
|
||||
for (int i = 0; i < tableStructureResult.getOcrItemList().size(); i++){
|
||||
OcrItem item = tableStructureResult.getOcrItemList().get(i);
|
||||
DetectionRectangle detectionRectangle = item.getOcrBox().toDetectionRectangle();
|
||||
ImageUtils.drawImageRectWithText(image, detectionRectangle, i + "", Color.RED);
|
||||
BufferedImageUtils.drawRectAndText(image, detectionRectangle, i + "", Color.RED);
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制表格
|
||||
* @param tableStructureResult
|
||||
* @param image
|
||||
* @return
|
||||
*/
|
||||
public Image drawTable(TableStructureResult tableStructureResult, Image image){
|
||||
if(Objects.isNull(tableStructureResult) || CollectionUtils.isEmpty(tableStructureResult.getTableTagList())){
|
||||
throw new OcrException("表格结构为空");
|
||||
}
|
||||
for (int i = 0; i < tableStructureResult.getOcrItemList().size(); i++){
|
||||
OcrItem item = tableStructureResult.getOcrItemList().get(i);
|
||||
DetectionRectangle detectionRectangle = item.getOcrBox().toDetectionRectangle();
|
||||
ImageUtils.drawRectAndText(image, detectionRectangle, i + "");
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ public interface TableStructureModel extends AutoCloseable{
|
||||
* @param image
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
default R<TableStructureResult> detect(BufferedImage image){
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
@@ -40,6 +41,7 @@ public interface TableStructureModel extends AutoCloseable{
|
||||
* @param imagePath 图片路径
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
default R<TableStructureResult> detect(String imagePath) {
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
@@ -49,6 +51,7 @@ public interface TableStructureModel extends AutoCloseable{
|
||||
* @param imageData 图片字节数组
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
default R<TableStructureResult> detect(byte[] imageData) {
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
@@ -66,4 +69,10 @@ public interface TableStructureModel extends AutoCloseable{
|
||||
default GenericObjectPool<Predictor<Image, TableStructureResult>> getPool() {
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
|
||||
default void setFromFactory(boolean fromFactory){
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,228 +0,0 @@
|
||||
package cn.smartjavaai.ocr.opencv;
|
||||
|
||||
import ai.djl.ndarray.NDArray;
|
||||
import org.opencv.core.CvType;
|
||||
import org.opencv.core.Mat;
|
||||
import org.opencv.core.MatOfPoint;
|
||||
import org.opencv.core.Point;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
/**
|
||||
* NDArray Utils
|
||||
*
|
||||
*/
|
||||
public class OcrNDArrayUtils {
|
||||
/**
|
||||
* Mat To MatOfPoint
|
||||
* @param mat
|
||||
* @return
|
||||
*/
|
||||
public static MatOfPoint matToMatOfPoint(Mat mat) {
|
||||
int rows = mat.rows();
|
||||
MatOfPoint matOfPoint = new MatOfPoint();
|
||||
|
||||
List<Point> list = new ArrayList<>();
|
||||
for (int i = 0; i < rows; i++) {
|
||||
Point point = new Point((float) mat.get(i, 0)[0], (float) mat.get(i, 1)[0]);
|
||||
list.add(point);
|
||||
}
|
||||
matOfPoint.fromList(list);
|
||||
|
||||
return matOfPoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* float NDArray To float[][] Array
|
||||
* @param ndArray
|
||||
* @return
|
||||
*/
|
||||
public static float[][] floatNDArrayToArray(NDArray ndArray) {
|
||||
int rows = (int) (ndArray.getShape().get(0));
|
||||
int cols = (int) (ndArray.getShape().get(1));
|
||||
float[][] arr = new float[rows][cols];
|
||||
|
||||
float[] arrs = ndArray.toFloatArray();
|
||||
for (int i = 0; i < rows; i++) {
|
||||
for (int j = 0; j < cols; j++) {
|
||||
arr[i][j] = arrs[i * cols + j];
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mat To double[][] Array
|
||||
* @param mat
|
||||
* @return
|
||||
*/
|
||||
public static double[][] matToDoubleArray(Mat mat) {
|
||||
int rows = mat.rows();
|
||||
int cols = mat.cols();
|
||||
|
||||
double[][] doubles = new double[rows][cols];
|
||||
|
||||
for (int i = 0; i < rows; i++) {
|
||||
for (int j = 0; j < cols; j++) {
|
||||
doubles[i][j] = mat.get(i, j)[0];
|
||||
}
|
||||
}
|
||||
|
||||
return doubles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mat To float[][] Array
|
||||
* @param mat
|
||||
* @return
|
||||
*/
|
||||
public static float[][] matToFloatArray(Mat mat) {
|
||||
int rows = mat.rows();
|
||||
int cols = mat.cols();
|
||||
|
||||
float[][] floats = new float[rows][cols];
|
||||
|
||||
for (int i = 0; i < rows; i++) {
|
||||
for (int j = 0; j < cols; j++) {
|
||||
floats[i][j] = (float) mat.get(i, j)[0];
|
||||
}
|
||||
}
|
||||
|
||||
return floats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mat To byte[][] Array
|
||||
* @param mat
|
||||
* @return
|
||||
*/
|
||||
public static byte[][] matToUint8Array(Mat mat) {
|
||||
int rows = mat.rows();
|
||||
int cols = mat.cols();
|
||||
|
||||
byte[][] bytes = new byte[rows][cols];
|
||||
|
||||
for (int i = 0; i < rows; i++) {
|
||||
for (int j = 0; j < cols; j++) {
|
||||
bytes[i][j] = (byte) mat.get(i, j)[0];
|
||||
}
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* float NDArray To float[][] Array
|
||||
* @param ndArray
|
||||
* @param cvType
|
||||
* @return
|
||||
*/
|
||||
public static Mat floatNDArrayToMat(NDArray ndArray, int cvType) {
|
||||
int rows = (int) (ndArray.getShape().get(0));
|
||||
int cols = (int) (ndArray.getShape().get(1));
|
||||
Mat mat = new Mat(rows, cols, cvType);
|
||||
|
||||
float[] arrs = ndArray.toFloatArray();
|
||||
for (int i = 0; i < rows; i++) {
|
||||
for (int j = 0; j < cols; j++) {
|
||||
mat.put(i, j, arrs[i * cols + j]);
|
||||
}
|
||||
}
|
||||
return mat;
|
||||
}
|
||||
|
||||
/**
|
||||
* float NDArray To Mat
|
||||
* @param ndArray
|
||||
* @return
|
||||
*/
|
||||
public static Mat floatNDArrayToMat(NDArray ndArray) {
|
||||
int rows = (int) (ndArray.getShape().get(0));
|
||||
int cols = (int) (ndArray.getShape().get(1));
|
||||
Mat mat = new Mat(rows, cols, CvType.CV_32F);
|
||||
|
||||
float[] arrs = ndArray.toFloatArray();
|
||||
for (int i = 0; i < rows; i++) {
|
||||
for (int j = 0; j < cols; j++) {
|
||||
mat.put(i, j, arrs[i * cols + j]);
|
||||
}
|
||||
}
|
||||
|
||||
return mat;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* uint8 NDArray To Mat
|
||||
* @param ndArray
|
||||
* @return
|
||||
*/
|
||||
public static Mat uint8NDArrayToMat(NDArray ndArray) {
|
||||
int rows = (int) (ndArray.getShape().get(0));
|
||||
int cols = (int) (ndArray.getShape().get(1));
|
||||
Mat mat = new Mat(rows, cols, CvType.CV_8U);
|
||||
|
||||
byte[] arrs = ndArray.toByteArray();
|
||||
|
||||
for (int i = 0; i < rows; i++) {
|
||||
for (int j = 0; j < cols; j++) {
|
||||
mat.put(i, j, arrs[i * cols + j]);
|
||||
}
|
||||
}
|
||||
return mat;
|
||||
}
|
||||
|
||||
/**
|
||||
* float[][] Array To Mat
|
||||
* @param arr
|
||||
* @return
|
||||
*/
|
||||
public static Mat floatArrayToMat(float[][] arr) {
|
||||
int rows = arr.length;
|
||||
int cols = arr[0].length;
|
||||
Mat mat = new Mat(rows, cols, CvType.CV_32F);
|
||||
|
||||
for (int i = 0; i < rows; i++) {
|
||||
for (int j = 0; j < cols; j++) {
|
||||
mat.put(i, j, arr[i][j]);
|
||||
}
|
||||
}
|
||||
|
||||
return mat;
|
||||
}
|
||||
|
||||
/**
|
||||
* byte[][] Array To Mat
|
||||
* @param arr
|
||||
* @return
|
||||
*/
|
||||
public static Mat uint8ArrayToMat(byte[][] arr) {
|
||||
int rows = arr.length;
|
||||
int cols = arr[0].length;
|
||||
Mat mat = new Mat(rows, cols, CvType.CV_8U);
|
||||
|
||||
for (int i = 0; i < rows; i++) {
|
||||
for (int j = 0; j < cols; j++) {
|
||||
mat.put(i, j, arr[i][j]);
|
||||
}
|
||||
}
|
||||
|
||||
return mat;
|
||||
}
|
||||
|
||||
/**
|
||||
* List To Mat
|
||||
* @param points
|
||||
* @return
|
||||
*/
|
||||
public static Mat toMat(List<ai.djl.modality.cv.output.Point> points) {
|
||||
Mat mat = new Mat(points.size(), 2, CvType.CV_32F);
|
||||
for (int i = 0; i < points.size(); i++) {
|
||||
ai.djl.modality.cv.output.Point point = points.get(i);
|
||||
mat.put(i, 0, (float) point.getX());
|
||||
mat.put(i, 1, (float) point.getY());
|
||||
}
|
||||
|
||||
return mat;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package cn.smartjavaai.ocr.utils;
|
||||
|
||||
import ai.djl.modality.cv.Image;
|
||||
import ai.djl.modality.cv.ImageFactory;
|
||||
import ai.djl.modality.cv.output.BoundingBox;
|
||||
import ai.djl.modality.cv.output.DetectedObjects;
|
||||
import ai.djl.modality.cv.output.Landmark;
|
||||
@@ -10,16 +9,14 @@ import ai.djl.ndarray.NDArray;
|
||||
import ai.djl.ndarray.NDList;
|
||||
import ai.djl.ndarray.NDManager;
|
||||
import ai.djl.opencv.OpenCVImageFactory;
|
||||
import cn.smartjavaai.common.cv.SmartImageFactory;
|
||||
import cn.smartjavaai.common.entity.*;
|
||||
import cn.smartjavaai.common.entity.Point;
|
||||
import cn.smartjavaai.common.utils.ImageUtils;
|
||||
import cn.smartjavaai.common.utils.OpenCVUtils;
|
||||
import cn.smartjavaai.common.utils.PointUtils;
|
||||
import cn.smartjavaai.common.utils.*;
|
||||
import cn.smartjavaai.ocr.entity.*;
|
||||
import cn.smartjavaai.ocr.entity.RotatedBox;
|
||||
import cn.smartjavaai.ocr.enums.AngleEnum;
|
||||
import cn.smartjavaai.ocr.enums.PlateType;
|
||||
import cn.smartjavaai.ocr.opencv.OcrNDArrayUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.opencv.core.Mat;
|
||||
@@ -28,7 +25,6 @@ import org.opencv.imgproc.Imgproc;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
@@ -79,33 +75,6 @@ public class OcrUtils {
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 图片旋转
|
||||
*
|
||||
* @param manager
|
||||
* @param image
|
||||
* @return
|
||||
*/
|
||||
public static Image rotateImg(NDManager manager, Image image) {
|
||||
NDArray rotated = NDImageUtils.rotate90(image.toNDArray(manager), 1);
|
||||
return ImageFactory.getInstance().fromNDArray(rotated);
|
||||
}
|
||||
|
||||
/**
|
||||
* 逆时针旋转图片
|
||||
*
|
||||
* @param image
|
||||
* @param times
|
||||
* @return
|
||||
*/
|
||||
public static Image rotateImg(Image image, int times) {
|
||||
try (NDManager manager = NDManager.newBaseManager()) {
|
||||
NDArray rotated = NDImageUtils.rotate90(image.toNDArray(manager), times);
|
||||
return OpenCVImageFactory.getInstance().fromNDArray(rotated);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 逆时针旋转图片
|
||||
*
|
||||
@@ -199,66 +168,21 @@ public class OcrUtils {
|
||||
/**
|
||||
* 透视变换 + 裁剪
|
||||
* @param srcMat
|
||||
* @param landMarks
|
||||
* @return
|
||||
*/
|
||||
public static Image transformAndCrop(Mat srcMat, List<ai.djl.modality.cv.output.Point> landMarks){
|
||||
if (landMarks == null || landMarks.size() != 4) {
|
||||
throw new IllegalArgumentException("必须提供4个关键点");
|
||||
}
|
||||
|
||||
// 步骤 1:排序为 左上、右上、右下、左下
|
||||
List<ai.djl.modality.cv.output.Point> ordered = PointUtils.orderPoints(landMarks);
|
||||
|
||||
ai.djl.modality.cv.output.Point lt = ordered.get(0);
|
||||
ai.djl.modality.cv.output.Point rt = ordered.get(1);
|
||||
ai.djl.modality.cv.output.Point rb = ordered.get(2);
|
||||
ai.djl.modality.cv.output.Point lb = ordered.get(3);
|
||||
|
||||
// 步骤 2:计算目标图像尺寸(宽、高)
|
||||
int img_crop_width = (int) Math.max(
|
||||
PointUtils.distance(lt, rt),
|
||||
PointUtils.distance(rb, lb)
|
||||
);
|
||||
int img_crop_height = (int) Math.max(
|
||||
PointUtils.distance(lt, lb),
|
||||
PointUtils.distance(rt, rb)
|
||||
);
|
||||
|
||||
// 步骤 3:构造目标坐标点
|
||||
List<ai.djl.modality.cv.output.Point> dstPoints = Arrays.asList(
|
||||
new ai.djl.modality.cv.output.Point(0, 0),
|
||||
new ai.djl.modality.cv.output.Point(img_crop_width, 0),
|
||||
new ai.djl.modality.cv.output.Point(img_crop_width, img_crop_height),
|
||||
new ai.djl.modality.cv.output.Point(0, img_crop_height)
|
||||
);
|
||||
|
||||
// 步骤 4:透视变换
|
||||
Mat srcPoint2f = OcrNDArrayUtils.toMat(ordered);
|
||||
Mat dstPoint2f = OcrNDArrayUtils.toMat(dstPoints);
|
||||
Mat cvMat = OpenCVUtils.perspectiveTransform(srcMat, srcPoint2f, dstPoint2f);
|
||||
|
||||
// 步骤 5:转为 DJL Image + 裁剪
|
||||
Image subImg = OpenCVImageFactory.getInstance().fromImage(cvMat);
|
||||
subImg = subImg.getSubImage(0, 0, img_crop_width, img_crop_height);
|
||||
|
||||
// 释放资源
|
||||
cvMat.release();
|
||||
srcPoint2f.release();
|
||||
dstPoint2f.release();
|
||||
|
||||
return subImg;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 透视变换+裁剪
|
||||
* @param srcMat
|
||||
* @param box
|
||||
* @return
|
||||
*/
|
||||
public static Image transformAndCrop(Mat srcMat, OcrBox box){
|
||||
Mat subImg = transformAndCropToMat(srcMat, box);
|
||||
return SmartImageFactory.getInstance().fromMat(subImg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 透视变换 + 裁剪
|
||||
* @param srcMat
|
||||
* @param box
|
||||
* @return
|
||||
*/
|
||||
public static Mat transformAndCropToMat(Mat srcMat, OcrBox box){
|
||||
float[] pointsArr = box.toFloatArray();
|
||||
float[] lt = java.util.Arrays.copyOfRange(pointsArr, 0, 2);
|
||||
float[] rt = java.util.Arrays.copyOfRange(pointsArr, 2, 4);
|
||||
@@ -276,18 +200,15 @@ public class OcrUtils {
|
||||
dstPoints.add(new ai.djl.modality.cv.output.Point(img_crop_width, 0));
|
||||
dstPoints.add(new ai.djl.modality.cv.output.Point(img_crop_width, img_crop_height));
|
||||
dstPoints.add(new ai.djl.modality.cv.output.Point(0, img_crop_height));
|
||||
Mat srcPoint2f = OcrNDArrayUtils.toMat(srcPoints);
|
||||
Mat dstPoint2f = OcrNDArrayUtils.toMat(dstPoints);
|
||||
Mat srcPoint2f = DJLCommonUtils.toMat(srcPoints);
|
||||
Mat dstPoint2f = DJLCommonUtils.toMat(dstPoints);
|
||||
//透视变换
|
||||
Mat cvMat = OpenCVUtils.perspectiveTransform(srcMat, srcPoint2f, dstPoint2f);
|
||||
Image subImg = OpenCVImageFactory.getInstance().fromImage(cvMat);
|
||||
//ImageUtils.saveImage(subImg, i + ".png", "build/output");
|
||||
//变换后裁剪
|
||||
subImg = subImg.getSubImage(0, 0, img_crop_width, img_crop_height);
|
||||
cvMat.release();
|
||||
srcPoint2f.release();
|
||||
dstPoint2f.release();
|
||||
return subImg;
|
||||
Mat result = OpenCVUtils.getSubImage(cvMat, 0, 0, img_crop_width, img_crop_height);
|
||||
cvMat.release();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -307,65 +228,116 @@ public class OcrUtils {
|
||||
|
||||
|
||||
/**
|
||||
* 绘制文本框及文本
|
||||
* @param image
|
||||
* @param ocrInfo
|
||||
* 将 OCR 结果转换为多边形标签列表
|
||||
* @param ocrItemList OCR 识别结果
|
||||
* @return PolygonLabel 列表
|
||||
*/
|
||||
public static void drawRectWithText(BufferedImage image, OcrInfo ocrInfo, int fontSize) {
|
||||
// 将绘制图像转换为Graphics2D
|
||||
Graphics2D g = (Graphics2D) image.getGraphics();
|
||||
try {
|
||||
Font font = new Font("楷体", Font.PLAIN, fontSize);
|
||||
g.setFont(font);
|
||||
g.setColor(new Color(0, 0, 255));
|
||||
// 声明画笔属性 :粗 细(单位像素)末端无修饰 折线处呈尖角
|
||||
BasicStroke bStroke = new BasicStroke(2, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER);
|
||||
g.setStroke(bStroke);
|
||||
List<OcrItem> ocrItemList = ocrInfo.getOcrItemList();
|
||||
if(CollectionUtils.isNotEmpty(ocrInfo.getLineList())){
|
||||
ocrItemList = ocrInfo.flattenLines();
|
||||
}
|
||||
for(OcrItem item : ocrItemList){
|
||||
OcrBox box = item.getOcrBox();
|
||||
int[] xPoints = {
|
||||
(int)box.getTopLeft().getX(),
|
||||
(int)box.getTopRight().getX(),
|
||||
(int)box.getBottomRight().getX(),
|
||||
(int)box.getBottomLeft().getX(),
|
||||
(int)box.getTopLeft().getX()
|
||||
};
|
||||
int[] yPoints = {
|
||||
(int)box.getTopLeft().getY(),
|
||||
(int)box.getTopRight().getY(),
|
||||
(int)box.getBottomRight().getY(),
|
||||
(int)box.getBottomLeft().getY(),
|
||||
(int)box.getTopLeft().getY()
|
||||
};
|
||||
g.drawPolyline(xPoints, yPoints, 5);
|
||||
g.drawString(item.getText(), xPoints[0], yPoints[0]);
|
||||
}
|
||||
} finally {
|
||||
g.dispose();
|
||||
public static List<PolygonLabel> toPolygonLabelList(List<OcrItem> ocrItemList) {
|
||||
List<PolygonLabel> polygonLabelList = new ArrayList<>();
|
||||
if (ocrItemList == null || ocrItemList.isEmpty()) {
|
||||
return polygonLabelList;
|
||||
}
|
||||
for (OcrItem item : ocrItemList) {
|
||||
if (item.getOcrBox() == null) continue;
|
||||
List<cn.smartjavaai.common.entity.Point> points = Arrays.asList(
|
||||
item.getOcrBox().getTopLeft(),
|
||||
item.getOcrBox().getTopRight(),
|
||||
item.getOcrBox().getBottomRight(),
|
||||
item.getOcrBox().getBottomLeft()
|
||||
);
|
||||
String text = null;
|
||||
//角度
|
||||
if(item.getAngle() != null){
|
||||
text = item.getAngle().getValue();
|
||||
}else{
|
||||
text = item.getText();
|
||||
}
|
||||
PolygonLabel label = new PolygonLabel(points, text);
|
||||
polygonLabelList.add(label);
|
||||
}
|
||||
return polygonLabelList;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将 OCR 结果转换为多边形标签列表
|
||||
* @param boxList
|
||||
* @return PolygonLabel 列表
|
||||
*/
|
||||
public static List<PolygonLabel> ocrBoxtoPolygonLabelList(List<OcrBox> boxList) {
|
||||
List<PolygonLabel> polygonLabelList = new ArrayList<>();
|
||||
if (boxList == null || boxList.isEmpty()) {
|
||||
return polygonLabelList;
|
||||
}
|
||||
for (OcrBox item : boxList) {
|
||||
List<cn.smartjavaai.common.entity.Point> points = Arrays.asList(
|
||||
item.getTopLeft(),
|
||||
item.getTopRight(),
|
||||
item.getBottomRight(),
|
||||
item.getBottomLeft()
|
||||
);
|
||||
PolygonLabel label = new PolygonLabel(points);
|
||||
polygonLabelList.add(label);
|
||||
}
|
||||
return polygonLabelList;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* OCR 结果绘制
|
||||
*/
|
||||
public static void drawOcrResult(BufferedImage image, OcrInfo ocrInfo, int fontSize) {
|
||||
List<OcrItem> ocrItemList = ocrInfo.getOcrItemList();
|
||||
if (CollectionUtils.isNotEmpty(ocrInfo.getLineList())) {
|
||||
ocrItemList = ocrInfo.flattenLines();
|
||||
}
|
||||
List<PolygonLabel> polygonLabelList = toPolygonLabelList(ocrItemList);
|
||||
BufferedImageUtils.drawPolygonWithText(image, polygonLabelList, fontSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* OCR 结果绘制
|
||||
*/
|
||||
public static void drawOcrResult(BufferedImage image, List<OcrItem> ocrItemList, int fontSize) {
|
||||
List<PolygonLabel> polygonLabelList = toPolygonLabelList(ocrItemList);
|
||||
BufferedImageUtils.drawPolygonWithText(image, polygonLabelList, fontSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* OCR 结果绘制
|
||||
*/
|
||||
public static void drawOcrDetResult(Image image, List<OcrBox> ocrBoxList, int fontSize) {
|
||||
List<PolygonLabel> polygonLabelList = ocrBoxtoPolygonLabelList(ocrBoxList);
|
||||
ImageUtils.drawPolygonWithText(image, polygonLabelList, fontSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* OCR 结果绘制
|
||||
*/
|
||||
public static void drawOcrResult(Image image, List<OcrItem> ocrItemList, int fontSize) {
|
||||
List<PolygonLabel> polygonLabelList = toPolygonLabelList(ocrItemList);
|
||||
ImageUtils.drawPolygonWithText(image, polygonLabelList, fontSize);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 绘制文本框及文本
|
||||
* @param srcMat
|
||||
* @param itemList
|
||||
*/
|
||||
public static void drawRectWithText(Mat srcMat, List<OcrItem> itemList) {
|
||||
for(OcrItem item : itemList){
|
||||
OcrBox ocrBox = item.getOcrBox();
|
||||
Imgproc.line(srcMat, ocrBox.getTopLeft().toCvPoint(), ocrBox.getTopRight().toCvPoint(), new Scalar(0, 255, 0), 1);
|
||||
Imgproc.line(srcMat, ocrBox.getTopRight().toCvPoint(), ocrBox.getBottomRight().toCvPoint(), new Scalar(0, 255, 0),1);
|
||||
Imgproc.line(srcMat, ocrBox.getBottomRight().toCvPoint(), ocrBox.getBottomLeft().toCvPoint(), new Scalar(0, 255, 0),1);
|
||||
Imgproc.line(srcMat, ocrBox.getBottomLeft().toCvPoint(), ocrBox.getTopLeft().toCvPoint(), new Scalar(0, 255, 0), 1);
|
||||
// 中文乱码
|
||||
Imgproc.putText(srcMat, item.getAngle().getValue(), ocrBox.getTopLeft().toCvPoint(), Imgproc.FONT_HERSHEY_SCRIPT_SIMPLEX, 1.0, new Scalar(0, 255, 0), 1);
|
||||
}
|
||||
}
|
||||
// public static void drawRectWithText(Mat srcMat, List<OcrItem> itemList) {
|
||||
// for(OcrItem item : itemList){
|
||||
// OcrBox ocrBox = item.getOcrBox();
|
||||
// Imgproc.line(srcMat, ocrBox.getTopLeft().toCvPoint(), ocrBox.getTopRight().toCvPoint(), new Scalar(0, 255, 0), 1);
|
||||
// Imgproc.line(srcMat, ocrBox.getTopRight().toCvPoint(), ocrBox.getBottomRight().toCvPoint(), new Scalar(0, 255, 0),1);
|
||||
// Imgproc.line(srcMat, ocrBox.getBottomRight().toCvPoint(), ocrBox.getBottomLeft().toCvPoint(), new Scalar(0, 255, 0),1);
|
||||
// Imgproc.line(srcMat, ocrBox.getBottomLeft().toCvPoint(), ocrBox.getTopLeft().toCvPoint(), new Scalar(0, 255, 0), 1);
|
||||
// // 中文乱码
|
||||
// Imgproc.putText(srcMat, item.getAngle().getValue(), ocrBox.getTopLeft().toCvPoint(), Imgproc.FONT_HERSHEY_SIMPLEX, 1.0, new Scalar(0, 255, 0), 1);
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
public static List<PlateInfo> convertToPlateInfo(DetectedObjects detectedObjects, Image image) {
|
||||
List<PlateInfo> plateInfoList = new ArrayList<>();
|
||||
@@ -414,7 +386,7 @@ public class OcrUtils {
|
||||
Imgproc.line(srcMat, ocrBox.getBottomRight().toCvPoint(), ocrBox.getBottomLeft().toCvPoint(), new Scalar(0, 0, 255),1);
|
||||
Imgproc.line(srcMat, ocrBox.getBottomLeft().toCvPoint(), ocrBox.getTopLeft().toCvPoint(), new Scalar(0, 0, 255), 1);
|
||||
// 中文乱码
|
||||
ImageUtils.putTextWithBackground(srcMat, plateInfo.getPlateNumber() + " " + plateInfo.getPlateColor(), ocrBox.getTopLeft().toCvPoint(), new Scalar(255, 255, 255), new Scalar(0, 0, 0), 1);
|
||||
OpenCVUtils.putTextWithBackground(srcMat, plateInfo.getPlateNumber() + " " + plateInfo.getPlateColor(), ocrBox.getTopLeft().toCvPoint(), new Scalar(255, 255, 255), new Scalar(0, 0, 0), 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -424,33 +396,19 @@ public class OcrUtils {
|
||||
public static void drawPlateInfo(BufferedImage image, List<PlateInfo> plateInfoList) {
|
||||
// 将绘制图像转换为Graphics2D
|
||||
Graphics2D graphics = (Graphics2D) image.getGraphics();
|
||||
try {
|
||||
graphics.setColor(Color.RED);// 边框颜色
|
||||
graphics.setStroke(new BasicStroke(2)); // 线宽2像素
|
||||
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
|
||||
RenderingHints.VALUE_ANTIALIAS_ON); // 抗锯齿
|
||||
int stroke = 2;
|
||||
for(PlateInfo plateInfo : plateInfoList){
|
||||
DetectionRectangle rectangle = plateInfo.getDetectionRectangle();
|
||||
graphics.setColor(Color.RED);// 边框颜色
|
||||
//绘制车牌框
|
||||
graphics.drawRect(rectangle.getX(), rectangle.getY(), rectangle.getWidth(), rectangle.getHeight());
|
||||
graphics.setColor(Color.BLACK);// 字体颜色
|
||||
ImageUtils.drawText(graphics, plateInfo.getPlateNumber() + " " + plateInfo.getPlateColor(), rectangle.getX(), rectangle.getY(), stroke, 4);
|
||||
OcrBox ocrBox = plateInfo.getBox();
|
||||
//绘制关键点
|
||||
graphics.setColor(Color.BLUE);
|
||||
graphics.drawRect((int)ocrBox.getTopLeft().getX(), (int)ocrBox.getTopLeft().getY(), 2, 2);
|
||||
graphics.setColor(Color.GREEN);
|
||||
graphics.drawRect((int)ocrBox.getTopRight().getX(), (int)ocrBox.getTopRight().getY(), 2, 2);
|
||||
graphics.setColor(Color.RED);
|
||||
graphics.drawRect((int)ocrBox.getBottomLeft().getX(), (int)ocrBox.getBottomLeft().getY(), 2, 2);
|
||||
graphics.setColor(Color.CYAN);
|
||||
graphics.drawRect((int)ocrBox.getBottomRight().getX(), (int)ocrBox.getBottomRight().getY(), 2, 2);
|
||||
}
|
||||
} finally {
|
||||
graphics.dispose();
|
||||
for(PlateInfo plateInfo : plateInfoList){
|
||||
DetectionRectangle rectangle = plateInfo.getDetectionRectangle();
|
||||
OcrBox ocrBox = plateInfo.getBox();
|
||||
String text = plateInfo.getPlateNumber() + " " + plateInfo.getPlateColor();
|
||||
BufferedImageUtils.drawRectAndText(graphics, rectangle, text, Color.BLACK);
|
||||
List<Point> keyPoints = new ArrayList<Point>();
|
||||
keyPoints.add(ocrBox.getTopLeft());
|
||||
keyPoints.add(ocrBox.getTopRight());
|
||||
keyPoints.add(ocrBox.getBottomRight());
|
||||
keyPoints.add(ocrBox.getBottomLeft());
|
||||
BufferedImageUtils.drawKeyPoints(graphics, keyPoints, Color.GREEN);
|
||||
}
|
||||
graphics.dispose();
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user