├── .gitignore
├── .idea
├── .gitignore
├── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
├── compiler.xml
├── encodings.xml
├── jarRepositories.xml
└── misc.xml
├── README.md
├── img_result
├── img_result.png
├── ocr-controller.png
└── project_layout.png
├── java-springboot-paddleocr-v2.iml
├── pom.xml
├── requirements.txt
└── src
└── main
├── java
└── com
│ ├── sy
│ ├── OcrServiceApplication.java
│ ├── common
│ │ ├── ImageUtils.java
│ │ └── ModelUrlUtils.java
│ └── ocr
│ │ ├── OcrController.java
│ │ ├── OcrProperties.java
│ │ └── OcrService.java
│ └── utils
│ ├── CollectionUtil.java
│ ├── FileFormatUtil.java
│ ├── FileUploadConfig.java
│ └── toFile.java
└── resources
└── application.properties
/.gitignore:
--------------------------------------------------------------------------------
1 | # Project exclude paths
2 | /target/
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Datasource local storage ignored files
5 | /dataSources/
6 | /dataSources.local.xml
7 | # Zeppelin ignored files
8 | /ZeppelinRemoteNotebooks/
9 | # Editor-based HTTP Client requests
10 | /httpRequests/
11 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/jarRepositories.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
java-springboot-paddleocr-v2
2 |
3 |
4 |
5 | Table of Contents
6 |
7 | -
8 | About
9 |
10 | -
11 | Getting Started
12 |
13 | -
14 | Result
15 |
16 | -
17 | Contact
18 |
19 | -
20 | Reference
21 |
22 |
23 |
24 |
25 |
26 | #### About
27 | 【( 新的java ocr项目,完全用java源码实现ocr功能,无需调用dll或者exe文件 ----> https://github.com/jiangnanboy/JiaJiaOCR )】
28 |
29 |
30 | 不同于[java-springboot-paddleocr](https://github.com/jiangnanboy/java-springboot-paddleocr) ,本项目利用JNI加载paddle-ocr的C++编译后的dll库,并利用springboot进行web部署访问,效果等同于java-springboot-paddleocr。
31 |
32 | #### Getting started
33 | paddle-ocr c++编译的dll库以及模型(det_db:文本检测;cls:文本方向;rec_crnn:文本识别),下载后放入项目的相应位置如下图:
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | 链接: https://pan.baidu.com/s/1_I3074n7WIv7Pyc_EoaBQA 提取码: fjh3
44 |
45 | 项目启动后访问 http://localhost:8080/swagger-ui/ 测试
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | 其中:
56 |
57 | 1./ocr:返回json结果(包括识别的文字,坐标以及置信度)
58 |
59 | 2./ocr/image:返回识别后绘制的图片结果
60 | #### Result
61 | ##### /ocr,返回json结果
62 |
63 | [{"box":[[101,458],[831,443],[831,462],[101,477]],"clsLabel":0,"clsScore":0.9893779,"score":0.9142323,"text":"登机起飞前O分钟关闭GATESCLOSE1OMINUTESBEFOREDEPARTURETIME"},
64 | {"box":[[347,350],[662,346],[662,365],[347,369]],"clsLabel":0,"clsScore":0.99999976,"score":0.9292435,"text":"ETKT7813699238489/1"},
65 | {"box":[[70,344],[164,341],[165,362],[71,364]],"clsLabel":0,"clsScore":1.0,"score":0.99424833,"text":"票价FARE"},
66 | {"box":[[103,313],[209,310],[210,333],[104,336]],"clsLabel":0,"clsScore":1.0,"score":0.9641626,"text":"张祺伟"},
67 | {"box":[[462,297],[578,295],[578,315],[462,318]],"clsLabel":0,"clsScore":0.9999988,"score":0.94890064,"text":"票号TKTNO"},
68 | {"box":[[77,278],[263,274],[263,294],[77,297]],"clsLabel":0,"clsScore":1.0,"score":0.94635695,"text":"ZHANGQIWEI"},
69 | {"box":[[68,251],[172,249],[172,268],[68,270]],"clsLabel":0,"clsScore":1.0,"score":0.9733718,"text":"姓名NAME"},
70 | {"box":[[346,239],[483,235],[483,255],[346,259]],"clsLabel":0,"clsScore":1.0,"score":0.91015244,"text":"身份识别DNO"},
71 | {"box":[[91,230],[202,228],[202,248],[91,250]],"clsLabel":0,"clsScore":0.999033,"score":0.82490426,"text":"FUZHOU"},
72 | {"box":[[338,219],[475,216],[475,236],[338,239]],"clsLabel":0,"clsScore":1.0,"score":0.8636264,"text":"TAIYUAN"},
73 | {"box":[[506,214],[553,214],[553,235],[506,235]],"clsLabel":0,"clsScore":0.5980619,"score":0.6953351,"text":"G11"},
74 | {"box":[[98,206],[166,204],[167,228],[99,230]],"clsLabel":0,"clsScore":0.9999995,"score":0.9914566,"text":"福州"},
75 | {"box":[[68,181],[169,178],[169,198],[68,201]],"clsLabel":0,"clsScore":0.9999999,"score":0.9419906,"text":"的地TO"},
76 | {"box":[[344,175],[469,171],[469,193],[345,197]],"clsLabel":0,"clsScore":1.0,"score":0.99677646,"text":"始发地FROM"},
77 | {"box":[[490,174],[555,172],[556,193],[491,195]],"clsLabel":0,"clsScore":0.99999833,"score":0.92120004,"text":"登机"},
78 | {"box":[[568,173],[613,173],[613,192],[568,192]],"clsLabel":0,"clsScore":1.0,"score":0.99297625,"text":"GATE"},
79 | {"box":[[679,169],[810,166],[810,187],[679,190]],"clsLabel":0,"clsScore":1.0,"score":0.9191831,"text":"登机时间BDT"},
80 | {"box":[[83,141],[212,137],[212,158],[83,162]],"clsLabel":0,"clsScore":0.99999845,"score":0.8854566,"text":"MU2379"},
81 | {"box":[[234,138],[326,136],[326,157],[234,159]],"clsLabel":0,"clsScore":0.99988997,"score":0.8469051,"text":"O3DEC"},
82 | {"box":[[406,133],[430,133],[430,157],[406,157]],"clsLabel":0,"clsScore":0.8780533,"score":0.7521334,"text":"W"},
83 | {"box":[[726,132],[773,132],[773,154],[726,154]],"clsLabel":0,"clsScore":0.8853147,"score":0.0,"text":""},
84 | {"box":[[511,131],[569,131],[569,155],[511,155]],"clsLabel":0,"clsScore":0.9968528,"score":0.97677803,"text":"035"},
85 | {"box":[[66,112],[193,108],[193,128],[66,132]],"clsLabel":0,"clsScore":0.99999833,"score":0.90373456,"text":"航班FLIGHT"},
86 | {"box":[[214,108],[318,106],[318,126],[214,128]],"clsLabel":0,"clsScore":1.0,"score":0.8624909,"text":"日期DATE"},
87 | {"box":[[341,105],[405,105],[405,126],[341,126]],"clsLabel":0,"clsScore":0.99999976,"score":0.9896834,"text":"舱位"},
88 | {"box":[[397,104],[457,104],[457,125],[397,125]],"clsLabel":0,"clsScore":0.99993443,"score":0.95271266,"text":"CLASS"},
89 | {"box":[[645,105],[663,105],[663,116],[645,116]],"clsLabel":0,"clsScore":0.546068,"score":0.0,"text":""},
90 | {"box":[[490,103],[652,100],[652,120],[490,123]],"clsLabel":0,"clsScore":1.0,"score":0.8552721,"text":"序号SERIALNO."},
91 | {"box":[[676,99],[832,96],[832,115],[676,119]],"clsLabel":0,"clsScore":1.0,"score":0.9639348,"text":"座位号SEATNO"},
92 | {"box":[[159,29],[357,26],[357,66],[159,68]],"clsLabel":0,"clsScore":1.0,"score":0.92540216,"text":"登机牌"},
93 | {"box":[[420,23],[819,15],[820,54],[421,62]],"clsLabel":0,"clsScore":1.0,"score":0.93226165,"text":"BOARDINGPASS"},
94 | {"box":[[136,13],[174,13],[174,27],[136,27]],"clsLabel":0,"clsScore":0.5823542,"score":0.0,"text":""},
95 | {"box":[[6,13],[47,13],[47,27],[6,27]],"clsLabel":0,"clsScore":0.5218532,"score":0.0,"text":""},
96 | {"box":[[56,9],[125,9],[125,23],[56,23]],"clsLabel":0,"clsScore":0.57580894,"score":0.0,"text":""},
97 | {"box":[[192,8],[244,8],[244,19],[192,19]],"clsLabel":0,"clsScore":0.69533056,"score":0.0,"text":""}]
98 |
99 | ##### /ocr/image,返回图片结果
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | #### Contact
110 | 如有问题,联系我:
111 |
112 | 1、github:https://github.com/jiangnanboy
113 |
114 | 2、QQ:2229029156
115 |
116 | #### Reference
117 | https://github.com/PaddlePaddle/PaddleOCR
118 |
119 | https://github.com/jiangnanboy/doc_ai
120 |
121 | https://github.com/deepjavalibrary/djl
122 |
123 | https://github.com/jiangnanboy/java-springboot-paddleocr
124 |
125 | https://github.com/greycode/ocrlite
126 |
127 |
--------------------------------------------------------------------------------
/img_result/img_result.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiangnanboy/java-springboot-paddleocr-v2/7cc20650c88f6444c89e04aa1efa5ad392891d1d/img_result/img_result.png
--------------------------------------------------------------------------------
/img_result/ocr-controller.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiangnanboy/java-springboot-paddleocr-v2/7cc20650c88f6444c89e04aa1efa5ad392891d1d/img_result/ocr-controller.png
--------------------------------------------------------------------------------
/img_result/project_layout.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiangnanboy/java-springboot-paddleocr-v2/7cc20650c88f6444c89e04aa1efa5ad392891d1d/img_result/project_layout.png
--------------------------------------------------------------------------------
/java-springboot-paddleocr-v2.iml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | org.example
8 | java-springboot-paddleocr-v2
9 | 1.0-SNAPSHOT
10 |
11 |
12 | org.springframework.boot
13 | spring-boot-starter-parent
14 | 2.6.4
15 |
16 |
17 |
18 |
19 | 11
20 | 1.10.0
21 | 1.9.1
22 |
23 |
24 |
25 |
26 |
27 | ai.djl
28 | bom
29 | 0.15.0
30 | pom
31 | import
32 |
33 |
34 |
35 |
36 |
37 |
38 | org.springframework.boot
39 | spring-boot-starter-web
40 |
41 |
42 | org.springframework.boot
43 | spring-boot-configuration-processor
44 | true
45 |
46 |
47 | org.springframework.boot
48 | spring-boot-starter-test
49 |
50 |
51 |
52 | org.projectlombok
53 | lombok
54 | true
55 |
56 |
57 |
58 | io.springfox
59 | springfox-boot-starter
60 | 3.0.0
61 |
62 |
63 |
64 | ai.djl
65 | api
66 |
67 |
68 | ai.djl.paddlepaddle
69 | paddlepaddle-model-zoo
70 |
71 |
72 | ai.djl.pytorch
73 | pytorch-model-zoo
74 |
75 |
76 | ai.djl.pytorch
77 | pytorch-engine
78 | runtime
79 |
80 |
81 | ai.djl.pytorch
82 | pytorch-native-auto
83 | ${pytorch.version}
84 |
85 |
86 |
87 | ai.djl.onnxruntime
88 | onnxruntime-engine
89 |
90 |
91 | com.microsoft.onnxruntime
92 | onnxruntime
93 |
94 |
95 |
96 |
97 |
98 | com.microsoft.onnxruntime
99 | onnxruntime
100 | ${onnx.version}
101 | runtime
102 |
103 |
104 |
105 | org.apache.commons
106 | commons-lang3
107 | 3.7
108 |
109 |
110 |
111 | org.apache.poi
112 | poi
113 | 4.0.0
114 |
115 |
116 |
117 | dom4j
118 | dom4j
119 | 1.6.1
120 |
121 |
122 |
123 | commons-collections
124 | commons-collections
125 | 3.2.2
126 |
127 |
128 |
129 | io.github.greycode
130 | ocrlite
131 | 3.0.0
132 |
133 |
134 |
135 | com.alibaba.fastjson2
136 | fastjson2
137 | 2.0.20
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 | org.springframework.boot
148 | spring-boot-maven-plugin
149 |
150 |
151 |
152 | org.projectlombok
153 | lombok
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | java11
2 | djl
3 | springboot
4 | paddle-ocr c++
--------------------------------------------------------------------------------
/src/main/java/com/sy/OcrServiceApplication.java:
--------------------------------------------------------------------------------
1 | package com.sy;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | /**
7 | * @author sy
8 | * @date 2022/11/13 22:15
9 | */
10 | @SpringBootApplication
11 | public class OcrServiceApplication {
12 | public static void main(String[] args) {
13 | SpringApplication.run(OcrServiceApplication.class, args);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/com/sy/common/ImageUtils.java:
--------------------------------------------------------------------------------
1 | package com.sy.common;
2 |
3 | import ai.djl.modality.cv.ImageFactory;
4 | import ai.djl.modality.cv.output.BoundingBox;
5 | import ai.djl.modality.cv.output.DetectedObjects;
6 | import ai.djl.modality.cv.util.NDImageUtils;
7 | import ai.djl.ndarray.NDArray;
8 | import ai.djl.ndarray.NDManager;
9 | import ai.djl.ndarray.types.DataType;
10 | import com.alibaba.fastjson2.JSON;
11 | import io.github.greycode.OCRPredictResult;
12 | import lombok.experimental.UtilityClass;
13 |
14 | import java.awt.*;
15 | import java.awt.image.BufferedImage;
16 | import java.io.IOException;
17 | import java.nio.file.Paths;
18 | import java.util.List;
19 | import java.util.Map;
20 |
21 | import static java.awt.Image.SCALE_DEFAULT;
22 |
23 | /**
24 | * @author sy
25 | * @date 2022/9/13 21:24
26 | */
27 | @UtilityClass
28 | public class ImageUtils {
29 |
30 | private static final Map colorMap = Map.of(
31 | 0, new Color(200, 0, 0),
32 | 1, new Color(0, 200, 0),
33 | 2, new Color(0, 0, 200),
34 | 3, new Color(200, 200, 0),
35 | 4, new Color(200, 0, 200),
36 | 5, new Color(0, 200, 200)
37 | );
38 |
39 | private static void drawText(Graphics2D g, String className, double probability, int x, int y, int width) {
40 | //设置水印的坐标
41 | String showText = String.format("%s %.0f%%", className, probability * 100);
42 | g.fillRect(x, y - 30, width, 30);
43 |
44 | g.setColor(Color.WHITE);
45 | g.setFont(new Font("Monospaced", Font.BOLD, 25));//设置字体
46 | g.drawString(showText, x, y - 10);
47 | }
48 |
49 | /**
50 | * 对ocr的结果在图片中绘制
51 | * @param image
52 | * @param ocrEntries
53 | * @return
54 | */
55 | public static BufferedImage drawDetectionResults(BufferedImage image, List ocrEntries) {
56 | Graphics2D g = (Graphics2D) image.getGraphics();
57 | int stroke = 2;
58 | g.setStroke(new BasicStroke(stroke));
59 | g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
60 | for(OCRPredictResult ocrEntry:ocrEntries) {
61 | String className = ocrEntry.getText();
62 | double probability = ocrEntry.getScore();
63 | List> box = ocrEntry.getBox();
64 | box = (List>) JSON.parse(JSON.toJSONString(box));
65 | Color color = colorMap.get(Math.abs(className.hashCode() % 6));
66 | g.setPaint(color);
67 | Integer x = box.get(0).get(0);
68 | Integer y = box.get(0).get(1);
69 | int width = box.get(1).get(0) - x;
70 | int height = box.get(3).get(1) - y;
71 | g.drawRect(x, y, width, height);
72 | drawText(g, className, probability, x, y, width);
73 | }
74 | g.dispose();
75 | return image;
76 | }
77 |
78 | }
79 |
80 |
--------------------------------------------------------------------------------
/src/main/java/com/sy/common/ModelUrlUtils.java:
--------------------------------------------------------------------------------
1 | package com.sy.common;
2 |
3 | import lombok.SneakyThrows;
4 | import lombok.experimental.UtilityClass;
5 | import lombok.extern.slf4j.Slf4j;
6 | import org.springframework.core.io.ClassPathResource;
7 |
8 | import java.io.IOException;
9 | import java.net.URI;
10 |
11 | /**
12 | * @author sy
13 | * @date 2022/9/15 22:42
14 | */
15 | @UtilityClass
16 | @Slf4j
17 | public class ModelUrlUtils {
18 |
19 | /**
20 | * 获取模型url,如果是http或file开头,直接返回
21 | * @param name 模型名称
22 | * @return url
23 | */
24 | @SneakyThrows
25 | public static String getRealUrl(String name) {
26 | if (name.startsWith("http") || name.startsWith("file:")) {
27 | System.out.println("model url is : " + name);
28 | return name;
29 | }
30 |
31 | URI uri = null;
32 | try {
33 | uri = new ClassPathResource(name).getURI();
34 | } catch (IOException e) {
35 | e.printStackTrace();
36 | }
37 |
38 | System.out.println("model uri of " + name + " is " + uri);
39 | if (uri.toString().startsWith("jar:")) {
40 | return "jar://" + name;
41 | }
42 |
43 | return uri.toString();
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/java/com/sy/ocr/OcrController.java:
--------------------------------------------------------------------------------
1 | package com.sy.ocr;
2 |
3 | import ai.djl.modality.cv.Image;
4 | import ai.djl.modality.cv.ImageFactory;
5 | import com.alibaba.fastjson2.JSON;
6 | import io.github.greycode.OCRPredictResult;
7 | import io.swagger.annotations.ApiOperation;
8 | import lombok.RequiredArgsConstructor;
9 | import lombok.extern.slf4j.Slf4j;
10 | import org.springframework.web.bind.annotation.PostMapping;
11 | import org.springframework.web.bind.annotation.RequestMapping;
12 | import org.springframework.web.bind.annotation.RequestPart;
13 | import org.springframework.web.bind.annotation.RestController;
14 | import org.springframework.web.multipart.MultipartFile;
15 |
16 | import javax.imageio.ImageIO;
17 | import javax.servlet.ServletOutputStream;
18 | import javax.servlet.http.HttpServletResponse;
19 | import java.awt.image.BufferedImage;
20 | import java.io.File;
21 | import java.io.IOException;
22 | import java.util.List;
23 | import java.util.Optional;
24 |
25 | import static com.utils.toFile.deleteTempFile;
26 | import static com.utils.toFile.multipartFiletoFile;
27 |
28 |
29 | /**
30 | * @author sy
31 | * @date 2022/10/13 22:25
32 | */
33 | @RestController
34 | @RequestMapping("ocr")
35 | @RequiredArgsConstructor
36 | @Slf4j
37 | public class OcrController {
38 |
39 | private final OcrService ocrService;
40 |
41 | /**
42 | * 返回文字识别后的图片
43 | *
44 | * @param multipartFile
45 | * @param response
46 | */
47 | @PostMapping("image")
48 | @ApiOperation("文字识别,返回图片结果")
49 | public void ocrImage(@RequestPart MultipartFile multipartFile, HttpServletResponse response) {
50 | File tFile = null;
51 | try {
52 | tFile = multipartFiletoFile(multipartFile);
53 | List ocrEntries = ocrService.ocr(tFile.toURI().getPath().replaceFirst("/", ""));
54 | Image img = ImageFactory.getInstance().fromInputStream(multipartFile.getInputStream());
55 | BufferedImage resultImage = ocrService.createResultImage(img, ocrEntries);
56 | response.setContentType("image/png");
57 | ServletOutputStream os = response.getOutputStream();
58 | ImageIO.write(resultImage, "PNG", os);
59 | os.flush();
60 | } catch (IOException e) {
61 | e.printStackTrace();
62 | } finally {
63 | if(Optional.ofNullable(tFile).isPresent()) {
64 | deleteTempFile(tFile);
65 | }
66 | }
67 | }
68 |
69 | /**
70 | * @param multipartFile
71 | * @return
72 | */
73 | @PostMapping
74 | @ApiOperation("文字识别,返回JSON结果")
75 | public String ocr(@RequestPart MultipartFile multipartFile) {
76 | File tFile = null;
77 | List ocrEntries = null;
78 | try {
79 | tFile = multipartFiletoFile(multipartFile);
80 | ocrEntries = ocrService.ocr(tFile.toURI().getPath().replaceFirst("/", ""));
81 | } catch (IOException e) {
82 | e.printStackTrace();
83 | } finally {
84 | if(Optional.ofNullable(tFile).isPresent()) {
85 | deleteTempFile(tFile);
86 | }
87 | }
88 | return JSON.toJSONString(ocrEntries);
89 | }
90 |
91 | }
92 |
93 |
--------------------------------------------------------------------------------
/src/main/java/com/sy/ocr/OcrProperties.java:
--------------------------------------------------------------------------------
1 | package com.sy.ocr;
2 |
3 | import lombok.Data;
4 | import org.springframework.context.annotation.Configuration;
5 |
6 | /**
7 | * @author sy
8 | * @date 2022/10/24 23:10
9 | */
10 | @Data
11 | @Configuration
12 | public class OcrProperties {
13 | private String ppocrKeys = "ppocr_keys_v1.txt";
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/com/sy/ocr/OcrService.java:
--------------------------------------------------------------------------------
1 | package com.sy.ocr;
2 |
3 | import ai.djl.modality.cv.Image;
4 | import com.sy.common.ImageUtils;
5 | import com.sy.common.ModelUrlUtils;
6 | import io.github.greycode.OCRPredictResult;
7 | import io.github.greycode.OcrDriver;
8 | import io.github.greycode.PaddleOcr;
9 | import lombok.RequiredArgsConstructor;
10 | import lombok.extern.slf4j.Slf4j;
11 | import org.springframework.stereotype.Service;
12 |
13 | import javax.annotation.PostConstruct;
14 | import javax.annotation.PreDestroy;
15 | import java.awt.image.BufferedImage;
16 | import java.io.File;
17 | import java.io.IOException;
18 | import java.net.URL;
19 | import java.util.HashMap;
20 | import java.util.List;
21 | import java.util.Map;
22 | import java.util.Optional;
23 |
24 | /**
25 | * @author sy
26 | * @date 2022/11/13 22:02
27 | */
28 | @Service
29 | @Slf4j
30 | @RequiredArgsConstructor
31 | public class OcrService {
32 | private final OcrProperties prop;
33 |
34 | /**
35 | * 初始化加载ocr模型
36 | */
37 | @PostConstruct
38 | private void init() throws IOException {
39 | System.out.println("加载Ocr执行文件...");
40 | loadOcrDriver();
41 | }
42 |
43 | /**
44 | * ocr
45 | * @param imgFile
46 | * @return
47 | */
48 | public List ocr(String imgFile) {
49 | List> OCRPredictResultList = OcrDriver.ocr(imgFile);
50 | return OCRPredictResultList.get(0);
51 | }
52 |
53 | /**
54 | * 结果图片
55 | * @param image 图片
56 | * @return image
57 | */
58 | public BufferedImage createResultImage(Image image, List ocrEntries) {
59 | return ImageUtils.drawDetectionResults((BufferedImage) image.getWrappedImage(), ocrEntries);
60 | }
61 |
62 | /**
63 | * 加载模型
64 | */
65 | public void loadOcrDriver() throws IOException {
66 | String jniLib = "PaddleOcrJni.dll";
67 | URL url = OcrService.class.getClassLoader().getResource(jniLib);
68 | System.setProperty("java.library.path", url.getPath().replace(jniLib, ""));
69 | PaddleOcr paddleOcr = new PaddleOcr();
70 | paddleOcr.setUseMkldnn(true);
71 | paddleOcr.setLabelPath(ModelUrlUtils.getRealUrl(prop.getPpocrKeys()).replaceFirst("file:/", ""));
72 | paddleOcr.initAll();
73 | OcrDriver.initializeDefaultModel(paddleOcr);
74 | }
75 |
76 | }
77 |
--------------------------------------------------------------------------------
/src/main/java/com/utils/CollectionUtil.java:
--------------------------------------------------------------------------------
1 | package com.utils;
2 |
3 | import java.util.*;
4 |
5 | /**
6 | * @author sy
7 | * @date 2022/11/13 22:02
8 | */
9 | public class CollectionUtil {
10 |
11 | public static List newArrayList() {
12 | return new ArrayList<>();
13 | }
14 |
15 | public static LinkedList newLinkedList() {
16 | return new LinkedList<>();
17 | }
18 |
19 | public static List newArrayList(int N) {
20 | return new ArrayList<>(N);
21 | }
22 |
23 | public static List newArrayList(Set entry) {
24 | return new ArrayList<>(entry);
25 | }
26 |
27 | public static Set newHashset() {
28 | return new HashSet<>();
29 | }
30 |
31 | public static Set newHashset(List entry) {
32 | return new HashSet<>(entry);
33 | }
34 |
35 | public static Map newHashMap() {
36 | return new HashMap<>();
37 | }
38 |
39 | public static LinkedHashMap newLinkedHashMap() {
40 | return new LinkedHashMap<>();
41 | }
42 |
43 | public static Map newTreeMap() {
44 | return new TreeMap<>();
45 | }
46 |
47 | public static List newArrayList(List asList) {
48 | return asList;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/java/com/utils/FileFormatUtil.java:
--------------------------------------------------------------------------------
1 | package com.utils;
2 |
3 | import java.util.Arrays;
4 | import java.util.List;
5 |
6 | /**
7 | * @author sy
8 | * @date 2022/11/11 21:35
9 | */
10 | public class FileFormatUtil {
11 | static List supportFileFormats = CollectionUtil.newArrayList(Arrays.asList("jpeg,jpg,png".split(",")));
12 | public static boolean checkFormats(String fileFullName) {
13 | String suffix = fileFullName.substring(fileFullName.lastIndexOf(".") + 1).toLowerCase();
14 | return supportFileFormats.stream().anyMatch(suffix::contains);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/com/utils/FileUploadConfig.java:
--------------------------------------------------------------------------------
1 | package com.utils;
2 |
3 | import org.springframework.boot.web.servlet.MultipartConfigFactory;
4 | import org.springframework.context.annotation.Bean;
5 | import org.springframework.context.annotation.Configuration;
6 | import org.springframework.util.unit.DataSize;
7 |
8 | import javax.servlet.MultipartConfigElement;
9 |
10 | /**
11 | * @author sy
12 | * @date 2022/11/11 22:57
13 | */
14 | @Configuration
15 | public class FileUploadConfig {
16 | @Bean
17 | public MultipartConfigElement multipartConfigElement() {
18 | MultipartConfigFactory multipartConfigFactory = new MultipartConfigFactory();
19 | // 单个文件大小为5MB
20 | multipartConfigFactory.setMaxFileSize(DataSize.ofMegabytes(5));
21 | // 总上传数据大小10MB
22 | multipartConfigFactory.setMaxRequestSize(DataSize.ofMegabytes(10));
23 | return multipartConfigFactory.createMultipartConfig();
24 | }
25 | }
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/src/main/java/com/utils/toFile.java:
--------------------------------------------------------------------------------
1 | package com.utils;
2 |
3 | import org.springframework.web.multipart.MultipartFile;
4 |
5 | import java.io.*;
6 | import java.util.Optional;
7 |
8 | /**
9 | * @author sy
10 | * @date 2022/11/23 22:03
11 | */
12 | public class toFile {
13 |
14 | /**
15 | * MultipartFile转File
16 | * @param file
17 | * @return
18 | * @throws IOException
19 | */
20 | public static File multipartFiletoFile(MultipartFile file) throws IOException {
21 | File toFile = null;
22 | if((!file.equals("")) && (file.getSize() > 0)) {
23 | String filePath = "/tmp/img";
24 | if(!new File(filePath).exists()) {
25 | new File(filePath).mkdirs();
26 | }
27 | InputStream inputStream = file.getInputStream();
28 | String fileFullName = file.getOriginalFilename();
29 | String fileName = fileFullName.substring(0, fileFullName.lastIndexOf("."));
30 | String prefix = fileFullName.substring(fileFullName.lastIndexOf("."));
31 | toFile = new File(filePath + fileName + "_" + System.currentTimeMillis() + prefix);
32 | intputStreamToFile(inputStream, toFile);
33 | inputStream.close();
34 | }
35 | return toFile;
36 | }
37 |
38 | /**
39 | * 获取文件流
40 | * @param inputStream
41 | * @param file
42 | */
43 | private static void intputStreamToFile(InputStream inputStream, File file) {
44 | try (OutputStream outputStream = new FileOutputStream(file)) {
45 | int bytesRead = 0;
46 | byte[] buffer = new byte[8192];
47 | while((bytesRead = inputStream.read(buffer, 0, 8192)) != -1) {
48 | outputStream.write(buffer, 0, bytesRead);
49 | }
50 | } catch (Exception e) {
51 | e.printStackTrace();
52 | }
53 | }
54 |
55 | /**
56 | * 删除临时文件
57 | * @param file
58 | */
59 | public static void deleteTempFile(File file) {
60 | if(Optional.ofNullable(file).isPresent()) {
61 | File del = new File(file.toURI());
62 | del.delete();
63 | }
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | spring.mvc.pathmatch.matching-strategy=ant_path_matcher
2 | spring.servlet.multipart.max-file-size=10MB
--------------------------------------------------------------------------------