diff --git "a/docs/cn/1 \345\277\253\351\200\237\344\270\212\346\211\213/1.0 \345\277\253\351\200\237\344\270\212\346\211\213.md" "b/docs/cn/1 \345\277\253\351\200\237\344\270\212\346\211\213/1.0 \345\277\253\351\200\237\344\270\212\346\211\213.md"
index 7fb5562..8d9a077 100644
--- "a/docs/cn/1 \345\277\253\351\200\237\344\270\212\346\211\213/1.0 \345\277\253\351\200\237\344\270\212\346\211\213.md"
+++ "b/docs/cn/1 \345\277\253\351\200\237\344\270\212\346\211\213/1.0 \345\277\253\351\200\237\344\270\212\346\211\213.md"
@@ -92,7 +92,7 @@ mvn clean package -DskipTests
以下是一个基本的 `jpackage` 命令示例,用于将 JavaFX JAR 打包为 `.exe` 文件:
```bash
-jpackage --type exe --input target/ --main-jar tool-ocr-1.2.6.jar --name tree-hole-ocr --main-class com.luooqi.ocr.MainFm
+jpackage --type exe --input target/ --main-jar tool-ocr-1.2.6.jar --name tree-hole-ocr --main-class com.luooqi.ocr.OcrApp
```
其中:
diff --git a/pom.xml b/pom.xml
index ba47252..ce322a4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,14 +6,14 @@
com.luooqi
tools-ocr
- 2.2.8
+ 2.2.9
UTF-8
1.8
${java.version}
${java.version}
- com.luooqi.ocr.MainFm
+ com.luooqi.ocr.OcrApp
1.18.20
1.2.3
0.25.0
@@ -39,6 +39,12 @@
4.2
+
+ org.apache.pdfbox
+ pdfbox
+ 2.0.24
+
+
org.projectlombok
lombok
@@ -96,21 +102,21 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/readme.md b/readme.md
index d092be0..2c15e9d 100644
--- a/readme.md
+++ b/readme.md
@@ -8,6 +8,9 @@
- onnx
- paddle ocr
- opencv
+
+## 开源地址
+[gitee](https://gitee.com/ppnt/tools-ocr) | [github](https://github.com/litongjava/tools-ocr)
## 安装
> - **安装路径请勿包含中文字符**;
> - 本程序使用 JavaFX 开发,提供的安装包中已经包含了Java
@@ -49,6 +52,7 @@ mvn jfx:native -DskipTests -f pom.xml
![2](readme_files/2.jpg)
## TODO
+- [x] PDF识别
- [x] 图片文字识别
- [x] 识别结果文本对齐(暂未实现多分栏)
- [x] 全屏模式下截图
diff --git a/src/main/java/com/litongjava/djl/paddle/ocr/v4/detection/OCRDetectionTranslator.java b/src/main/java/com/litongjava/djl/paddle/ocr/v4/detection/OCRDetectionTranslator.java
index bb0c944..ee59fdb 100644
--- a/src/main/java/com/litongjava/djl/paddle/ocr/v4/detection/OCRDetectionTranslator.java
+++ b/src/main/java/com/litongjava/djl/paddle/ocr/v4/detection/OCRDetectionTranslator.java
@@ -104,18 +104,20 @@ public NDList processOutput(TranslatorContext ctx, NDList list) {
srcMat.release();
}
+ NDList dt_boxes = null;
NDArray boxes = boxes_from_bitmap(manager, pred, newMask);
+ if (boxes != null) {
+ //boxes[:, :, 0] = boxes[:, :, 0] / ratio_w
+ NDArray boxes1 = boxes.get(":, :, 0").div(ratio_w);
+ boxes.set(new NDIndex(":, :, 0"), boxes1);
+ //boxes[:, :, 1] = boxes[:, :, 1] / ratio_h
+ NDArray boxes2 = boxes.get(":, :, 1").div(ratio_h);
+ boxes.set(new NDIndex(":, :, 1"), boxes2);
- //boxes[:, :, 0] = boxes[:, :, 0] / ratio_w
- NDArray boxes1 = boxes.get(":, :, 0").div(ratio_w);
- boxes.set(new NDIndex(":, :, 0"), boxes1);
- //boxes[:, :, 1] = boxes[:, :, 1] / ratio_h
- NDArray boxes2 = boxes.get(":, :, 1").div(ratio_h);
- boxes.set(new NDIndex(":, :, 1"), boxes2);
+ dt_boxes = this.filter_tag_det_res(boxes);
- NDList dt_boxes = this.filter_tag_det_res(boxes);
-
- dt_boxes.detach();
+ dt_boxes.detach();
+ }
// release Mat
newMask.release();
@@ -255,12 +257,18 @@ private NDArray boxes_from_bitmap(NDManager manager, NDArray pred, Mat bitmap) {
newContour.release();
}
- NDArray boxes = NDArrays.stack(boxList);
-
// release
hierarchy.release();
+ NDArray boxes = null;
+ if (boxList.size() > 0) {
+ boxes = NDArrays.stack(boxList);
+ return boxes;
+ }
+
return boxes;
+
+
}
/**
diff --git a/src/main/java/com/litongjava/djl/paddle/ocr/v4/recognition/OcrV4Recognition.java b/src/main/java/com/litongjava/djl/paddle/ocr/v4/recognition/OcrV4Recognition.java
index 9ad7135..9ce8df4 100644
--- a/src/main/java/com/litongjava/djl/paddle/ocr/v4/recognition/OcrV4Recognition.java
+++ b/src/main/java/com/litongjava/djl/paddle/ocr/v4/recognition/OcrV4Recognition.java
@@ -64,6 +64,9 @@ public List predict(NDManager manager,
Image image, Predictor detector, Predictor recognizer)
throws TranslateException {
NDList boxes = detector.predict(image);
+ if (boxes == null) {
+ return null;
+ }
// 交给 NDManager自动管理内存
// attach to manager for automatic memory management
boxes.attach(manager);
diff --git a/src/main/java/com/luooqi/ocr/OcrApp.java b/src/main/java/com/luooqi/ocr/OcrApp.java
new file mode 100644
index 0000000..7a82e10
--- /dev/null
+++ b/src/main/java/com/luooqi/ocr/OcrApp.java
@@ -0,0 +1,40 @@
+package com.luooqi.ocr;
+
+import cn.hutool.core.thread.GlobalThreadPool;
+import com.luooqi.ocr.config.InitConfig;
+import com.luooqi.ocr.local.PaddlePaddleOCRV4;
+import com.luooqi.ocr.windows.MainForm;
+import javafx.application.Application;
+import javafx.stage.Stage;
+import lombok.extern.slf4j.Slf4j;
+import org.jnativehook.GlobalScreen;
+
+@Slf4j
+public class OcrApp extends Application {
+
+ public static void main(String[] args) {
+ launch(args);
+ }
+
+ @Override
+ public void init() throws Exception {
+ super.init();
+ InitConfig.init();
+ }
+
+
+ @Override
+ public void start(Stage primaryStage) {
+ MainForm mainForm = new MainForm();
+ mainForm.init(primaryStage);
+ primaryStage.show();
+ }
+
+ @Override
+ public void stop() throws Exception {
+ log.info("close");
+ GlobalScreen.unregisterNativeHook();
+ PaddlePaddleOCRV4.INSTANCE.close();
+ GlobalThreadPool.shutdown(true);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/luooqi/ocr/constants/ImagesConstants.java b/src/main/java/com/luooqi/ocr/constants/ImagesConstants.java
new file mode 100644
index 0000000..4d15e88
--- /dev/null
+++ b/src/main/java/com/luooqi/ocr/constants/ImagesConstants.java
@@ -0,0 +1,8 @@
+package com.luooqi.ocr.constants;
+
+/**
+ * Created by litonglinux@qq.com on 12/9/2023_7:14 PM
+ */
+public class ImagesConstants {
+ public static final String LOGO = "img/logo.png";
+}
diff --git a/src/main/java/com/luooqi/ocr/local/LocalOCR.java b/src/main/java/com/luooqi/ocr/local/LocalOCR.java
deleted file mode 100644
index f7690c5..0000000
--- a/src/main/java/com/luooqi/ocr/local/LocalOCR.java
+++ /dev/null
@@ -1,49 +0,0 @@
-//package com.luooqi.ocr.local;
-//
-//import cn.hutool.log.StaticLog;
-//import com.benjaminwan.ocrlibrary.OcrEngine;
-//import com.litongjava.jfinal.aop.Aop;
-//import com.litongjava.project.config.ConfigKeys;
-//import com.litongjava.project.config.ProjectConfig;
-//import com.luooqi.ocr.utils.LibraryUtils;
-//
-//public enum LocalOCR {
-// INSTANCE;
-//
-// private final OcrEngine ocrEngine;
-//
-// LocalOCR() {
-// ProjectConfig projectConfig = Aop.get(ProjectConfig.class);
-// String libPath = projectConfig.getStr(ConfigKeys.libPath);
-//
-// String modelsDir = projectConfig.getStr(ConfigKeys.modelsDir);
-// String detName = projectConfig.getStr(ConfigKeys.detName);
-// String clsName = projectConfig.getStr(ConfigKeys.clsName);
-// String recName = projectConfig.getStr(ConfigKeys.recName);
-// String keysName = projectConfig.getStr(ConfigKeys.keysName);
-//
-// LibraryUtils.addLibary(libPath);
-//
-// this.ocrEngine = new OcrEngine();
-// StaticLog.info("version=" + ocrEngine.getVersion());
-// ocrEngine.setNumThread(4);
-// //------- init Logger -------
-// ocrEngine.initLogger(true, false, false);
-// //ocrEngine.enableResultText("");
-// ocrEngine.setGpuIndex(-1);
-// boolean initModelsRet = ocrEngine.initModels(modelsDir, detName, clsName, recName, keysName);
-// if (!initModelsRet) {
-// StaticLog.error("Error in models initialization, please check the models/keys path!");
-// return;
-// }
-// StaticLog.info("padding(%d) boxScoreThresh(%f) boxThresh(%f) unClipRatio(%f) doAngle(%b) mostAngle(%b)", ocrEngine.getPadding(), ocrEngine.getBoxScoreThresh(), ocrEngine.getBoxThresh(), ocrEngine.getUnClipRatio(), ocrEngine.getDoAngle(), ocrEngine.getMostAngle());
-// }
-//
-// public OcrEngine getOcrEngine() {
-// return ocrEngine;
-// }
-//
-// public void useGpu(Boolean isUse) {
-// this.ocrEngine.setGpuIndex(isUse ? 0 : -1);
-// }
-//}
\ No newline at end of file
diff --git a/src/main/java/com/luooqi/ocr/local/PaddlePaddleOCRV4.java b/src/main/java/com/luooqi/ocr/local/PaddlePaddleOCRV4.java
index 33b9c98..e0a860f 100644
--- a/src/main/java/com/luooqi/ocr/local/PaddlePaddleOCRV4.java
+++ b/src/main/java/com/luooqi/ocr/local/PaddlePaddleOCRV4.java
@@ -9,6 +9,7 @@
import ai.djl.repository.zoo.ModelNotFoundException;
import ai.djl.repository.zoo.ModelZoo;
import ai.djl.repository.zoo.ZooModel;
+import ai.djl.translate.TranslateException;
import com.litongjava.djl.paddle.ocr.v4.common.RotatedBox;
import com.litongjava.djl.paddle.ocr.v4.common.RotatedBoxCompX;
import com.litongjava.djl.paddle.ocr.v4.detection.OcrV4Detection;
@@ -62,7 +63,14 @@ public void init() {
public String ocr(File imageFile) throws Exception {
Path path = imageFile.toPath();
Image image = OpenCVImageFactory.getInstance().fromFile(path);
+ return ocr(image);
+ }
+
+ public String ocr(Image image) throws Exception {
List detections = recognition.predict(manager, image, detector, recognizer);
+ if (detections == null) {
+ return null;
+ }
List initList = new ArrayList<>();
for (RotatedBox result : detections) {
@@ -105,4 +113,9 @@ public String ocr(File imageFile) throws Exception {
}
return fullText.toString();
}
+
+ public void close() {
+ detector.close();
+ recognizer.close();
+ }
}
diff --git a/src/main/java/com/luooqi/ocr/snap/ScreenCapture.java b/src/main/java/com/luooqi/ocr/snap/ScreenCapture.java
index 0a64f11..0afbe19 100644
--- a/src/main/java/com/luooqi/ocr/snap/ScreenCapture.java
+++ b/src/main/java/com/luooqi/ocr/snap/ScreenCapture.java
@@ -3,9 +3,9 @@
import cn.hutool.core.swing.ScreenUtil;
import cn.hutool.log.StaticLog;
-import com.luooqi.ocr.MainFm;
import com.luooqi.ocr.model.CaptureInfo;
import com.luooqi.ocr.utils.CommUtils;
+import com.luooqi.ocr.windows.MainForm;
import javafx.animation.AnimationTimer;
import javafx.application.Platform;
import javafx.embed.swing.SwingFXUtils;
@@ -180,8 +180,7 @@ public ScreenCapture(Stage mainStage) {
mainCanvas.setOnMouseDragged(m -> {
if (m.getButton() == MouseButton.PRIMARY) {
- if (m.getScreenX() >= CaptureInfo.ScreenMinX &&
- m.getScreenX() <= CaptureInfo.ScreenMaxX) {
+ if (m.getScreenX() >= CaptureInfo.ScreenMinX && m.getScreenX() <= CaptureInfo.ScreenMaxX) {
data.mouseXNow = (int) m.getX();
} else if (m.getScreenX() > CaptureInfo.ScreenMaxX) {
data.mouseXNow = CaptureInfo.ScreenWidth;
@@ -311,8 +310,7 @@ private void addKeyHandlers() {
}
});
- data.anyPressed.addListener((obs, wasPressed, isNowPressed) ->
- {
+ data.anyPressed.addListener((obs, wasPressed, isNowPressed) -> {
if (isNowPressed) {
yPressedAnimation.start();
} else {
@@ -370,7 +368,8 @@ private void repaintCanvas() {
: data.mouseYNow // UP
;
- gc.strokeRect(data.rectUpperLeftX - 1.00, data.rectUpperLeftY - 1.00, data.rectWidth + 2.00, data.rectHeight + 2.00);
+ gc.strokeRect(data.rectUpperLeftX - 1.00, data.rectUpperLeftY - 1.00, data.rectWidth + 2.00,
+ data.rectHeight + 2.00);
gc.clearRect(data.rectUpperLeftX, data.rectUpperLeftY, data.rectWidth, data.rectHeight);
// draw the text
@@ -378,7 +377,8 @@ private void repaintCanvas() {
double middle = data.rectUpperLeftX + data.rectWidth / 2.00;
gc.setLineWidth(1);
gc.setFill(Color.FIREBRICK);
- gc.fillRect(middle - 77, data.rectUpperLeftY < 50 ? data.rectUpperLeftY + 2 : data.rectUpperLeftY - 18.00, 100, 18);
+ gc.fillRect(middle - 77, data.rectUpperLeftY < 50 ? data.rectUpperLeftY + 2 : data.rectUpperLeftY - 18.00, 100,
+ 18);
gc.setFill(Color.WHITE);
gc.fillText(data.rectWidth + " * " + data.rectHeight, middle - 77 + 9,
data.rectUpperLeftY < 50 ? data.rectUpperLeftY + 17.00 : data.rectUpperLeftY - 4.00);
@@ -398,16 +398,16 @@ private void selectWholeScreen() {
public void prepareForCapture() {
isSnapping = true;
- MainFm.stage.setOpacity(0.0f);
+ MainForm.stage.setOpacity(0.0f);
Platform.runLater(() -> {
- Rectangle rectangle = CommUtils.getDisplayScreen(MainFm.stage);
+ Rectangle rectangle = CommUtils.getDisplayScreen(MainForm.stage);
data.reset();
CaptureInfo.ScreenMinX = rectangle.x;
CaptureInfo.ScreenMaxX = rectangle.x + rectangle.width;
CaptureInfo.ScreenWidth = rectangle.width;
CaptureInfo.ScreenHeight = rectangle.height;
BufferedImage bufferedImage = ScreenUtil.captureScreen(rectangle);
- //bufferedImage = Scalr.resize(bufferedImage, Scalr.Method.QUALITY, Scalr.Mode.AUTOMATIC, CaptureInfo.ScreenWidth * 2, CaptureInfo.ScreenHeight * 2);
+ // bufferedImage = Scalr.resize(bufferedImage, Scalr.Method.QUALITY, Scalr.Mode.AUTOMATIC, CaptureInfo.ScreenWidth * 2, CaptureInfo.ScreenHeight * 2);
WritableImage fxImage = SwingFXUtils.toFXImage(bufferedImage, null);
deActivateAllKeys();
scene.setRoot(new Pane());
@@ -417,9 +417,9 @@ public void prepareForCapture() {
mainCanvas.setHeight(CaptureInfo.ScreenHeight);
mainCanvas.setCursor(Cursor.CROSSHAIR);
initGraphContent();
- rootPane.setBackground(new Background(new BackgroundImage(fxImage,
- BackgroundRepeat.NO_REPEAT, BackgroundRepeat.NO_REPEAT,
- BackgroundPosition.CENTER, new BackgroundSize(CaptureInfo.ScreenWidth, CaptureInfo.ScreenHeight, false, false, true, true))));
+ rootPane.setBackground(new Background(new BackgroundImage(fxImage, BackgroundRepeat.NO_REPEAT,
+ BackgroundRepeat.NO_REPEAT, BackgroundPosition.CENTER,
+ new BackgroundSize(CaptureInfo.ScreenWidth, CaptureInfo.ScreenHeight, false, false, true, true))));
repaintCanvas();
stage.setScene(scene);
stage.setFullScreenExitHint("");
@@ -438,19 +438,21 @@ private void prepareImage() {
BufferedImage image;
try {
mainCanvas.setDisable(true);
- image = new Robot().createScreenCapture(new Rectangle(data.rectUpperLeftX + CaptureInfo.ScreenMinX, data.rectUpperLeftY + (int) CommUtils.getCrtScreen(stage).getVisualBounds().getMinY(), data.rectWidth, data.rectHeight));
+ image = new Robot().createScreenCapture(new Rectangle(data.rectUpperLeftX + CaptureInfo.ScreenMinX,
+ data.rectUpperLeftY + (int) CommUtils.getCrtScreen(stage).getVisualBounds().getMinY(), data.rectWidth,
+ data.rectHeight));
} catch (AWTException ex) {
StaticLog.error(ex);
return;
} finally {
mainCanvas.setDisable(false);
- MainFm.restore(false);
+ MainForm.restore(false);
}
- MainFm.doOcr(image);
+ MainForm.doOcr(image);
}
public void cancelSnap() {
deActivateAllKeys();
- MainFm.restore(true);
+ MainForm.restore(true);
}
}
diff --git a/src/main/java/com/luooqi/ocr/utils/BufferedImageUtils.java b/src/main/java/com/luooqi/ocr/utils/BufferedImageUtils.java
new file mode 100644
index 0000000..61a4d23
--- /dev/null
+++ b/src/main/java/com/luooqi/ocr/utils/BufferedImageUtils.java
@@ -0,0 +1,28 @@
+package com.luooqi.ocr.utils;
+
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Created by litonglinux@qq.com on 12/9/2023_6:28 PM
+ */
+public class BufferedImageUtils {
+ public static InputStream toInputStream(BufferedImage bufferedImage) {
+ // 将BufferedImage写入到一个ByteArrayOutputStream
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try {
+ ImageIO.write(bufferedImage, "png", baos); // 选择合适的格式,如 "png" 或 "jpg"
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ // 使用输出流的字节数组来创建一个InputStream
+ byte[] imageBytes = baos.toByteArray();
+ InputStream inputStream = new ByteArrayInputStream(imageBytes);
+ return inputStream;
+ }
+}
diff --git a/src/main/java/com/luooqi/ocr/utils/CommUtils.java b/src/main/java/com/luooqi/ocr/utils/CommUtils.java
index 9431c13..ea260c8 100644
--- a/src/main/java/com/luooqi/ocr/utils/CommUtils.java
+++ b/src/main/java/com/luooqi/ocr/utils/CommUtils.java
@@ -1,18 +1,46 @@
package com.luooqi.ocr.utils;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.imageio.IIOImage;
+import javax.imageio.ImageIO;
+import javax.imageio.ImageWriteParam;
+import javax.imageio.ImageWriter;
+import javax.imageio.stream.MemoryCacheImageOutputStream;
+import javax.swing.ImageIcon;
+
+import cn.hutool.core.util.ClassUtil;
+import com.luooqi.ocr.OcrApp;
+import com.luooqi.ocr.constants.ImagesConstants;
+import com.luooqi.ocr.model.TextBlock;
+
import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.log.StaticLog;
-import com.luooqi.ocr.MainFm;
-import com.luooqi.ocr.model.TextBlock;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.geometry.Rectangle2D;
import javafx.scene.control.Button;
-import javafx.scene.control.*;
+import javafx.scene.control.ButtonBase;
+import javafx.scene.control.Separator;
+import javafx.scene.control.ToggleButton;
+import javafx.scene.control.ToggleGroup;
+import javafx.scene.control.Tooltip;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
@@ -21,31 +49,12 @@
import javafx.stage.Screen;
import javafx.stage.Stage;
-import javax.imageio.IIOImage;
-import javax.imageio.ImageIO;
-import javax.imageio.ImageWriteParam;
-import javax.imageio.ImageWriter;
-import javax.imageio.stream.MemoryCacheImageOutputStream;
-import javax.swing.*;
-import java.awt.*;
-import java.awt.image.BufferedImage;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.lang.reflect.Method;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
public class CommUtils {
public static final Paint MASK_COLOR = Color.rgb(0, 0, 0, 0.4);
public static final int BUTTON_SIZE = 28;
- public static Background BG_TRANSPARENT = new Background(new BackgroundFill(Color.TRANSPARENT,
- CornerRadii.EMPTY, Insets.EMPTY));
+ public static Background BG_TRANSPARENT = new Background(
+ new BackgroundFill(Color.TRANSPARENT, CornerRadii.EMPTY, Insets.EMPTY));
private static Pattern NORMAL_CHAR = Pattern.compile("[\\u4e00-\\u9fa5\\w、-,/|_]");
public static Separator SEPARATOR = new Separator(Orientation.VERTICAL);
private static final float IMAGE_QUALITY = 0.5f;
@@ -97,7 +106,7 @@ static String combineTextBlocks(List textBlocks, boolean isEng) {
int maxX = -1;
double maxAngle = -100;
for (TextBlock textBlock : textBlocks) {
- //System.out.println(textBlock.getAngle()+ "\t" + textBlock.getFontSize());
+ // System.out.println(textBlock.getAngle()+ "\t" + textBlock.getFontSize());
if (textBlock.getTopLeft().x < minX) {
minX = textBlock.getTopLeft().x;
minBlock = textBlock;
@@ -138,10 +147,8 @@ static String combineTextBlocks(List textBlocks, boolean isEng) {
continue;
}
String endTxt = blockTxt.substring(blockTxt.length() - 1);
- if (maxX - lastBlock.getTopRight().x >= CHAR_WIDTH * 2 ||
- !NORMAL_CHAR.matcher(endTxt).find() ||
- (NORMAL_CHAR.matcher(endTxt).find() &&
- (firstBlock.getTopLeft().x - minX) > CHAR_WIDTH * 2)) {
+ if (maxX - lastBlock.getTopRight().x >= CHAR_WIDTH * 2 || !NORMAL_CHAR.matcher(endTxt).find()
+ || (NORMAL_CHAR.matcher(endTxt).find() && (firstBlock.getTopLeft().x - minX) > CHAR_WIDTH * 2)) {
sb.append("\n");
for (int i = 0, ln = (firstBlock.getTopLeft().x - minX) / CHAR_WIDTH; i < ln; i++) {
if (i % 2 == 0) {
@@ -149,7 +156,8 @@ static String combineTextBlocks(List textBlocks, boolean isEng) {
}
}
} else {
- if (CharUtil.isLetterOrNumber(endTxt.charAt(0)) && CharUtil.isLetterOrNumber(firstBlock.getText().charAt(0))) {
+ if (CharUtil.isLetterOrNumber(endTxt.charAt(0))
+ && CharUtil.isLetterOrNumber(firstBlock.getText().charAt(0))) {
sb.append(" ");
}
}
@@ -165,8 +173,8 @@ static String combineTextBlocks(List textBlocks, boolean isEng) {
TextBlock text = line.get(i);
String ocrText = text.getText();
if (i > 0) {
- for (int a = 0, ln = (text.getTopLeft().x - line.get(i - 1).getTopRight().x) / (CHAR_WIDTH * 2);
- a < ln; a++) {
+ for (int a = 0,
+ ln = (text.getTopLeft().x - line.get(i - 1).getTopRight().x) / (CHAR_WIDTH * 2); a < ln; a++) {
sb.append(" ");
}
}
@@ -250,17 +258,18 @@ private static void initButton(ButtonBase button, String id, int size, Runnable
}
public static void initStage(Stage stage) {
+
try {
if (CommUtils.IS_MAC_OS) {
- URL iconURL = MainFm.class.getResource("/img/logo.png");
+ URL iconURL = ClassUtil.getClassLoader().getResource(ImagesConstants.LOGO);
java.awt.Image image = new ImageIcon(iconURL).getImage();
Class appleApp = Class.forName("com.apple.eawt.Application");
- //noinspection unchecked
+ // noinspection unchecked
Method getApplication = appleApp.getMethod("getApplication");
Object application = getApplication.invoke(appleApp);
Class[] params = new Class[1];
params[0] = java.awt.Image.class;
- //noinspection unchecked
+ // noinspection unchecked
Method setDockIconImage = appleApp.getMethod("setDockIconImage", params);
setDockIconImage.invoke(application, image);
}
@@ -268,7 +277,8 @@ public static void initStage(Stage stage) {
StaticLog.error(e);
}
stage.setTitle("树洞OCR文字识别");
- stage.getIcons().add(new javafx.scene.image.Image(MainFm.class.getResource("/img/logo.png").toExternalForm()));
+ URL iconURL = ClassUtil.getClassLoader().getResource(ImagesConstants.LOGO);
+ stage.getIcons().add(new javafx.scene.image.Image(iconURL.toExternalForm()));
}
private static final Pattern SCALE_PATTERN = Pattern.compile("renderScale:([\\d.]+)");
@@ -276,8 +286,7 @@ public static void initStage(Stage stage) {
public static Rectangle getDisplayScreen(Stage stage) {
Screen crtScreen = getCrtScreen(stage);
Rectangle2D rectangle2D = crtScreen.getBounds();
- return new Rectangle((int) rectangle2D.getMinX(), (int) rectangle2D.getMinY(),
- (int) rectangle2D.getWidth(),
+ return new Rectangle((int) rectangle2D.getMinX(), (int) rectangle2D.getMinY(), (int) rectangle2D.getWidth(),
(int) rectangle2D.getHeight());
}
diff --git a/src/main/java/com/luooqi/ocr/utils/GlobalKeyListener.java b/src/main/java/com/luooqi/ocr/utils/GlobalKeyListener.java
index 26671b6..f64a1cb 100644
--- a/src/main/java/com/luooqi/ocr/utils/GlobalKeyListener.java
+++ b/src/main/java/com/luooqi/ocr/utils/GlobalKeyListener.java
@@ -1,8 +1,9 @@
package com.luooqi.ocr.utils;
import cn.hutool.log.StaticLog;
-import com.luooqi.ocr.MainFm;
+import com.luooqi.ocr.OcrApp;
import com.luooqi.ocr.snap.ScreenCapture;
+import com.luooqi.ocr.windows.MainForm;
import org.jnativehook.NativeInputEvent;
import org.jnativehook.keyboard.NativeKeyEvent;
import org.jnativehook.keyboard.NativeKeyListener;
@@ -19,11 +20,11 @@ public void nativeKeyTyped(NativeKeyEvent nativeKeyEvent) {
public void nativeKeyPressed(NativeKeyEvent e) {
if (e.getKeyCode() == NativeKeyEvent.VC_F4) {
preventEvent(e);
- MainFm.screenShotOcr();
+ MainForm.screenShotOcr();
} else if (e.getKeyCode() == NativeKeyEvent.VC_ESCAPE) {
if (ScreenCapture.isSnapping) {
preventEvent(e);
- MainFm.cancelSnap();
+ MainForm.cancelSnap();
}
}
}
diff --git a/src/main/java/com/luooqi/ocr/utils/OcrUtils.java b/src/main/java/com/luooqi/ocr/utils/OcrUtils.java
index 6387134..85264de 100644
--- a/src/main/java/com/luooqi/ocr/utils/OcrUtils.java
+++ b/src/main/java/com/luooqi/ocr/utils/OcrUtils.java
@@ -1,9 +1,12 @@
package com.luooqi.ocr.utils;
+import ai.djl.modality.cv.Image;
+import ai.djl.opencv.OpenCVImageFactory;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.CharsetUtil;
+import cn.hutool.core.util.HashUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.crypto.SecureUtil;
@@ -17,9 +20,16 @@
import com.benjaminwan.ocrlibrary.OcrResult;
import com.luooqi.ocr.local.PaddlePaddleOCRV4;
import com.luooqi.ocr.model.TextBlock;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.rendering.PDFRenderer;
+import javax.imageio.ImageIO;
import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferInt;
import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
import java.util.List;
import java.util.*;
@@ -36,12 +46,13 @@ public static String recImgLocal(byte[] imgData) {
return recImgLocal(file);
}
+ public static String recImgLocal(BufferedImage image) {
+ byte[] bytes = CommUtils.imageToBytes(image);
+ return recImgLocal(bytes);
+ }
+
public static String recImgLocal(File file) {
if (file.exists()) {
-// OcrEngine ocrEngine = LocalOCR.INSTANCE.getOcrEngine();
-// OcrResult ocrResult = ocrEngine.detect(file.getAbsolutePath());
-// return extractLocalResult(ocrResult);
- //替换为PaddlePaddleOCRV4
try {
return PaddlePaddleOCRV4.INSTANCE.ocr(file);
} catch (Exception e) {
@@ -53,6 +64,33 @@ public static String recImgLocal(File file) {
}
+ public static String recPdfLocal(File pdfFile) {
+ if (pdfFile.exists()) {
+ try (PDDocument document = PDDocument.load(pdfFile)) {
+ PDFRenderer renderer = new PDFRenderer(document);
+ List ocrResults = new ArrayList<>();
+
+ for (int i = 0; i < document.getNumberOfPages(); ++i) {
+ BufferedImage bufferedImage = renderer.renderImageWithDPI(i, 300);
+ long hashCode = HashUtil.hfHash(pdfFile.getName());
+ String filename = "temp_" + hashCode + "_" + i + ".png";
+ FileOutputStream fileOutputStream = new FileOutputStream(filename);
+ ImageIO.write(bufferedImage, "png", fileOutputStream); // 选择合适的格式,如 "png" 或 "jpg"
+ String text = recImgLocal(new File(filename));
+ ocrResults.add(text);
+ }
+ // 将所有页面的OCR结果合并为一个字符串
+ return String.join("\n", ocrResults);
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return "文件不存在";
+ }
+ return null;
+ }
+
+
public static String ocrImg(byte[] imgData) {
int i = Math.abs(UUID.randomUUID().hashCode()) % 4;
StaticLog.info("OCR Engine: " + i);
diff --git a/src/main/java/com/luooqi/ocr/MainFm.java b/src/main/java/com/luooqi/ocr/windows/MainForm.java
similarity index 83%
rename from src/main/java/com/luooqi/ocr/MainFm.java
rename to src/main/java/com/luooqi/ocr/windows/MainForm.java
index ca26739..23f9519 100644
--- a/src/main/java/com/luooqi/ocr/MainFm.java
+++ b/src/main/java/com/luooqi/ocr/windows/MainForm.java
@@ -1,6 +1,7 @@
-package com.luooqi.ocr;
+package com.luooqi.ocr.windows;
-import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.io.FileTypeUtil;
+import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.log.StaticLog;
import com.luooqi.ocr.config.InitConfig;
@@ -10,7 +11,6 @@
import com.luooqi.ocr.snap.ScreenCapture;
import com.luooqi.ocr.utils.CommUtils;
import com.luooqi.ocr.utils.OcrUtils;
-import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.geometry.Insets;
@@ -27,36 +27,34 @@
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import lombok.extern.slf4j.Slf4j;
-import org.jnativehook.GlobalScreen;
-import org.slf4j.LoggerFactory;
import java.awt.image.BufferedImage;
import java.io.File;
-import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
-import static javafx.application.Platform.runLater;
-
+/**
+ * Created by litonglinux@qq.com on 12/9/2023_4:40 PM
+ */
@Slf4j
-public class MainFm extends Application {
-
- public static void main(String[] args) {
- InitConfig.init();
- launch(args);
- }
-
+public class MainForm {
private static StageInfo stageInfo;
public static Stage stage;
private static Scene mainScene;
+
+ @Override
+ public int hashCode() {
+ return super.hashCode();
+ }
+
private static ScreenCapture screenCapture;
private static ProcessController processController;
private static TextArea textArea;
//private static boolean isSegment = true;
//private static String ocrText = "";
- @Override
- public void start(Stage primaryStage) {
+ public void init(Stage primaryStage) {
+
log.info("primaryStage:{}", primaryStage);
stage = primaryStage;
setAutoResize();
@@ -75,26 +73,31 @@ public void start(Stage primaryStage) {
// isSegment = newValue.getUserData().toString().equals("segmentBtn");
// });
- HBox topBar = new HBox(
- CommUtils.createButton("snapBtn", MainFm::screenShotOcr, "截图"),
- CommUtils.createButton("openImageBtn", MainFm::openImageOcr, "打开"),
- CommUtils.createButton("copyBtn", this::copyText, "复制"),
- CommUtils.createButton("pasteBtn", this::pasteText, "粘贴"),
- CommUtils.createButton("clearBtn", this::clearText, "清空"),
- CommUtils.createButton("wrapBtn", this::wrapText, "换行")
- //CommUtils.SEPARATOR, resetBtn, segmentBtn
+ HBox topBar = getTopBar();
+ textArea = getCenter();
+ ToolBar footerBar = getFooterBar();
+ BorderPane root = new BorderPane();
+ root.setTop(topBar);
+ root.setCenter(textArea);
+ root.setBottom(footerBar);
+ root.getStylesheets().addAll(
+ getClass().getResource("/css/main.css").toExternalForm()
);
- topBar.setId("topBar");
- topBar.setMinHeight(40);
- topBar.setSpacing(8);
- topBar.setPadding(new Insets(6, 8, 6, 8));
+ CommUtils.initStage(primaryStage);
+ mainScene = new Scene(root, 670, 470);
+ stage.setScene(mainScene);
+ }
- textArea = new TextArea();
+ private TextArea getCenter() {
+ TextArea textArea = new TextArea();
textArea.setId("ocrTextArea");
textArea.setWrapText(true);
textArea.setBorder(new Border(new BorderStroke(Color.DARKGRAY, BorderStrokeStyle.SOLID, CornerRadii.EMPTY, BorderWidths.DEFAULT)));
textArea.setFont(Font.font("Arial", FontPosture.REGULAR, 14));
+ return textArea;
+ }
+ private ToolBar getFooterBar() {
ToolBar footerBar = new ToolBar();
footerBar.setId("statsToolbar");
Label statsLabel = new Label();
@@ -102,18 +105,24 @@ public void start(Stage primaryStage) {
textArea.textProperty().addListener((observable, oldValue, newValue) -> statsProperty.set("总字数:" + newValue.replaceAll(CommUtils.SPECIAL_CHARS, "").length()));
statsLabel.textProperty().bind(statsProperty);
footerBar.getItems().add(statsLabel);
- BorderPane root = new BorderPane();
- root.setTop(topBar);
- root.setCenter(textArea);
- root.setBottom(footerBar);
- root.getStylesheets().addAll(
- getClass().getResource("/css/main.css").toExternalForm()
+ return footerBar;
+ }
+
+ private HBox getTopBar() {
+ HBox topBar = new HBox(
+ CommUtils.createButton("snapBtn", MainForm::screenShotOcr, "截图"),
+ CommUtils.createButton("openImageBtn", this::openImageOcr, "打开"),
+ CommUtils.createButton("copyBtn", this::copyText, "复制"),
+ CommUtils.createButton("pasteBtn", this::pasteText, "粘贴"),
+ CommUtils.createButton("clearBtn", this::clearText, "清空"),
+ CommUtils.createButton("wrapBtn", this::wrapText, "换行")
+ //CommUtils.SEPARATOR, resetBtn, segmentBtn
);
- CommUtils.initStage(primaryStage);
- mainScene = new Scene(root, 670, 470);
- stage.setScene(mainScene);
- stage.show();
-// InitConfig.after();
+ topBar.setId("topBar");
+ topBar.setMinHeight(40);
+ topBar.setSpacing(8);
+ topBar.setPadding(new Insets(6, 8, 6, 8));
+ return topBar;
}
private void setAutoResize() {
@@ -134,10 +143,6 @@ private void wrapText() {
textArea.setWrapText(!textArea.isWrapText());
}
- @Override
- public void stop() throws Exception {
- GlobalScreen.unregisterNativeHook();
- }
private void clearText() {
textArea.setText("");
@@ -170,23 +175,24 @@ public static void screenShotOcr() {
stageInfo.setWidth(stage.getWidth());
stageInfo.setHeight(stage.getHeight());
stageInfo.setFullScreenState(stage.isFullScreen());
- runLater(screenCapture::prepareForCapture);
+ Platform.runLater(screenCapture::prepareForCapture);
}
/**
* 打开图片
*/
- private static void openImageOcr() {
+ private void openImageOcr() {
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Please Select Image File");
- fileChooser.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("Image Files", "*.png", "*.jpg"));
+ String[] extensions = {"*.png", "*.jpg", "*.pdf", "*.PDF"};
+ fileChooser.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("Image Files", extensions));
File selectedFile = fileChooser.showOpenDialog(stage);
if (selectedFile == null || !selectedFile.isFile()) {
return;
}
stageInfo = new StageInfo(stage.getX(), stage.getY(),
stage.getWidth(), stage.getHeight(), stage.isFullScreen());
- MainFm.stage.close();
+
try {
//BufferedImage image = ImageIO.read(selectedFile);
doOcr(selectedFile);
@@ -197,18 +203,18 @@ private static void openImageOcr() {
public static void cancelSnap() {
- runLater(screenCapture::cancelSnap);
+ Platform.runLater(screenCapture::cancelSnap);
}
public static void doOcr(BufferedImage image) {
processController.setX(CaptureInfo.ScreenMinX + (CaptureInfo.ScreenWidth - 300) / 2);
processController.setY(250);
processController.show();
- Thread ocrThread = new Thread(() -> {
- byte[] bytes = CommUtils.imageToBytes(image);
+
+ ThreadUtil.execute(() -> {
String text = null;
try {
- text = OcrUtils.recImgLocal(bytes);
+ text = OcrUtils.recImgLocal(image);
} catch (Exception e) {
text = e.getMessage();
}
@@ -220,35 +226,34 @@ public static void doOcr(BufferedImage image) {
restore(true);
});
});
- ocrThread.setDaemon(false);
- ocrThread.start();
}
public static void doOcr(File selectedFile) {
- org.slf4j.Logger log = LoggerFactory.getLogger(MainFm.class);
processController.setX(CaptureInfo.ScreenMinX + (CaptureInfo.ScreenWidth - 300) / 2);
processController.setY(250);
processController.show();
- Thread ocrThread = new Thread(() -> {
+ ThreadUtil.execute(() -> {
String text = null;
try {
- text = OcrUtils.recImgLocal(selectedFile);
+ String fileType = FileTypeUtil.getType(selectedFile);
+ if ("pdf".equalsIgnoreCase(fileType)) {
+ text = OcrUtils.recPdfLocal(selectedFile);
+ } else {
+ text = OcrUtils.recImgLocal(selectedFile);
+ }
+
} catch (Exception e) {
text = e.getMessage();
e.printStackTrace();
}
- //log.info("识别结果:{}", text);
String finalText = text;
Platform.runLater(() -> {
processController.close();
textArea.setText(finalText);
-
restore(true);
});
});
- ocrThread.setDaemon(false);
- ocrThread.start();
}
public static void restore(boolean focus) {
@@ -266,4 +271,4 @@ public static void restore(boolean focus) {
stage.setOpacity(0.0f);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/test/java/com/luooqi/ocr/utils/OcrUtilsTest.java b/src/test/java/com/luooqi/ocr/utils/OcrUtilsTest.java
index 192144d..478b01a 100644
--- a/src/test/java/com/luooqi/ocr/utils/OcrUtilsTest.java
+++ b/src/test/java/com/luooqi/ocr/utils/OcrUtilsTest.java
@@ -10,6 +10,7 @@
import java.awt.*;
import java.awt.geom.AffineTransform;
+import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -17,23 +18,35 @@
public class OcrUtilsTest {
- String html = "{\"result\":[{\"groupID\":0,\"content\":\"目前,RFID(radio frequency identification\\n\",\"frame\":[\"537,11\",\"935,11\",\"935,41\",\"537,41\"]},{\"groupID\":1,\"content\":\"0引言\\n\",\"frame\":[\"23,22\",\"129,22\",\"129,54\",\"23,54\"]},{\"groupID\":2,\"content\":\"devices)技术、网络化信息管理及数据库技术在农\\n\",\"frame\":[\"496,43\",\"931,43\",\"931,65\",\"496,65\"]},{\"groupID\":3,\"content\":\"近年来,关于遗传育种的研究已从传统的常规\\n\",\"frame\":[\"64,68\",\"461,68\",\"461,93\",\"64,93\"]},{\"groupID\":2,\"content\":\"业领域的应用较广泛,三者相结合主要应用于农田\\n\",\"frame\":[\"496,68\",\"931,68\",\"931,93\",\"496,93\"]},{\"groupID\":2,\"content\":\"信息采集[9-12]、食品存储监测及安全跟踪[13-19]、畜\\n\",\"frame\":[\"499,94\",\"931,94\",\"931,121\",\"499,121\"]},{\"groupID\":3,\"content\":\"育种技术进入依靠生物技术育种阶段,研究内容也\\n\",\"frame\":[\"28,94\",\"460,94\",\"460,121\",\"28,121\"]},{\"groupID\":2,\"content\":\"禽养殖监测[20-2]等方面。关于采用编码的方法结合\\n\",\"frame\":[\"499,125\",\"931,125\",\"931,149\",\"499,149\"]},{\"groupID\":3,\"content\":\"从单个基因的测序转为有计划、大规模地检测水稻\\n\",\"frame\":[\"25,125\",\"460,125\",\"460,148\",\"25,148\"]},{\"groupID\":3,\"content\":\"等重要生物体的基因图谱。要实现水稻分子遗传育\\n\",\"frame\":[\"25,150\",\"460,150\",\"460,175\",\"25,175\"]},{\"groupID\":2,\"content\":\"数据库技术对农业中的动、植物进行标识、溯源和\\n\",\"frame\":[\"497,155\",\"929,155\",\"929,177\",\"497,177\"]},{\"groupID\":2,\"content\":\"建模方面的研究也有报道[242]。其在农作物育种方\\n\",\"frame\":[\"500,178\",\"929,178\",\"929,203\",\"500,203\"]},{\"groupID\":3,\"content\":\"种的目标,首要任务是了解、选择群体细胞的生理\\n\",\"frame\":[\"25,181\",\"460,181\",\"460,204\",\"25,204\"]},{\"groupID\":3,\"content\":\"生化特性以及与之相对应的表型现象,也就是要确\\n\",\"frame\":[\"25,207\",\"461,207\",\"461,232\",\"25,232\"]},{\"groupID\":2,\"content\":\"面的研究则主要涉及育种参数统计分析,育种遗传\\n\",\"frame\":[\"496,211\",\"931,211\",\"931,235\",\"496,235\"]},{\"groupID\":2,\"content\":\"过程的计算机数学模型,农作物育种专家系统,种\\n\",\"frame\":[\"496,237\",\"931,237\",\"931,262\",\"496,262\"]},{\"groupID\":3,\"content\":\"定植物群体实际基因或基因片段的表达与表型现\\n\",\"frame\":[\"25,237\",\"461,237\",\"461,262\",\"25,262\"]},{\"groupID\":3,\"content\":\"象的内在关联程度1一]。高通量的水稻种植试验成为\\n\",\"frame\":[\"25,263\",\"460,263\",\"460,288\",\"25,288\"]},{\"groupID\":2,\"content\":\"质资源数据库和育种信息管理等方面。由于长期以\\n\",\"frame\":[\"499,267\",\"931,267\",\"931,291\",\"499,291\"]},{\"groupID\":2,\"content\":\"来农作物育种信息采用人工方式记录管理,在育种\\n\",\"frame\":[\"499,293\",\"931,293\",\"931,318\",\"499,318\"]},{\"groupID\":3,\"content\":\"表型现象验证的重要手段。大规模、高效率的分子\\n\",\"frame\":[\"25,293\",\"461,293\",\"461,318\",\"25,318\"]},{\"groupID\":3,\"content\":\"遗传育种技术试验平台是实现高通量分子遗传育\\n\",\"frame\":[\"25,319\",\"461,319\",\"461,345\",\"25,345\"]},{\"groupID\":2,\"content\":\"试验量不大时,信息管理需求方面的矛眉并未突显,\\n\",\"frame\":[\"496,324\",\"928,324\",\"928,347\",\"496,347\"]},{\"groupID\":2,\"content\":\"因此国内关于农作物育种信息管理和数据库方面的\\n\",\"frame\":[\"496,349\",\"931,349\",\"931,374\",\"496,374\"]},{\"groupID\":3,\"content\":\"种试验的关键环节。温、湿度自动调节的现代化温\\n\",\"frame\":[\"25,350\",\"460,350\",\"460,373\",\"25,373\"]},{\"groupID\":3,\"content\":\"室以及全自动化的水稻种植、栽培、输送、检测试\\n\",\"frame\":[\"25,377\",\"460,377\",\"460,400\",\"25,400\"]},{\"groupID\":2,\"content\":\"研究报导较少[283]。随着分子遗传育种试验的中信\\n\",\"frame\":[\"496,376\",\"931,376\",\"931,404\",\"496,404\"]},{\"groupID\":2,\"content\":\"息量的倍增,如何运用计算机技术对育种试验信息\\n\",\"frame\":[\"499,406\",\"931,406\",\"931,431\",\"499,431\"]},{\"groupID\":3,\"content\":\"验环境的建立是保证高通重分子遗传育种试验完\\n\",\"frame\":[\"25,407\",\"460,407\",\"460,430\",\"25,430\"]},{\"groupID\":3,\"content\":\"成的基础条件。\\n\",\"frame\":[\"25,433\",\"154,433\",\"154,456\",\"25,456\"]},{\"groupID\":2,\"content\":\"进行科学、高效的管理成为一个亟待解决的问题。\\n\",\"frame\":[\"498,435\",\"914,435\",\"914,459\",\"498,459\"]},{\"groupID\":4,\"content\":\"本文以高通量水稻种植试验为研究对象,在已\\n\",\"frame\":[\"535,462\",\"931,462\",\"931,487\",\"535,487\"]},{\"groupID\":5,\"content\":\"修订日期:2014-02-26\\n\",\"frame\":[\"197,487\",\"336,487\",\"336,504\",\"197,504\"]},{\"groupID\":6,\"content\":\"收稿日期:2013-04-13\\n\",\"frame\":[\"27,487\",\"164,487\",\"164,504\",\"27,504\"]},{\"groupID\":4,\"content\":\"有的温室水稻盆栽自动化输送设备基础上,结合\\n\",\"frame\":[\"499,489\",\"931,489\",\"931,517\",\"499,517\"]},{\"groupID\":7,\"content\":\"基金项目:中央高校基本科研业务费专项资金资助(2013PY052):国\\n\",\"frame\":[\"27,511\",\"461,511\",\"461,527\",\"27,527\"]},{\"groupID\":4,\"content\":\"RFID技术、网络化信息管理及数据库技术,研究\\n\",\"frame\":[\"496,519\",\"934,519\",\"934,542\",\"496,542\"]},{\"groupID\":7,\"content\":\"家自然科学基金(61007058)\\n\",\"frame\":[\"27,530\",\"204,530\",\"204,549\",\"27,549\"]},{\"groupID\":4,\"content\":\"水稻的遗传育种试验中种植试验信息的管理方案,\\n\",\"frame\":[\"499,545\",\"922,545\",\"922,570\",\"499,570\"]},{\"groupID\":8,\"content\":\"作者简介:高云(1974一),女(汉),湖北鄂州,副教授,硕士,农\\n\",\"frame\":[\"27,554\",\"461,554\",\"461,570\",\"27,570\"]},{\"groupID\":8,\"content\":\"业智能检测与控制。武汉湖北省武汉市洪山区华中农业大学工学院,\\n\",\"frame\":[\"26,573\",\"455,573\",\"455,594\",\"26,594\"]},{\"groupID\":4,\"content\":\"针对水稻的突变体库的特点,提出了水稻种植试验\\n\",\"frame\":[\"496,576\",\"931,576\",\"931,599\",\"496,599\"]},{\"groupID\":9,\"content\":\"430070。Email:angelclouder@hotmail.com\\n\",\"frame\":[\"23,595\",\"284,595\",\"284,616\",\"23,616\"]},{\"groupID\":4,\"content\":\"家系可追溯的数据管理方法,实现水稻家系的自动\\n\",\"frame\":[\"499,603\",\"931,603\",\"931,626\",\"499,626\"]},{\"groupID\":10,\"content\":\"中国农业工程学会会员(E041700006M)\\n\",\"frame\":[\"27,621\",\"274,621\",\"274,637\",\"27,637\"]},{\"groupID\":4,\"content\":\"追溯和家系树的自动生成。该研究有效解决了高通\\n\",\"frame\":[\"496,632\",\"931,632\",\"931,655\",\"496,655\"]},{\"groupID\":11,\"content\":\"米通信作者:李小昱(1953一),女(汉),陕西西安,教授,博士生导\\n\",\"frame\":[\"26,639\",\"460,639\",\"460,660\",\"26,660\"]},{\"groupID\":4,\"content\":\"量下育种种植试验的信息量大、管理复杂的问题,\\n\",\"frame\":[\"499,658\",\"922,658\",\"922,682\",\"499,682\"]},{\"groupID\":11,\"content\":\"师,智能化检测技术。武汉湖北省武汉市洪山区华中农业大学工学院,\\n\",\"frame\":[\"25,664\",\"458,664\",\"458,680\",\"25,680\"]},{\"groupID\":12,\"content\":\"430070。Email:lixiaoyu@mail.hzau.edu.cn\\n\",\"frame\":[\"24,686\",\"280,686\",\"280,703\",\"24,703\"]},{\"groupID\":4,\"content\":\"提高了试验效率。对于其他与水稻相类似农作物育\\n\",\"frame\":[\"499,687\",\"931,687\",\"931,712\",\"499,712\"]},{\"groupID\":13,\"content\":\"中国农业工程学会高级会员(E041200068S)\\n\",\"frame\":[\"27,707\",\"297,707\",\"297,723\",\"27,723\"]}],\"success\":1,\"zly\":\"zly\",\"ocr_time\":1584.014892578125,\"id\":\"2274054490548600832\",\"lang\":\"zh-Chs\",\"direction\":0}";
-
- @Test
- public void sogouMobileOcr() {
-
- }
-
- private Point frameToPoint(String text){
- String[] arr = text.split(",");
- return new Point(Integer.valueOf(arr[0].trim()), Integer.valueOf(arr[1].trim()));
- }
-
- @Test
- public void sogouWebOcr() {
- GraphicsConfiguration asdf = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
- AffineTransform asfd2 = asdf.getDefaultTransform();
- double scaleX = asfd2.getScaleX();
- double scaleY = asfd2.getScaleY();
- }
+ String html = "{\"result\":[{\"groupID\":0,\"content\":\"目前,RFID(radio frequency identification\\n\",\"frame\":[\"537,11\",\"935,11\",\"935,41\",\"537,41\"]},{\"groupID\":1,\"content\":\"0引言\\n\",\"frame\":[\"23,22\",\"129,22\",\"129,54\",\"23,54\"]},{\"groupID\":2,\"content\":\"devices)技术、网络化信息管理及数据库技术在农\\n\",\"frame\":[\"496,43\",\"931,43\",\"931,65\",\"496,65\"]},{\"groupID\":3,\"content\":\"近年来,关于遗传育种的研究已从传统的常规\\n\",\"frame\":[\"64,68\",\"461,68\",\"461,93\",\"64,93\"]},{\"groupID\":2,\"content\":\"业领域的应用较广泛,三者相结合主要应用于农田\\n\",\"frame\":[\"496,68\",\"931,68\",\"931,93\",\"496,93\"]},{\"groupID\":2,\"content\":\"信息采集[9-12]、食品存储监测及安全跟踪[13-19]、畜\\n\",\"frame\":[\"499,94\",\"931,94\",\"931,121\",\"499,121\"]},{\"groupID\":3,\"content\":\"育种技术进入依靠生物技术育种阶段,研究内容也\\n\",\"frame\":[\"28,94\",\"460,94\",\"460,121\",\"28,121\"]},{\"groupID\":2,\"content\":\"禽养殖监测[20-2]等方面。关于采用编码的方法结合\\n\",\"frame\":[\"499,125\",\"931,125\",\"931,149\",\"499,149\"]},{\"groupID\":3,\"content\":\"从单个基因的测序转为有计划、大规模地检测水稻\\n\",\"frame\":[\"25,125\",\"460,125\",\"460,148\",\"25,148\"]},{\"groupID\":3,\"content\":\"等重要生物体的基因图谱。要实现水稻分子遗传育\\n\",\"frame\":[\"25,150\",\"460,150\",\"460,175\",\"25,175\"]},{\"groupID\":2,\"content\":\"数据库技术对农业中的动、植物进行标识、溯源和\\n\",\"frame\":[\"497,155\",\"929,155\",\"929,177\",\"497,177\"]},{\"groupID\":2,\"content\":\"建模方面的研究也有报道[242]。其在农作物育种方\\n\",\"frame\":[\"500,178\",\"929,178\",\"929,203\",\"500,203\"]},{\"groupID\":3,\"content\":\"种的目标,首要任务是了解、选择群体细胞的生理\\n\",\"frame\":[\"25,181\",\"460,181\",\"460,204\",\"25,204\"]},{\"groupID\":3,\"content\":\"生化特性以及与之相对应的表型现象,也就是要确\\n\",\"frame\":[\"25,207\",\"461,207\",\"461,232\",\"25,232\"]},{\"groupID\":2,\"content\":\"面的研究则主要涉及育种参数统计分析,育种遗传\\n\",\"frame\":[\"496,211\",\"931,211\",\"931,235\",\"496,235\"]},{\"groupID\":2,\"content\":\"过程的计算机数学模型,农作物育种专家系统,种\\n\",\"frame\":[\"496,237\",\"931,237\",\"931,262\",\"496,262\"]},{\"groupID\":3,\"content\":\"定植物群体实际基因或基因片段的表达与表型现\\n\",\"frame\":[\"25,237\",\"461,237\",\"461,262\",\"25,262\"]},{\"groupID\":3,\"content\":\"象的内在关联程度1一]。高通量的水稻种植试验成为\\n\",\"frame\":[\"25,263\",\"460,263\",\"460,288\",\"25,288\"]},{\"groupID\":2,\"content\":\"质资源数据库和育种信息管理等方面。由于长期以\\n\",\"frame\":[\"499,267\",\"931,267\",\"931,291\",\"499,291\"]},{\"groupID\":2,\"content\":\"来农作物育种信息采用人工方式记录管理,在育种\\n\",\"frame\":[\"499,293\",\"931,293\",\"931,318\",\"499,318\"]},{\"groupID\":3,\"content\":\"表型现象验证的重要手段。大规模、高效率的分子\\n\",\"frame\":[\"25,293\",\"461,293\",\"461,318\",\"25,318\"]},{\"groupID\":3,\"content\":\"遗传育种技术试验平台是实现高通量分子遗传育\\n\",\"frame\":[\"25,319\",\"461,319\",\"461,345\",\"25,345\"]},{\"groupID\":2,\"content\":\"试验量不大时,信息管理需求方面的矛眉并未突显,\\n\",\"frame\":[\"496,324\",\"928,324\",\"928,347\",\"496,347\"]},{\"groupID\":2,\"content\":\"因此国内关于农作物育种信息管理和数据库方面的\\n\",\"frame\":[\"496,349\",\"931,349\",\"931,374\",\"496,374\"]},{\"groupID\":3,\"content\":\"种试验的关键环节。温、湿度自动调节的现代化温\\n\",\"frame\":[\"25,350\",\"460,350\",\"460,373\",\"25,373\"]},{\"groupID\":3,\"content\":\"室以及全自动化的水稻种植、栽培、输送、检测试\\n\",\"frame\":[\"25,377\",\"460,377\",\"460,400\",\"25,400\"]},{\"groupID\":2,\"content\":\"研究报导较少[283]。随着分子遗传育种试验的中信\\n\",\"frame\":[\"496,376\",\"931,376\",\"931,404\",\"496,404\"]},{\"groupID\":2,\"content\":\"息量的倍增,如何运用计算机技术对育种试验信息\\n\",\"frame\":[\"499,406\",\"931,406\",\"931,431\",\"499,431\"]},{\"groupID\":3,\"content\":\"验环境的建立是保证高通重分子遗传育种试验完\\n\",\"frame\":[\"25,407\",\"460,407\",\"460,430\",\"25,430\"]},{\"groupID\":3,\"content\":\"成的基础条件。\\n\",\"frame\":[\"25,433\",\"154,433\",\"154,456\",\"25,456\"]},{\"groupID\":2,\"content\":\"进行科学、高效的管理成为一个亟待解决的问题。\\n\",\"frame\":[\"498,435\",\"914,435\",\"914,459\",\"498,459\"]},{\"groupID\":4,\"content\":\"本文以高通量水稻种植试验为研究对象,在已\\n\",\"frame\":[\"535,462\",\"931,462\",\"931,487\",\"535,487\"]},{\"groupID\":5,\"content\":\"修订日期:2014-02-26\\n\",\"frame\":[\"197,487\",\"336,487\",\"336,504\",\"197,504\"]},{\"groupID\":6,\"content\":\"收稿日期:2013-04-13\\n\",\"frame\":[\"27,487\",\"164,487\",\"164,504\",\"27,504\"]},{\"groupID\":4,\"content\":\"有的温室水稻盆栽自动化输送设备基础上,结合\\n\",\"frame\":[\"499,489\",\"931,489\",\"931,517\",\"499,517\"]},{\"groupID\":7,\"content\":\"基金项目:中央高校基本科研业务费专项资金资助(2013PY052):国\\n\",\"frame\":[\"27,511\",\"461,511\",\"461,527\",\"27,527\"]},{\"groupID\":4,\"content\":\"RFID技术、网络化信息管理及数据库技术,研究\\n\",\"frame\":[\"496,519\",\"934,519\",\"934,542\",\"496,542\"]},{\"groupID\":7,\"content\":\"家自然科学基金(61007058)\\n\",\"frame\":[\"27,530\",\"204,530\",\"204,549\",\"27,549\"]},{\"groupID\":4,\"content\":\"水稻的遗传育种试验中种植试验信息的管理方案,\\n\",\"frame\":[\"499,545\",\"922,545\",\"922,570\",\"499,570\"]},{\"groupID\":8,\"content\":\"作者简介:高云(1974一),女(汉),湖北鄂州,副教授,硕士,农\\n\",\"frame\":[\"27,554\",\"461,554\",\"461,570\",\"27,570\"]},{\"groupID\":8,\"content\":\"业智能检测与控制。武汉湖北省武汉市洪山区华中农业大学工学院,\\n\",\"frame\":[\"26,573\",\"455,573\",\"455,594\",\"26,594\"]},{\"groupID\":4,\"content\":\"针对水稻的突变体库的特点,提出了水稻种植试验\\n\",\"frame\":[\"496,576\",\"931,576\",\"931,599\",\"496,599\"]},{\"groupID\":9,\"content\":\"430070。Email:angelclouder@hotmail.com\\n\",\"frame\":[\"23,595\",\"284,595\",\"284,616\",\"23,616\"]},{\"groupID\":4,\"content\":\"家系可追溯的数据管理方法,实现水稻家系的自动\\n\",\"frame\":[\"499,603\",\"931,603\",\"931,626\",\"499,626\"]},{\"groupID\":10,\"content\":\"中国农业工程学会会员(E041700006M)\\n\",\"frame\":[\"27,621\",\"274,621\",\"274,637\",\"27,637\"]},{\"groupID\":4,\"content\":\"追溯和家系树的自动生成。该研究有效解决了高通\\n\",\"frame\":[\"496,632\",\"931,632\",\"931,655\",\"496,655\"]},{\"groupID\":11,\"content\":\"米通信作者:李小昱(1953一),女(汉),陕西西安,教授,博士生导\\n\",\"frame\":[\"26,639\",\"460,639\",\"460,660\",\"26,660\"]},{\"groupID\":4,\"content\":\"量下育种种植试验的信息量大、管理复杂的问题,\\n\",\"frame\":[\"499,658\",\"922,658\",\"922,682\",\"499,682\"]},{\"groupID\":11,\"content\":\"师,智能化检测技术。武汉湖北省武汉市洪山区华中农业大学工学院,\\n\",\"frame\":[\"25,664\",\"458,664\",\"458,680\",\"25,680\"]},{\"groupID\":12,\"content\":\"430070。Email:lixiaoyu@mail.hzau.edu.cn\\n\",\"frame\":[\"24,686\",\"280,686\",\"280,703\",\"24,703\"]},{\"groupID\":4,\"content\":\"提高了试验效率。对于其他与水稻相类似农作物育\\n\",\"frame\":[\"499,687\",\"931,687\",\"931,712\",\"499,712\"]},{\"groupID\":13,\"content\":\"中国农业工程学会高级会员(E041200068S)\\n\",\"frame\":[\"27,707\",\"297,707\",\"297,723\",\"27,723\"]}],\"success\":1,\"zly\":\"zly\",\"ocr_time\":1584.014892578125,\"id\":\"2274054490548600832\",\"lang\":\"zh-Chs\",\"direction\":0}";
+
+ @Test
+ public void sogouMobileOcr() {
+
+ }
+
+ private Point frameToPoint(String text) {
+ String[] arr = text.split(",");
+ return new Point(Integer.valueOf(arr[0].trim()), Integer.valueOf(arr[1].trim()));
+ }
+
+ @Test
+ public void sogouWebOcr() {
+ GraphicsConfiguration asdf = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
+ AffineTransform asfd2 = asdf.getDefaultTransform();
+ double scaleX = asfd2.getScaleX();
+ double scaleY = asfd2.getScaleY();
+ }
+
+ @Test
+ public void recPdfLocal() {
+ File file = new File("F:\\document\\dev-docs\\24.Internet_of_things\\02_C++\\2.1 面向C++模板库应用开发\\01 第一章C++.pdf");
+ String s = OcrUtils.recPdfLocal(file);
+ System.out.println(s);
+ }
+
+ @Test
+ public void recImageLocal(){
+ OcrUtils.recImgLocal(new File("temp_1010298_4.png"));
+ }
}
diff --git a/src/test/java/com/luooqi/ocr/utils/PdfTest.java b/src/test/java/com/luooqi/ocr/utils/PdfTest.java
new file mode 100644
index 0000000..ea0af09
--- /dev/null
+++ b/src/test/java/com/luooqi/ocr/utils/PdfTest.java
@@ -0,0 +1,28 @@
+package com.luooqi.ocr.utils;
+
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.rendering.PDFRenderer;
+import org.junit.Test;
+
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+public class PdfTest {
+
+ @Test
+ public void extraImageFromPdf() throws IOException {
+ File pdfFile = new File("F:\\document\\dev-docs\\24.Internet_of_things\\02_C++\\2.1 面向C++模板库应用开发\\01 第一章C++.pdf");
+ try (PDDocument document = PDDocument.load(pdfFile)) {
+ PDFRenderer renderer = new PDFRenderer(document);
+
+ for (int i = 0; i < document.getNumberOfPages(); ++i) {
+ BufferedImage bufferedImage = renderer.renderImageWithDPI(i, 300);
+ FileOutputStream fileOutputStream = new FileOutputStream(i + ".png");
+ ImageIO.write(bufferedImage, "png", fileOutputStream); // 选择合适的格式,如 "png" 或 "jpg"
+ }
+ }
+ }
+}