jadice web toolkit hOCR Addon

Gültig ab jadice web toolkit 5.11.x.x

Dieser Artikel beschreibt die Nutzung eines neu geschaffenen Add-Ons zur OCR von Dokumenten und Teilbereichen eines Dokuments.

Überblick

Das jadice web toolkit hOCR Addon beinhaltet Funktionen, um optische Zeichenerkennung auf Dokumenten oder Dokumentbereichen durchzuführen. Ziel ist hierbei, den erkannten Text dem Benutzer in Form eines Dialogs zur Verfügung zu stellen (OCR). Alternativ kann das hOCR Layer im jadice web toolkit geladen werden, um den erkannten Text direkt im Dokument selektierbar zu machen.

Es werden Standardfunktionen angeboten, mithilfe von verfügbaren Utility-Klassen können jedoch auch weitere Szenarien abgebildet werden. Die folgenden Anwendungsfälle gibt es:

  • OCRServerOperation: Vorgefertigte Commands, um über eine ServerOperation OCR Operationen auszulösen:
    • OCR für einen selektierten Bereich ermitteln: Der Benutzer kann einen Bereich aufziehen/markieren und diesen dann an den OCR Service übergeben. Das Ergebnis wird in einem Dialog dargestellt.
    • OCR für die aktuelle Seite ermitteln: Ähnlich dem selektierten Bereich wird hier für die aktuelle Seite OCR durchgeführt und das Ergebnis in einem Dialog dargestellt.
    • OCR für das Dokument: Hier wird der komplette Text des Dokuments in einem Dialog dargestellt
  • Mithilfe der Server-seitigen Klasse OCRService können auch in einer DocumentDataProvider Implementierung direkt OCR Daten angereichert werden

Es gibt hierzu für die Integration verschiedene Einstiegspunkte, um z.B. einen eigenen Dialog für die Anzeige zu verwenden.


Voraussetzungen

Die Erkennung wird über jadice flow (="jadice flow 6") durchgeführt. Folgendes wird benötigt:

  • Eine vorhandene jadice flow Instanz mit OCR Cloud Worker und ein S3 Storage für temporäre Daten (Input-Bilder, OCR Ergebnisse)
  • Aktuell müssen im jadice web toolkit 3 Variablen entweder als System-Property in der JVM oder in der application.yml zur Verfügung gestellt werden:
    • webtoolkit.flow.director-url → URL zum jadice flow Director Client für die Übermittlung der OCR Aufträge
    • webtoolkit.flow.s3-proxy-url → URL für den S3 Storage Proxy für die Speicherung der Bilddaten
    • webtoolkit.flow.auth-token → Das jadice flow Security Token
  • Alternativ können diese 3 Variablen auch über die Klasse JadiceFlowConfiguration programmatisch gesetzt werden. Dazu später mehr.


Maven Dependencies und GWT Modul Descriptor

Anpassungen an der POM.xml:

POM.xml
<dependency>
   <groupId>com.levigo.jadice.webtoolkit</groupId>
   <artifactId>webtoolkit-addon-hocr</artifactId>
</dependency>

Die Version wird hier automatisch über die importierte Maven BOM des jadice web toolkits ermittelt. Falls die jadice web toolkit-BOM nicht importiert wird, kann die Version hier mit der Version des jadice web toolkit angegeben werden.


Anpassungen Modul-Descriptor .gwt.xml:

.gwt.xml
<inherits name="com.levigo.jadice.web.addon.ocr.AddonHOCR" />

Diese Abhängigkeit im Modul-Descriptor wird benötigt, wenn die Client-Seitigen Funktionen verwendet werden sollen.


Überblick über die vorhandenen Komponenten

Server-Seite:

  • OCRServerOperation: Diese ServerOperation wird für Anfragen für OCR Requests vom Client verwendet
  • OCRService: Die OCRServerOperation verwendet den OCRService für den eigentlichen OCR Vorgang. Der OCRService ist auch an anderen Stellen, z.B. in einer DocumentDataProvider-Implementierung nutzbar. Dazu später mehr.

Client-Seite:

  • OCRDefaultButtons: Die OCR Standardfunktionen (verwenden die OCRServerOperation)
  • DefaultActions: (nicht Bestandteil dieses Addons, sondern bereits mit den jadice webtoolkit client mitgeliefert): Über DefaultActions#startAreaSelectionAction() kann ein Standard-Knopf für die Bereichsselektion hinzugefügt werden.


AreaSelectionTool

Das bereits im Standardumfang enthaltene AreaSelectionTool erlaubt die Bereichsselektion. Dem Tool können Commands hinzugefügt werden, die nach der Bereichsselektion ausgeführt werden sollen.

Für dieses Tool bietet das hOCR Addon eine Aktion an, um auf einem Bereich nach der Selektion die Bilderkennung durchzuführen.

Verwendung AreaSelectionTool

Falls Bereiche für OCR ausgewählt werden sollen, muss das AreaSelectionTool im ToolManager registriert sein.

AreaSelectionTool ast = toolManager.register(AreaSelectionTool.class, false);
ast.addRegisteredActions(new OCRAction());

Hier zu sehen:

Das AreaSelectionTool wird immer deaktiviert hinzugefügt (hier: false). Es wird die OCRAction hinzugefügt. Diese Aktion ist speziell für OCR für den selektierten Bereich ausgelegt.

Erst durch das StartAreaSelectionCommand wird das Tool aktiviert und damit der Bereich aufgezogen. Sobald der Bereich aufgezogen wurde, erscheint eine Toolbar, in der der Benutzer die gewünschte Aktion auswählen kann.

Für dieses Start-Command gibt es bereits ein Standard-Kommando:

toolbar.add(new JadiceDefaultButton(DefaultActions.startAreaSelectionAction(keyStroke, context), showLabel));


Es ist ebenfalls möglich, direkt die OCR-Operation auszuführen, ohne zuvor dem Benutzer die Toolbar anzuzeigen. Siehe hierzu den folgenden Abschnitt.


Standardaktionen / Kommandos

Die Klasse OCRDefaultButtons bietet die OCR Standardfunktionen als Actions an, um diese direkt einer Toolbar hinzufügen zu können:

OCRDefaultButtons
toolbar.add(new JadiceDefaultButton(OCRDefaultButtons.ocrDocumentAction(keyStroke, context), showLabel));
toolbar.add(new JadiceDefaultButton(OCRDefaultButtons.ocrPageAction(keyStroke, context), showLabel));
toolbar.add(new JadiceDefaultButton(OCRDefaultButtons.ocrAreaAction(keyStroke, context), showLabel));

Die zugehörigen Standard-Commands sind:

  • Bei der ocrDocumentAction: OCRDocumentCommand - führt OCR für das Dokument aus
  • Bei der ocrPageAction: OCRPageCommand - führt OCR für die Seite aus
  • Bei der ocrAreaAction: StartOCRAreaCommand - startet direkt die Bereichsselektion und führt sofort OCR aus, ohne zunächst die Toolbar mit den registrierten Funktionen des AreaSelectionTools anzuzeigen.

Die Standard-Commands zeigen das Ergebnis in einem Dialog dem Benutzer an. Falls ein anderes Verhalten gewünscht ist, können die bestehenden Commands erweitert werden.

Die Commands können auch in eine eigene Action integriert werden, falls z.B. ein anderes Icon für die Funktion gewünscht ist.


Anpassung des Ergebnis-Dialogs

Wenn ein OCR-Ergebnis über das OCRPageCommand oder StartOCRAreaCommand erhalten wird, wird das Ergebnis in einer Text-Annotation auf dem Dokument angebracht. Sofern hier ein angepasster Dialog gewünscht ist, kann eine Unterklasse des entsprechenden Kommands erzeugt werden. Dort ist folgende Methode zu implementieren:

protected void showOCRResultPopup(OCRData ocrData, SelectedArea area, PageView pageView) {
...
}

&

protected void addAnnotation(String textContent, SelectedArea selectedArea, String annotationProfile) {
...
}

Aus dem OCRData - Objekt kann man den reinen Text oder auch die angereicherten hOCR Informationen (XML) erhalten.


Klasse OCRServerOperation

Die OCRServerOperation wird bei der Verwendung von Spring automatisch registriert.

Hierbei werden die Standard-OCRReaderSettings der ServerOperation verwendet. Alternativ können via 

@Autowired
OCRServerOperation ocrServerOperation;

ocrServerOperation.setOcrReaderSettings(...)

Die Einstellungen übergeben werden. Diese OCRReaderSettings werden dann bei der Verarbeitung von Seiten / Dokument-Requests verwendet.

Klasse OCRService

Der OCRService ist der serverseitige Einstiegspunkt für OCR Operationen. Die OCRServerOperation nutzt Funktionen des OCRService.

Dieser Service verwendet einen internen ThreadPool, um die Anzahl an parallelen OCR Requests gegen jadice flow zu begrenzen. Wenn mehr OCR Anfragen eintreffen, werden diese zunächst in der Queue abgelegt und später vom Executor verarbeitet.


Mithilfe des OCRService kann z.B. direkt innerhalb eines DocumentDataProviders OCR durchgeführt werden. Beispiel:

Beispiel-Einbindung in DocumentDataProvider
Reader r = ...;
r.read(documentStream);

try {
  OCRReaderSettings ocrSettings = reader.getSettings(OCRReaderSettings.class);
  // Modify settings, if desired
  OCRService.readHOCRLayerForDocument(reader);
} catch (Exception e) {
  // TODO: handle exception
  e.printStackTrace();
}

Es wird also direkt nach dem normalen Reader.read()-Aufruf der OCRService aufgerufen. Aus Benutzer-Sicht erscheint das Dokument wie gewohnt sofort - die OCR Informationen (selektierbarer Text) wird verfügbar, sobald der OCR Vorgang abgeschlossen ist.


Falls keine Benutzerkonfiguration zur weiteren Einschränkung zur Verfügung steht, wird eben potentiell für jedes Bilddokument OCR durchgeführt und das Ergebnis im Hintergrundlayer zur Textselektion bereitgestellt.

Sollte dies nicht gewünscht sein, sollte die Implementierung der OCR Funktion zunächst nicht im DataProvider, sondern über die Standard-OCR Commands erfolgen. Diese Commands werden ja immer nur vom Benutzer direkt ausgelöst und das Ergebnis als Dialog angezeigt.

Document Filter

In der Klasse OCRServiceConfiguration kann ein DocumentFilter gesetzt werden. Der Filter wird verwendet, um die Dokumente zu filtern, für die überhaupt OCR durchgeführt werden soll.

(Warnung) Zu beachten:

Wenn dies so wie oben im Beispiel im DocumentDataProvider angegeben ist, würde zunächst jedes Dokument an die Methode "OCRService.readHOCRLayerForDocument" übergeben. Damit nicht unnötig viele OCR-Operationen stattfinden, kann auf der OCRServiceConfiguration der DocumentFilter gesetzt werden. Der vorhandene Standardfilter lässt nur Dokumente für OCR zu, die keinen Text über den TextContentService liefern.

Somit werden hier z.B. keine MO:DCA oder PDF Dateien verarbeitet, die bereits Text liefern.

In einer eigenen Filter-Implementierung könnten noch weitere Kriterien geprüft werden, z.B. die Benutzerkonfiguration, falls verfügbar.

Klasse JadiceFlowConfiguration

Diese Klasse steuert den grundlegenden Zugriff auf jadice flow:

  • authToken - Das jadice flow Security Token
  • directorUrl - URL zum jadice flow Director Client
  • s3ProxyUrl - URL zum S3 Storage Proxy
  • requestTimeout - Standard Timeout für OCR Abfragen (Default 10 Sekunden)

ServerConfiguration

Über die ServerConfiguration wird der bereits erwähnte Thread-Pool für Anfragen zu jadice flow konfiguriert

  • flowWorkerPoolCoreSize - (Default CPUs * 2)
  • flowWorkerPoolMaxSize - (Default CPUs * 2)
  • flowWorkerMaxQueueSizeActive - (Default 200)

Klasse OCRServiceConfiguration

Diese Klasse steuert die grundlegenden OCRService Einstellungen:

  • workerName - Name des jadice flow workers (Default "ocr") 
  • maxPageCountForParallelRequest - Maximale Anzahl der parallelen OCR-Requests (Default 10)
  • documentFilter - Der angesprochene Dokument Filter, um die für OCR relevanten Dokumente herauszufiltern. Standardmäßig werden nur Dokumente zugelassen, die nicht bereits schon Text liefern (z.B. PDF,...) (Default nur Dokumente ohne Text-Layer)


Die Konfiguration kann wie folgt vorgenommen werden

@Autowired
OCRServiceConfiguration ocrServiceConfiguration;
@Autowired
JadiceFlowConfiguration jadiceFlowConfiguration;

...


ocrServiceConfiguration.set...



OCRReaderSettings

Diese Konfigurationsklasse steuert bei OCR Operationen für ein Dokument bzw eine Seite die Konvertierungsparameter (bei Seitenausschnitten kommen sie nicht zum Einsatz). Die aktuellen Einstellungen umfassen:

  • maxImageResolution - die maximale Auflösung der Bilddaten, die an den jadice flow übergeben werden. Bilder werden ggf heruntergerechnet, um nicht zu große Datenmengen zu übertragen
  • compression - die ImageCompression ist Standardmäßig JPG, kann aber z.B. auf Graustufen (CCITT) umkonfiguriert werden
  • documentStrategy - Die Dokument-Strategie kennt 2 Werte:
    • SPLIT_INTO_PAGE_REQUESTS: Ein Dokument wird in Einzelseiten zerlegt und pro Seite wird eine OCR Anfrage an jadice flow gestellt
    • TREAT_AS_ONE_REQUEST: Ein Dokument wird zwar ebenfalls intern zerlegt - es läuft aber für dieses Dokument nur ein "paralleler" Request (die Seiten werden sequentiell verarbeitet). Im Rahmen von JWT-3530 wird nochmal der Aspekt beleuchtet, ob das Dokument nicht zerteilt werden muss und direkt als Multipage-Dokument an jadice flow übergeben werden kann.

Die Klasse OCRServerOperation bietet, wie bereits im entsprechenden Abschnitt erwähnt, Standard OCRReaderSettings, die dort geändert werden können. Bei Verwendung des OCRService können in den betroffenen Methoden die Settings direkt angegeben werden, falls gewünscht.


Die OCR Funktionen sind aktuell noch recht neu. Es ist wahrscheinlich, dass hier künftig noch weitere Parameter für die Steuerung hinzukommen.

Integration

Um nun in einer Integration zu beginnen, sind die bereits erwähnten Voraussetzungen zu schaffen:

  • Erreichbarer jadice flow
  • Maven-Abhängigkeit + GWT Modul konfiguriert

Nun folgen die nächsten Schritte.

Bei Verwendung der Client-Seitigen Funktionen:

  • Einer Toolbar eine OCR Funktion hinzufügen


Und / oder bei Implementierung direkt im DataProvider:

  • DataProvider Implementierung anpassen und den OCRService darin aufrufen, wie im Abschnitt OCRService erwähnt


Weitere Einsatzbeispiele

Ein weiteres Beispiel, um mehr Kontrolle über die hOCR Daten zu erhalten (Cache). In einem Vor-Verarbeitungsschritt die hOCR-Daten ermitteln und ggf. in einem separaten Cache lagern. Anschließend das Dokument mitsamt den hOCR-Daten laden.


Hierbei wäre folgende Vorgehensweise denkbar:

  • Im DocumentDataProvider wie bisher das Dokument laden
  • z.B. anhand einer Prüfsumme des Datenstroms oder Dokument-ID die hOCR Daten aus einem externen Cache anziehen
  • Nur falls nicht vorhanden: OCR durchführen und das Ergebnis im Cache ablegen (das hOCR XML ist aus dem OCRData-Ergebnis vom OCRService abrufbar).

Falls hOCR-Daten (XML) direkt vorliegen, kann man diese auch sofort laden (hier mal als nicht optimierter Pseudo-Code):

Beispiel OCR Cache (Pseudo-Code)
Reader reader = new Reader();
reader.read(documentStream);

reader.setFormat(new HocrFormat());
// First retrieve from cache or fill cache if no entry exists
Document doc = reader.getDocument();
OCRResult cachedOCRResult = getOCRResultFromCache(doc);
if (cachedOCRResult == null) {
  cachedOCRResult = OCRResult.awaitResults(OCRService.get().ocrForDocument(doc), timeoutDuration, timeUnit);
  putResultToCache(doc, cachedOCRResult);
}
// For each base page segment id from the key set, add the HOCR data
for (String bpsID : cachedOCRResult.getPageResultMap().keySet()) {
  OCRData hocr = cachedOCRResult.getPageResultMap().get(bpsID);
  Page page = SelectedArea.findPageForBasePageSegmentUUID(doc, bpsID);
  if (null != hocr.getHocr() && page != null) {
	if (hocr.getDesiredHocrResolution() != null) {
	  reader.getSettings(HocrReaderSettings.class).setResolutionDetector(new ResolutionDetector() {
		@Override
		public Resolution detectResolution() {
		  return new Resolution(hocr.getDesiredHocrResolution());
		}
	  });
	}
	reader.setTargetIndex(doc.getPageIndex(page));
	reader.read(new ByteArrayInputStream(hocr.getHocr().getBytes()));
  }
}

Es ist theoretisch möglich, einen hOCR XML Datenstrom mit mehreren Seiten zu haben und diesen auf PageIndex 0 zu setzen. Alternativ können einzelne hOCR Datenströme auch pro Seite mit dem jeweiligen Index angebracht werden. Die hier dargestellte Logik kann mit beiden Fällen umgehen.


Bei Ausführung der normalen Standard-OCR Commands für ein Dokument wird OCR immer für Einzelseiten durchgeführt, da Dokumente unter Umständen gefiltert sein können und nicht alle Seiten beinhalten. Dies ist auch der Grund, warum nicht der PageIndex, sondern die PageSegmentID des basis-Layers verwendet wird, um bei gefilterten Seiten die korrekte Zuordnung treffen zu können.


Aktuell sollte bei einer Cache-Implementierung bei der Speicherung noch eine Ermittlung des Page-Index erfolgen (via "SelectedArea.findPageForBasePageSegmentUUID(doc, bpsID)") und dann dieser gespeichert werden. Die Klasse OCRData beinhaltet die eigentlichen Daten und ist prinzipiell serialisierbar. 


Aktuelle Einschränkungen / Blick in die Zukunft

  • Aktuell wird noch keine "Confidence", also Erkennungssicherheit des OCR Ergebnisses angewendet bzw. ausgewertet. Hier müssen wir zunächst auch noch weitere Erkenntnisse sammeln und dann z.B. über konfigurierbare Schwellwerte eine Steuerung ermöglichen.
  • "Generator-API", um auch die Hintergrundinformationen (hOCR Layer) zur Laufzeit (unabhängig vom DocumentDataProvider) dynamisch nachladen zu können
  • Die Einstellungen für das OCR Bild (OCRReaderSettings) werden ggf. noch erweitert um weitere Optionen; allerdings müssen auch hier zunächst noch weitere Erkenntnisse gesammelt werden, mit welchen Optionen bei welchen ImageTypen eine besonders gute Zeichenerkennung möglich ist. Dies ist stellenweise leider nicht generell festzulegen.
  • Optimierungen des OCR Workers in jadice flow (z.B. weitere OCR Engines einbinden und nur bestes Ergebnis zurückliefern)