├── .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 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 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 | 9 | 10 | 14 | 15 | 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 |
  1. 8 | About 9 |
  2. 10 |
  3. 11 | Getting Started 12 |
  4. 13 |
  5. 14 | Result 15 |
  6. 16 |
  7. 17 | Contact 18 |
  8. 19 |
  9. 20 | Reference 21 |
  10. 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 --------------------------------------------------------------------------------