Compare commits

..
Author SHA1 Message Date
Dominique Eifländer 8924e905ad hotfix: Extend Tesseract instead of Tesseract1 2024-01-15 16:13:43 +01:00
11 changed files with 56 additions and 35 deletions
@@ -12,10 +12,6 @@ group = "com.knecon.fforesight.service"
java.sourceCompatibility = JavaVersion.VERSION_17
java.targetCompatibility = JavaVersion.VERSION_17
pmd {
isConsoleOutput = true
}
tasks.pmdMain {
pmd.ruleSetFiles = files("${rootDir}/config/pmd/pmd.xml")
}
+4 -3
View File
@@ -9,11 +9,12 @@
</description>
<rule ref="category/java/errorprone.xml">
<exclude name="DataflowAnomalyAnalysis"/>
<exclude name="MissingSerialVersionUID"/>
<exclude name="NullAssignment"/>
<exclude name="AvoidLiteralsInIfCondition"/>
<exclude name="AvoidDuplicateLiterals"/>
<exclude name="NullAssignment"/>
<exclude name="AssignmentInOperand"/>
<exclude name="AvoidFieldNameMatchingMethodName"/>
</rule>
</ruleset>
</ruleset>
+5 -4
View File
@@ -10,13 +10,14 @@
<rule ref="category/java/errorprone.xml">
<exclude name="DataflowAnomalyAnalysis"/>
<exclude name="MissingSerialVersionUID"/>
<exclude name="NullAssignment"/>
<exclude name="AvoidLiteralsInIfCondition"/>
<exclude name="AvoidDuplicateLiterals"/>
<exclude name="NullAssignment"/>
<exclude name="AssignmentInOperand"/>
<exclude name="AvoidFieldNameMatchingMethodName"/>
<exclude name="AvoidFieldNameMatchingTypeName"/>
<exclude name="TestClassWithoutTestCases"/>
<exclude name="BeanMembersShouldSerialize"/>
</rule>
</ruleset>
</ruleset>
@@ -24,6 +24,6 @@ dependencies {
api("io.github.karols:hocr4j:0.2.0")
api("com.amazonaws:aws-java-sdk-kms:1.12.440")
api("com.google.guava:guava:31.1-jre")
api("com.iqser.red.commons:pdftron-logic-commons:2.23.0")
api("com.iqser.red.commons:pdftron-logic-commons:2.20.0")
testImplementation("org.junit.jupiter:junit-jupiter:5.8.1")
}
@@ -28,8 +28,6 @@ import com.knecon.fforesight.service.ocr.processor.service.scriptdetection.FontS
import com.knecon.fforesight.service.ocr.processor.service.threads.OCRThread;
import com.knecon.fforesight.service.ocr.processor.settings.OcrServiceSettings;
import io.micrometer.observation.ObservationRegistry;
import io.micrometer.observation.annotation.Observed;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
@@ -50,7 +48,6 @@ public class OCRService {
OcrResultWriter ocrResultWriter;
GhostScriptService ghostScriptService;
FontStyleDetector boldDetector;
ObservationRegistry registry;
/**
@@ -62,13 +59,12 @@ public class OCRService {
* @param fileId Id of file
* @param out OutputStream where to write to
*/
@Observed(name = "OCRService", contextualName = "run-ocr-on-document")
@SneakyThrows
public void runOcrOnDocument(String dossierId, String fileId, OutputStream out) {
try (InputStream fileStream = removeWatermarkIfEnabled(dossierId, fileId); ByteArrayOutputStream transferOutputStream = new ByteArrayOutputStream()) {
invisibleElementRemovalService.removeInvisibleElements(fileStream, transferOutputStream, false, false);
invisibleElementRemovalService.removeInvisibleElements(fileStream, transferOutputStream, false);
try (InputStream transferInputStream = new ByteArrayInputStream(transferOutputStream.toByteArray())) {
log.info("Starting OCR for file {}", fileId);
@@ -109,6 +109,7 @@ public class GhostScriptOutputHandler extends Thread {
if (imageFile == null) {
throw new IllegalArgumentException(String.format("Page number %d does not exist in this thread. It only has pagenumbers %s", pageNumber, pagesToProcess.keySet()));
}
assert new File(imageFile.absoluteFilePath()).isFile();
renderedPageImageFileOutput.add(imageFile);
}
@@ -198,10 +198,8 @@ public class ImageProcessingThread extends Thread {
grayScale = pix;
} else if (pix.d == 32) {
grayScale = Leptonica1.pixConvertRGBToGrayFast(pix);
LeptUtils.disposePix(pix);
} else if (pix.d == 1) {
grayScale = Leptonica1.pixConvert1To8(null, pix, (byte) 0, (byte) 255);
LeptUtils.disposePix(pix);
} else {
throw new UnsupportedOperationException(String.format("Unknown pix format with bpp of %d", pix.d));
}
@@ -210,27 +208,29 @@ public class ImageProcessingThread extends Thread {
float targetFactor = targetDpi / imageDpi;
if (targetFactor > 2.1) {
scaledUp = Leptonica1.pixScaleGray4xLI(grayScale);
LeptUtils.disposePix(grayScale);
} else if (targetFactor > 1.1) {
scaledUp = Leptonica1.pixScaleGray2xLI(grayScale);
LeptUtils.disposePix(grayScale);
} else {
scaledUp = grayScale;
}
// remove noise and prep for Otsu
gaussian = Leptonica1.pixConvolve(scaledUp, gaussianKernel, 8, 1);
LeptUtils.disposePix(scaledUp);
// Threshold to binary
if (pix.w < 100 || pix.h < 100) {
binarized = Leptonica1.pixThresholdToBinary(gaussian, 170);
} else {
binarized = Leptonica1.pixOtsuThreshOnBackgroundNorm(gaussian, null, 50, 50, 165, 10, 100, 5, 5, 0.2f, null);
if (binarized == null) { // Sometimes Otsu just fails, then we binarize directly
binarized = Leptonica1.pixThresholdToBinary(gaussian, 170);
}
}
LeptUtils.disposePix(pix);
LeptUtils.disposePix(grayScale);
LeptUtils.disposePix(scaledUp);
LeptUtils.disposePix(gaussian);
return binarized;
@@ -1,5 +1,25 @@
package com.knecon.fforesight.service.ocr.processor.utils;
import static net.sourceforge.tess4j.ITessAPI.TRUE;
import static net.sourceforge.tess4j.TessAPI1.TessBaseAPIDelete;
import static net.sourceforge.tess4j.TessAPI1.TessBaseAPIEnd;
import static net.sourceforge.tess4j.TessAPI1.TessBaseAPIGetIterator;
import static net.sourceforge.tess4j.TessAPI1.TessBaseAPIGetStringVariable;
import static net.sourceforge.tess4j.TessAPI1.TessBaseAPIMeanTextConf;
import static net.sourceforge.tess4j.TessAPI1.TessBaseAPIProcessPage;
import static net.sourceforge.tess4j.TessAPI1.TessDeleteResultRenderer;
import static net.sourceforge.tess4j.TessAPI1.TessHOcrRendererCreate;
import static net.sourceforge.tess4j.TessAPI1.TessPageIteratorBegin;
import static net.sourceforge.tess4j.TessAPI1.TessPageIteratorBoundingBox;
import static net.sourceforge.tess4j.TessAPI1.TessPageIteratorNext;
import static net.sourceforge.tess4j.TessAPI1.TessResultIteratorConfidence;
import static net.sourceforge.tess4j.TessAPI1.TessResultIteratorDelete;
import static net.sourceforge.tess4j.TessAPI1.TessResultIteratorGetPageIterator;
import static net.sourceforge.tess4j.TessAPI1.TessResultIteratorGetUTF8Text;
import static net.sourceforge.tess4j.TessAPI1.TessResultRendererBeginDocument;
import static net.sourceforge.tess4j.TessAPI1.TessResultRendererEndDocument;
import static net.sourceforge.tess4j.TessAPI1.TessResultRendererInsert;
import java.awt.Rectangle;
import java.nio.IntBuffer;
import java.util.ArrayList;
@@ -9,20 +29,19 @@ import com.sun.jna.Pointer;
import lombok.extern.slf4j.Slf4j;
import net.sourceforge.lept4j.Pix;
import net.sourceforge.tess4j.ITessAPI;
import net.sourceforge.tess4j.OCRResult;
import net.sourceforge.tess4j.TessAPI1;
import net.sourceforge.tess4j.Tesseract1;
import net.sourceforge.tess4j.Tesseract;
import net.sourceforge.tess4j.TesseractException;
import net.sourceforge.tess4j.Word;
@Slf4j
/**
* Overriden version only so I can use Tesseract1 with Pixs instead of BufferedImages. All Functions are copied and then the BufferedImage -> Pix conversion deleted.
*/
public class Tesseract2 extends Tesseract1 {
*/ public class Tesseract2 extends Tesseract {
private int createDocuments(Pix pix, String filename, TessResultRenderer renderer) {
private int createDocuments(Pix pix, String filename, ITessAPI.TessResultRenderer renderer) {
String title = TessBaseAPIGetStringVariable(getHandle(), DOCUMENT_TITLE);
TessResultRendererBeginDocument(renderer, title);
@@ -62,7 +81,7 @@ public class Tesseract2 extends Tesseract1 {
try {
for (int i = 0; i < pixs.length; i++) {
try {
TessResultRenderer renderer = createRenderers(outputbases[i], formats);
ITessAPI.TessResultRenderer renderer = createRenderers(outputbases[i], formats);
int meanTextConfidence = createDocuments(pixs[i], filenames[i], renderer);
TessDeleteResultRenderer(renderer);
List<Word> words = meanTextConfidence > 0 ? getRecognizedWords(pageIteratorLevel) : new ArrayList<Word>();
@@ -85,8 +104,8 @@ public class Tesseract2 extends Tesseract1 {
List<Word> words = new ArrayList<>();
try {
TessResultIterator ri = TessBaseAPIGetIterator(getHandle());
TessPageIterator pi = TessResultIteratorGetPageIterator(ri);
ITessAPI.TessResultIterator ri = TessBaseAPIGetIterator(getHandle());
ITessAPI.TessPageIterator pi = TessResultIteratorGetPageIterator(ri);
TessPageIteratorBegin(pi);
do {
@@ -119,9 +138,9 @@ public class Tesseract2 extends Tesseract1 {
}
private TessResultRenderer createRenderers(String outputbase, List<RenderedFormat> formats) {
private ITessAPI.TessResultRenderer createRenderers(String outputbase, List<RenderedFormat> formats) {
TessResultRenderer renderer = null;
ITessAPI.TessResultRenderer renderer = null;
for (RenderedFormat format : formats) {
switch (format) {
@@ -138,4 +157,12 @@ public class Tesseract2 extends Tesseract1 {
return renderer;
}
@Override
protected void dispose() {
TessBaseAPIEnd(getHandle());
TessBaseAPIDelete(getHandle());
}
}
@@ -21,7 +21,6 @@ import com.knecon.fforesight.service.ocr.v1.api.model.DocumentRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileErrorInfo;
import feign.FeignException;
import io.micrometer.observation.annotation.Observed;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
@@ -6,7 +6,7 @@
"overrides": [
{
"name": "tesseract",
"version": "5.3.3"
"version": "5.3.2"
},
{
"name": "leptonica",
@@ -64,7 +64,7 @@ public class OcrServiceIntegrationTest extends AbstractTest {
@SneakyThrows
public void testOcr() {
String text = testOCR("files/UNAPPROVED_VV-331155 (1).pdf");
String text = testOCR("files/402Study.pdf");
}