3.29. Texte - in Bildern (OCR)

Überblick

PDFUnit kann Text aus Bildern extrahieren und diesen Text auf die gleiche Weise wie Fließtext validieren. Wie in anderen PDFUnit-Tests entspricht die Syntax dieser OCR-Tests weitestgehend der Umgangssprache: jeder OCR-Test beginnt mit den Methoden hasImage().withText() bzw. hasImage().withTextInRegion(). Mit folgenden Methoden können Texte in Bildern validiert werden:

// Tests for text in images:
.hasImage().withText().containing(..)
.hasImage().withText().endingWith(..)
.hasImage().withText().equalsTo(..)
.hasImage().withText().matchesRegex(..)
.hasImage().withText().startingWith(..)

// Tests for text in parts of an image:
.hasImage().withTextInRegion(imageRegion).containing(..)
.hasImage().withTextInRegion(imageRegion).endingWith(..)
.hasImage().withTextInRegion(imageRegion).equalsTo(..)
.hasImage().withTextInRegion(imageRegion).matchesRegex(..)
.hasImage().withTextInRegion(imageRegion).startingWith(..)

Für die Texterkennung benutzt PDFUnit den OCR-Processor Tesseract.

Beispiel - Text aus Bildern prüfen

Das folgende Beispiel basiert auf einer PDF-Datei, die ein Image enthält, das den Text des Romans "Der kleine Lord" enthält. Das Image hat einen farbigen Hintergrund.

@Test
public void hasImageWithText() throws Exception {
  String filename = "ocr_little-lord-fauntleroy.pdf";
  int leftX  =  10; // millimeter
  int upperY =  35;
  int width  = 160;
  int height = 135;
  PageRegion pageRegion = new PageRegion(leftX, upperY, width, height);
  
  String expectedText = "Cedric himself knew nothing whatever about it.";
  AssertThat.document(filename)
            .restrictedTo(FIRST_PAGE)
            .restrictedTo(pageRegion)
            .hasImage()
            .withText()
            .containing(expectedText)
  ;
}

Normalisierung des OCR-Textes

Wenn man den erwarteten Text des vorhergehenden Tests mit dem Bild vergleicht, fällt der Zeilenumbruch hinter dem Wort 'nothing' auf. Trotz dieses Zeilenumbruchs ist der Test erfolgreich, weil alle Leerzeichen im Zuge einer OCR-Normalisierung vor dem Vergleich eliminiert werden.

Schritte der Normalisierung der OCR-Text:

  • Zeichen werden in Kleinbuchstaben umgewandelt

  • Alle Leerzeichen (Whitespaces) werden entfernt

  • 12 verschiedene Trennzeichen (Hyphens) werden entfernt

  • 10 verschiedene Unterstriche (Underscore) werden entfernt

  • Satzzeichen (Punctuation) werden entfernt

Die Ergebnisse der Texterkennung können verbessert werden, wenn der Processor 'trainiert' wurde. Sprachspezifische Trainingsdaten können von https://github.com/tesseract-ocr/tessdata heruntergeladen werden.

Beispiel - Text in Bildausschnitten

Es könnte sein, dass ein zu validierender Text in einem bestimmten Bereich des Bildes erwartet wird. Um eine solche Situation abzudecken, kann ein Bildausschnitt definiert und übergeben werden:

@Test
public void hasImageWithTextInRegion() throws Exception {
  String filename = "ocr_little-lord-fauntleroy.pdf";
  
  int leftX  =  10; // millimeter
  int upperY =  35;
  int width  = 160;
  int height = 135;
  PageRegion pageRegion = new PageRegion(leftX, upperY, width, height);
  
  int imgLeftX  = 250; // pixel
  int imgUpperY =  90;
  int imgWidth  = 130;
  int imgHeight =  30;
  ImageRegion imageRegion = new ImageRegion(imgLeftX, imgUpperY, imgWidth, imgHeight);
  
  String expectedText = "Englishman";
  AssertThat.document(filename)
            .restrictedTo(FIRST_PAGE)
            .restrictedTo(pageRegion)
            .hasImage()
            .withTextInRegion(imageRegion)
            .containing(expectedText)
  ;
}

Die Einheit für die Definition von Bildausschnitten ist immer 'Pixel'. 'Millimeter' als Einheit wären schwierig in der Handhabung, weil Bilder in PDF skaliert sein können. Dadurch sind sie physisch größer oder kleiner, als sie aussehen. Um die richtigen Maße für einen Bildausschnitt zu bekommen, extrahieren Sie die Bilder aus einem PDF und vermessen den Ausschnitt anschließend mit einem gängigen Bildbearbeitungsprogramm. Für die Extraktion stellt PDFUnit das Tool ExtractImages zur Verfügung. Es ist in Kapitel 9.3: „Bilder aus PDF extrahieren“ beschrieben.

Beispiel - Gespiegelter und gedrehter Text in Bildern

Wasserzeichen oder andere Texte in Bildern sind möglicherweise absichtlich gedreht oder gespiegelt. Auch solche Texte können validiert werden. Dafür gibt es folgende Methoden:

// Method to rotate and flip images before OCR processing:

.hasImage().flipped(FlipDirection).withText()...
.hasImage().rotatedBy(Rotation).withText()...

Hier ein Bild, dessen Inhalt mit dem nachfolgenden Test validiert wird.

Der Text in dem Bild ist um 270 Grad gedreht und zusätzlich vertikal umgeklappt (flipped). Wenn ein Test diese Werte berücksichtigt, kann der tatsächliche Text gegen einen Erwartungswert geprüft werden:

@Test
public void testFlippedAndRotated() throws Exception {
  String filename = "image-with-rotated-and-flipped-text.pdf";
  int leftX  = 80; // in millimeter
  int upperY = 65;
  int width  = 50;
  int height = 75;
  PageRegion pageRegion = new PageRegion(leftX, upperY, width, height);

  String expectedText = "text rotated 270 and flipped vertically";
  
  AssertThat.document(filename)
            .restrictedTo(FIRST_PAGE)
            .restrictedTo(pageRegion)
            .hasImage()
            .rotatedBy(Rotation.DEGREES_270) 
            .flipped(FlipDirection.VERTICAL)
            .withText()
            .equalsTo(expectedText)
  ;
}

Die erlaubten Werte für eine Drehung oder Spiegelung sind:

Rotation.DEGREES_0
Rotation.DEGREES_90
Rotation.DEGREES_180
Rotation.DEGREES_270

FlipDirection.NONE
FlipDirection.HORIZONTAL
FlipDirection.VERTICAL