Merge branch 'RED-7669' into 'master'
RED-7669: optimize OCR-module performance Closes RED-7669 See merge request redactmanager/ocr-service!24
This commit is contained in:
commit
65d818200f
@ -55,7 +55,7 @@ public class GhostScriptService {
|
|||||||
Statistics stats) {
|
Statistics stats) {
|
||||||
|
|
||||||
BlockingQueue<RenderedPageImageFile> imageFileCollectorQueue = new LinkedBlockingDeque<>();
|
BlockingQueue<RenderedPageImageFile> imageFileCollectorQueue = new LinkedBlockingDeque<>();
|
||||||
Thread asyncTransferThread = new BlockingQueueFiller(imageFileCollectorQueue, imageProcessingQueue);
|
BlockingQueueFiller asyncTransferThread = new BlockingQueueFiller(imageFileCollectorQueue, imageProcessingQueue);
|
||||||
asyncTransferThread.start();
|
asyncTransferThread.start();
|
||||||
int numOfProcesses = Math.min(settings.getGsProcessCount(), stitchedPageNumbers.size());
|
int numOfProcesses = Math.min(settings.getGsProcessCount(), stitchedPageNumbers.size());
|
||||||
|
|
||||||
@ -86,7 +86,7 @@ public class GhostScriptService {
|
|||||||
stats.increasePDF2ImgDuration(System.currentTimeMillis() - timestamp);
|
stats.increasePDF2ImgDuration(System.currentTimeMillis() - timestamp);
|
||||||
log.info("Batch {}: Ghostscript processes finished with exit codes " + processExitCodes, batchIdx);
|
log.info("Batch {}: Ghostscript processes finished with exit codes " + processExitCodes, batchIdx);
|
||||||
}
|
}
|
||||||
asyncTransferThread.interrupt();
|
asyncTransferThread.setAllImagesQueued(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -94,8 +94,8 @@ public class OcrImageFactory {
|
|||||||
if (!stitchedPageNumbers.isEmpty()) {
|
if (!stitchedPageNumbers.isEmpty()) {
|
||||||
ghostScriptService.renderPagesAsImagesBatchedAndAddToQueue(stitchedPageNumbers, documentFile.toString(), tmpImageDir, document, imageProcessingQueue, stats);
|
ghostScriptService.renderPagesAsImagesBatchedAndAddToQueue(stitchedPageNumbers, documentFile.toString(), tmpImageDir, document, imageProcessingQueue, stats);
|
||||||
}
|
}
|
||||||
imageProcessingThread.interrupt();
|
|
||||||
log.info("All images extracted, interrupting processing thread.");
|
imageProcessingThread.setAllImagesExtracted(true);
|
||||||
|
|
||||||
imageProcessingThread.join();
|
imageProcessingThread.join();
|
||||||
|
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package com.knecon.fforesight.service.ocr.processor.service.threads;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
|
||||||
import com.knecon.fforesight.service.ocr.processor.model.OcrImage;
|
import com.knecon.fforesight.service.ocr.processor.model.OcrImage;
|
||||||
@ -10,6 +11,7 @@ import com.knecon.fforesight.service.ocr.processor.model.UnprocessedImage;
|
|||||||
|
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import lombok.experimental.FieldDefaults;
|
import lombok.experimental.FieldDefaults;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@ -21,41 +23,35 @@ This just moves the Elements from the GhostScriptOutputListener into the ImagePr
|
|||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
|
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||||
public class BlockingQueueFiller extends Thread {
|
public class BlockingQueueFiller extends Thread {
|
||||||
|
|
||||||
BlockingQueue<RenderedPageImageFile> imageInputQueue;
|
final BlockingQueue<RenderedPageImageFile> imageInputQueue;
|
||||||
BlockingQueue<UnprocessedImage> imageOutputQueue;
|
final BlockingQueue<UnprocessedImage> imageOutputQueue;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
boolean allImagesQueued;
|
||||||
|
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
|
||||||
// Interrupting signals that the image extraction has finished
|
// Interrupting signals that the image extraction has finished
|
||||||
while (true) {
|
while (!allImagesQueued) {
|
||||||
try {
|
|
||||||
final UnprocessedImage image = imageInputQueue.take();
|
final UnprocessedImage image = imageInputQueue.take();
|
||||||
try {
|
|
||||||
imageOutputQueue.put(image);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
imageOutputQueue.put(image);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// empty the queue
|
|
||||||
List<UnprocessedImage> remainingImages = new ArrayList<>(imageInputQueue.size());
|
|
||||||
imageInputQueue.drainTo(remainingImages);
|
|
||||||
remainingImages.forEach(image -> {
|
|
||||||
try {
|
|
||||||
imageOutputQueue.put(image);
|
imageOutputQueue.put(image);
|
||||||
} catch (InterruptedException e) {
|
}
|
||||||
log.error(e.getMessage());
|
|
||||||
|
// empty the queue
|
||||||
|
try {
|
||||||
|
while (true) {
|
||||||
|
final UnprocessedImage image = imageInputQueue.remove();
|
||||||
|
imageOutputQueue.put(image);
|
||||||
}
|
}
|
||||||
});
|
} catch (NoSuchElementException e) {
|
||||||
|
log.debug("No images left in queue, stopping.");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import java.nio.FloatBuffer;
|
|||||||
import java.nio.IntBuffer;
|
import java.nio.IntBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
@ -24,6 +25,7 @@ import com.sun.jna.ptr.PointerByReference;
|
|||||||
|
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import lombok.experimental.FieldDefaults;
|
import lombok.experimental.FieldDefaults;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@ -37,48 +39,40 @@ import net.sourceforge.tess4j.TessAPI1;
|
|||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
|
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||||
public class ImageProcessingThread extends Thread {
|
public class ImageProcessingThread extends Thread {
|
||||||
|
|
||||||
BlockingQueue<UnprocessedImage> imageInputQueue;
|
final BlockingQueue<UnprocessedImage> imageInputQueue;
|
||||||
BlockingQueue<OcrImage> imageOutputQueue;
|
final BlockingQueue<OcrImage> imageOutputQueue;
|
||||||
ITessAPI.TessBaseAPI detectionScriptHandle = initDetectionScriptHandle();
|
final ITessAPI.TessBaseAPI detectionScriptHandle = initDetectionScriptHandle();
|
||||||
Statistics stats;
|
final Statistics stats;
|
||||||
OcrServiceSettings settings;
|
final OcrServiceSettings settings;
|
||||||
PDDocument document;
|
final PDDocument document;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
boolean allImagesExtracted;
|
||||||
|
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
|
||||||
// Interrupting signals that the image extraction has finished
|
while (!allImagesExtracted) {
|
||||||
while (true) {
|
final UnprocessedImage image = imageInputQueue.take();
|
||||||
try {
|
var ocrImage = this.process(image);
|
||||||
final UnprocessedImage image = imageInputQueue.take();
|
imageOutputQueue.put(ocrImage);
|
||||||
OcrImage extractedOcrImage = this.process(image);
|
|
||||||
try {
|
|
||||||
imageOutputQueue.put(extractedOcrImage);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
imageOutputQueue.put(extractedOcrImage);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// empty the queue
|
|
||||||
List<UnprocessedImage> remainingImages = new ArrayList<>(imageInputQueue.size());
|
|
||||||
imageInputQueue.drainTo(remainingImages);
|
try {
|
||||||
remainingImages.forEach(image -> {
|
while (true) {
|
||||||
OcrImage ocrImage = this.process(image);
|
final UnprocessedImage image = imageInputQueue.remove();
|
||||||
try {
|
OcrImage ocrImage = this.process(image);
|
||||||
imageOutputQueue.put(ocrImage);
|
imageOutputQueue.put(ocrImage);
|
||||||
} catch (InterruptedException e) {
|
|
||||||
log.error(e.getMessage());
|
|
||||||
}
|
}
|
||||||
});
|
} catch (NoSuchElementException e) {
|
||||||
|
log.debug("No images left in processing queue, stopping.");
|
||||||
|
}
|
||||||
|
|
||||||
TessAPI1.TessBaseAPIDelete(this.detectionScriptHandle);
|
TessAPI1.TessBaseAPIDelete(this.detectionScriptHandle);
|
||||||
}
|
}
|
||||||
@ -150,9 +144,7 @@ public class ImageProcessingThread extends Thread {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int detectOrientation(Pix pix, int dpi, ITessAPI.TessBaseAPI detectionScriptHandle) {
|
||||||
|
|
||||||
static public int detectOrientation(Pix pix, int dpi, ITessAPI.TessBaseAPI detectionScriptHandle) {
|
|
||||||
|
|
||||||
TessAPI1.TessBaseAPISetImage2(detectionScriptHandle, pix);
|
TessAPI1.TessBaseAPISetImage2(detectionScriptHandle, pix);
|
||||||
TessAPI1.TessBaseAPISetSourceResolution(detectionScriptHandle, dpi);
|
TessAPI1.TessBaseAPISetSourceResolution(detectionScriptHandle, dpi);
|
||||||
@ -173,7 +165,7 @@ public class ImageProcessingThread extends Thread {
|
|||||||
orientationDegreeConfidenceBuffer,
|
orientationDegreeConfidenceBuffer,
|
||||||
scriptureNameBuffer,
|
scriptureNameBuffer,
|
||||||
scriptureConfidenceBuffer);
|
scriptureConfidenceBuffer);
|
||||||
if (result == TRUE && orientationDegreeConfidenceBuffer.get() > 10) {
|
if (result == TRUE && orientationDegreeConfidenceBuffer.get() > settings.getMinRotationConfidence()) {
|
||||||
orientationDegree = orientationDegreeResultBuffer.get();
|
orientationDegree = orientationDegreeResultBuffer.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,7 +190,7 @@ public class ImageProcessingThread extends Thread {
|
|||||||
ITessAPI.TessBaseAPI handle = TessAPI1.TessBaseAPICreate();
|
ITessAPI.TessBaseAPI handle = TessAPI1.TessBaseAPICreate();
|
||||||
String datapath = System.getenv("TESSDATA_PREFIX");
|
String datapath = System.getenv("TESSDATA_PREFIX");
|
||||||
TessAPI1.TessBaseAPIInit3(handle, datapath, "osd");
|
TessAPI1.TessBaseAPIInit3(handle, datapath, "osd");
|
||||||
|
TessAPI1.TessBaseAPISetVariable(handle, "debug_file", "/dev/null");
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -19,6 +19,7 @@ public class OcrServiceSettings {
|
|||||||
int psmOverride = -1; // Overrides the page segmentation mode if > 0
|
int psmOverride = -1; // Overrides the page segmentation mode if > 0
|
||||||
int minImageHeight = 20; // Minimum height for images to be processed
|
int minImageHeight = 20; // Minimum height for images to be processed
|
||||||
int minImageWidth = 20; // Minimum width for images to be processed
|
int minImageWidth = 20; // Minimum width for images to be processed
|
||||||
|
float minRotationConfidence = 2; //
|
||||||
boolean debug; // If true, overlays OCR images with a grid and draws word bounding boxes
|
boolean debug; // If true, overlays OCR images with a grid and draws word bounding boxes
|
||||||
boolean removeWatermark; // If true, watermarks will be removed
|
boolean removeWatermark; // If true, watermarks will be removed
|
||||||
String languages = "deu+eng"; // Defines languages loaded into Tesseract as 3-char codes, additional languages must also be installed in the docker environment
|
String languages = "deu+eng"; // Defines languages loaded into Tesseract as 3-char codes, additional languages must also be installed in the docker environment
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user