Dienstag, 27. August 2013

baseline TIFF – Versuch einer Rekonstruktion, Teil1

Vorwort

Im Artikel »baseline TIFF« bin ich auf den Aufbau des TIFF-Formates eingegangen. Um die Eignung von TIFF-Dateien als Bildformat für die Langzeitverfügbarkeit (bzw. Langzeitarchivierung) zu testen, habe ich das Bild aus dem Beitrag genommen, leicht geändert und mit einem Fuzzing-Tool bitweise zerstört.

Als Werkzeug habe ich zzuf genutzt, welches ich angewiesen habe, 1‰ aller Bits des Original-Bildes zufällig zu verändern.
$> zzuf -c -s87423 -r0.001 <example_tiff.tiff >fuzzy-0.001.tiff
$> display fuzzy-0.001.tiff
 
display.im6: fuzzy-0.001.tiff: invalid TIFF directory; tags are not sorted in ascending order. `TIFFReadDirectory' @ warning/tiff.c/TIFFWarnings/768.
display.im6: fuzzy-0.001.tiff: unknown field with tag 275 (0x113) encountered. `TIFFReadDirectory' @ warning/tiff.c/TIFFWarnings/768.
display.im6: fuzzy-0.001.tiff: unknown field with tag 16663 (0x4117) encountered. `TIFFReadDirectory' @ warning/tiff.c/TIFFWarnings/768.
display.im6: fuzzy-0.001.tiff: TIFF directory is missing required "StripOffsets" field. `MissingRequired' @ error/tiff.c/TIFFErrors/508.

Versuch einer Rekonstruktion des Image File Directory (IFD)

Versuchen wir das Bild zu rekonstruieren. Zur Erinnerung hier nochmal der Aufbau eines TIFFs:
Aufbau TIFF
Das kaputte TIFF-Bild sieht als Hexdump so aus:
00000000  49 49 2a 00 28 03 00 00  00 00 00 00 00 00 00 00  |II*.(...........|
00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000050  00 00 00 00 00 00 00 00  00 00 00 0c 00 00 00 0c  |................|
00000060  00 00 00 0c 00 00 00 0c  00 00 00 0c 00 00 00 0c  |................|
00000070  00 00 00 01 00 00 00 09  00 00 00 04 00 00 00 00  |................|
00000080  00 00 00 05 00 00 00 0c  00 00 00 0c 00 00 00 0c  |................|
00000090  00 00 00 0c 00 00 00 00  00 00 00 0c 00 00 00 0c  |................|
000000a0  00 00 00 0c 00 00 00 0c  00 00 00 f6 00 00 00 f6  |................|
000000b0  00 00 00 fa 00 00 00 ff  00 00 00 f6 00 00 00 f6  |................|
000000c0  00 00 00 1a 00 00 00 d5  00 00 00 54 00 00 00 00  |...........T....|
000000d0  00 00 00 75 00 00 00 ff  00 00 00 f5 00 00 00 f5  |...u............|
000000e0  00 00 00 ef 00 00 00 00  00 00 00 ff 00 00 00 f4  |................|
000000f0  00 00 00 f5 00 00 00 f5  00 00 00 00 00 00 00 00  |................|
00000100  10 00 00 28 00 00 00 e3  00 00 00 00 00 00 00 00  |...(............|
00000110  00 00 00 00 00 00 00 cb  00 00 00 51 00 00 00 00  |...........Q....|
00000120  00 00 00 70 00 00 00 af  00 00 00 01 00 00 00 01  |...p............|
00000130  00 00 00 00 00 00 00 00  00 00 00 ff 00 00 00 00  |................|
00000140  00 00 00 01 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000150  00 00 00 34 00 00 00 e4  00 00 00 00 00 00 00 00  |...4............|
00000160  00 00 00 00 00 00 00 cb  00 00 00 51 00 00 00 00  |...........Q....|
00000170  10 00 00 70 00 00 00 da  00 00 00 8b 00 00 00 8c  |...p............|
00000180  00 00 00 00 00 00 00 00  00 00 00 ff 00 00 00 88  |................|
00000190  00 00 00 8b 00 00 00 3d  00 00 00 00 00 00 00 00  |.......=........|
000001a0  00 00 00 34 00 00 00 e4  00 00 00 00 00 00 00 00  |...4............|
000001b0  00 00 00 00 00 00 00 cb  00 00 00 51 00 00 00 00  |...........Q....|
000001c0  00 00 00 70 00 00 00 ce  00 00 00 63 00 00 00 63  |...p.......c...c|
000001d0  00 00 00 00 00 00 00 00  00 00 00 ff 00 00 00 5f  |..............._|
000001e0  00 00 00 63 00 00 00 2b  00 00 00 00 00 00 00 00  |...c...+........|
000001f0  00 00 00 35 00 00 00 e8  00 00 00 00 00 00 00 00  |...5............|
00000200  00 00 00 00 00 00 00 cf  00 00 00 52 00 00 00 00  |...........R....|
00000210  00 00 00 72 00 00 00 af  00 00 00 00 00 00 00 00  |...r............|
00000220  00 00 00 00 00 00 01 00  20 00 00 ff 00 00 00 00  |........ .......|
00000230  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000240  00 00 00 2c 00 00 00 c2  00 00 00 00 00 00 00 00  |...,............|
00000250  00 00 00 00 00 00 00 ad  00 00 00 44 00 00 00 00  |...........D....|
00000260  00 00 00 5f 00 00 00 94  00 00 00 00 00 00 00 00  |..._............|
00000270  00 00 00 00 00 00 00 00  00 00 00 e7 00 00 00 00  |................|
00000280  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000290  00 00 00 0e 00 00 00 3c  00 00 00 00 00 00 00 00  |.......<........|
000002a0  00 00 00 00 00 00 00 35  00 00 00 15 00 00 00 00  |.......5........|
000003b0  00 00 00 1d 00 00 00 2e  00 00 00 00 00 00 00 00  |................|
000002c0  00 00 00 00 00 00 00 00  00 00 00 47 00 00 00 00  |...........G....|
000002d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000310  00 08 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000320  00 00 00 00 00 00 00 00  12 00 fe 00 04 00 01 00  |................|
00000330  00 00 00 00 00 00 00 01  03 00 01 00 00 00 14 00  |................|
00000340  00 00 01 01 03 00 01 00  00 00 0a 00 00 00 02 01  |................|
00000350  03 00 04 00 00 00 06 04  00 00 03 01 03 00 01 00  |................|
00000360  00 00 01 00 00 00 06 01  03 00 01 00 00 00 02 00  |................|
00000370  00 00 0d 01 02 00 2a 00  00 00 0e 04 00 00 06 01  |......*.........|
00000380  02 00 0e 00 00 00 38 04  00 00 13 01 04 00 01 00  |......8.........|
00000390  00 00 08 00 00 00 12 01  03 00 01 00 00 00 01 00  |................|
000003a0  00 00 15 01 03 00 01 00  00 00 04 00 00 00 16 01  |................|
000003b0  03 00 01 00 00 00 40 00  00 00 17 41 04 00 01 00  |......@....A....|
000003c0  00 00 20 03 00 00 1a 01  05 00 01 00 00 00 46 04  |.. ...........F.|
000003d0  00 00 1b 01 05 00 01 00  00 00 4e 04 00 00 1c 01  |..........N.....|
000003e0  03 00 01 00 00 00 01 00  00 00 28 01 03 00 01 00  |..........(.....|
000003f0  00 00 02 00 00 00 52 01  03 00 01 00 00 00 01 00  |......R.........|
00000400  00 00 00 00 00 00 08 00  08 00 08 00 08 00 2f 68  |............../h|
00000410  6f 6d 65 2f 72 6f 6d 65  79 6b 65 2f 44 6f 6b 75  |ome/romeyke/Doku|
00000420  6d 65 6e 74 65 2f 65 78  61 6d 70 6c 65 5f 74 69  |mente/example_ti|
00000430  66 66 2e 74 69 66 66 00  54 65 73 74 62 69 6c 64  |ff.tiff.Testbild|
00000440  20 54 49 46 46 00 00 00  00 48 00 00 00 01 00 00  | TIFF....H......|
00000450  00 48 00 00 00 01                                 |.H....|
00000456

Schauen wir uns zu erst die 8 Bytes des Headers an. Das einzig Kritische könnte ein zerstörter Offset zum IFD sein:

00000000  49 49 2a 00 28 03 00 00  00 00 00 00 00 00 00 00  |II*.(...........|

Die ersten zwei Bytes sind in Ordnung, da sie nur zwei Werte annehmen können:
0x4949 little endian (least significant to most significant byte) 
0x4d4d big endian (most significant to least significant byte)

Die nächsten zwei Bytes stellen die Zahl 42 in jeweiliger Endianess dar: 2a 00 ist also auch in Ordnung.

Die Bytes 4-7 zeigen auf das erste Image File Directory (IFD), hier die Adresse 0x0000 0328
Kann dieser Wert stimmen?

Von IFDs wissen wir, daß diese wie folgt aufgebaut sind:
Aufbau Image File Directory (IFD) in TIFF-Datei
An der Adresse 0x0328 stehen ff. Werte: 
00000320  00 00 00 00 00 00 00 00  12 00 fe 00 04 00 01 00  |................|
00000330  00 00 00 00 00 00 00 01  03 00 01 00 00 00 14 00  |................|
00000340  00 00 01 01 03 00 01 00  00 00 0a 00 00 00 02 01  |................|
00000350  03 00 04 00 00 00 06 04  00 00 03 01 03 00 01 00  |................|
00000360  00 00 01 00 00 00 06 01  03 00 01 00 00 00 02 00  |................|
00000370  00 00 0d 01 02 00 2a 00  00 00 0e 04 00 00 06 01  |......*.........|
00000380  02 00 0e 00 00 00 38 04  00 00 13 01 04 00 01 00  |......8.........|
00000390  00 00 08 00 00 00 12 01  03 00 01 00 00 00 01 00  |................|
000003a0  00 00 15 01 03 00 01 00  00 00 04 00 00 00 16 01  |................|
000003b0  03 00 01 00 00 00 40 00  00 00 17 41 04 00 01 00  |......@....A....|
000003c0  00 00 20 03 00 00 1a 01  05 00 01 00 00 00 46 04  |.. ...........F.|
000003d0  00 00 1b 01 05 00 01 00  00 00 4e 04 00 00 1c 01  |..........N.....|
000003e0  03 00 01 00 00 00 01 00  00 00 28 01 03 00 01 00  |..........(.....|
000003f0  00 00 02 00 00 00 52 01  03 00 01 00 00 00 01 00  |......R.........|
00000400  00 00 00 00 00 00 08 00  08 00 08 00 08 00 2f 68  |............../h|
00000410  6f 6d 65 2f 72 6f 6d 65  79 6b 65 2f 44 6f 6b 75  |ome/romeyke/Doku|
00000420  6d 65 6e 74 65 2f 65 78  61 6d 70 6c 65 5f 74 69  |mente/example_ti|
00000430  66 66 2e 74 69 66 66 00  54 65 73 74 62 69 6c 64  |ff.tiff.Testbild|
00000440  20 54 49 46 46 00 00 00  00 48 00 00 00 01 00 00  | TIFF....H......|
00000450  00 48 00 00 00 01                                 |.H....|
00000456

Die Anzahl der Tags ergibt sich aus den Bytes 0x0328 und 0x0329 mit Wert 0x12 und 0x00, was für  18 Einträge steht. Dieser Wert klingt erstmal plausibel. Wir wissen, daß jedes Tag 12 Byte groß ist und im baseline-TIFF der nextIFD-Eintrag aus 4Bytes mit dem Wert 0x00 besteht. Schauen wir also an die Adresse 0x0328+2+12*18 = 0x402. Dort stehen 4 mal die Werte 0x00. Sieht erst einmal korrekt aus.

Wir können also annehmen, daß das IFD korrekt ist. Jeder Eintrag im IFD besteht ja aus 12 Bytes und ist wie folgt aufgebaut:
Tag-Eintrag
Aus dem Artikel zu baseline TIFF wissen wir, daß die Tags in ihrer Nummer (ID) aufsteigend sortiert sind. Lesen wir dazu jeweils die 2 Bytes mit Offset n*12:
  • 1. 0x32a: 0xfe 0x00 -> 254
  • 2. 0x336: 0x00 0x01 -> 64
  • 3. 0x342: 0x01 0x01 -> 257
  • 4. 0x34e: 0x02 0x01 -> 258
  • 5. 0x35a: 0x03 0x01 -> 259
  • 6. 0x366: 0x06 0x01 -> 262
  • 7. 0x372: 0x0d 0x01 -> 269
  • 8. 0x37e: 0x06 0x01 -> 262
  • 9. 0x38a: 0x13 0x01 -> 275
  • 10.0x396: 0x12 0x01 -> 274
  • 11.0x3a2: 0x15 0x01 -> 277
  • 12.0x3ae: 0x16 0x01 -> 278
  • 13.0x3ba: 0x17 0x41 -> 16663
  • 14.0x3c6: 0x1a 0x01 -> 282
  • 15.0x3d2: 0x1b 0x01 -> 283
  • 16.0x3de: 0x1c 0x01 -> 284
  • 17.0x3ea: 0x28 0x01 -> 296
  • 18.0x3f6: 0x52 0x01 -> 338

Ins Auge fallen die Tags 2, 6, 8, 13 u. 18. Insbesondere Tag 2 und 13 können nicht stimmen, da sie krass die Sortierung verletzten. Eventuell müssen wir die Tags 6-10 nochmal prüfen.

Der Tag 2 an Adresse 0x336 müsste einen Wert um die 250 enthalten.Sehr wahrscheinlich ist, daß er statt 0x00 0x01 die Folge 0x01 0x00 enthält, was dem Wert 256 entspräche.

Der Tag 13 an Adresse 0x3ba hat wahrscheinlich im zweiten Byte nur einen Bit-Fehler und sollte statt 0x17 0x41 die Folge 0x17 0x01 enthalten. Das ergebe dann den Wert 279.

Tragen wir die Tag-Bezeichner und die vorläufig korrigierten Tags des IFD ein:
  • 1. 0x32a: 0xfe 0x00 -> 254 -> TIFFTAG_SUBFILETYPE
  • 2. 0x336: 0x00 0x01 -> 256 -> TIFFTAG_IMAGEWIDTH
  • 3. 0x342: 0x01 0x01 -> 257 -> TIFFTAG_IMAGELENGTH
  • 4. 0x34e: 0x02 0x01 -> 258 -> TIFFTAG_BITSPERSAMPLE
  • 5. 0x35a: 0x03 0x01 -> 259 -> TIFFTAG_COMPRESSION
  • 6. 0x366: 0x06 0x01 -> 262 -> TIFFTAG_PHOTOMETRIC
  • 7. 0x372: 0x0d 0x01 -> 269 -> TIFFTAG_DOCUMENTNAME
  • 8. 0x37e: 0x06 0x01 -> 262 -> TIFFTAG_PHOTOMETRIC
  • 9. 0x38a: 0x13 0x01 -> 275 ->  (nicht existent)
  • 10.0x396: 0x12 0x01 -> 274 -> TIFFTAG_ORIENTATION
  • 11.0x3a2: 0x15 0x01 -> 277 -> TIFFTAG_SAMPLESPERPIXEL
  • 12.0x3ae: 0x16 0x01 -> 278 -> TIFFTAG_ROWSPERSTRIP
  • 13.0x3ba: 0x17 0x01 -> 279 -> TIFFTAG_STRIPBYTECOUNTS
  • 14.0x3c6: 0x1a 0x01 -> 282 -> TIFFTAG_XRESOLUTION
  • 15.0x3d2: 0x1b 0x01 -> 283 -> TIFFTAG_YRESOLUTION
  • 16.0x3de: 0x1c 0x01 -> 284 -> TIFFTAG_PLANARCONFIG
  • 17.0x3ea: 0x28 0x01 -> 296 -> TIFFTAG_RESOLUTIONUNIT
  • 18.0x3f6: 0x52 0x01 -> 338 -> TIFFTAG_EXTRASAMPLES
Bis auf Eintrag 9 und eventuell Eintrag 18 klingen die Tags so sinnvoll, daß sie richtig sein könnten. Tag 9 muß dann zwischen 262 und 274 liegen und könnte ff. Tags enthalten:
  • TIFFTAG_THRESHHOLDING           263 
  • TIFFTAG_CELLWIDTH               264
  • TIFFTAG_CELLLENGTH              265
  • TIFFTAG_FILLORDER               266
  • TIFFTAG_DOCUMENTNAME            269
  • TIFFTAG_IMAGEDESCRIPTION        270
  • TIFFTAG_MAKE                    271
  • TIFFTAG_MODEL                   272
  • TIFFTAG_STRIPOFFSETS            273
Wenn wir tiffinfo (Teil der libtiff) zur Hilfe nehmen, bekommen wir ff. Ausgabe:

$> tiffinfo fuzzy-0.001.tiff
TIFFReadDirectoryCheckOrder: Warning, Invalid TIFF directory; tags are not sorted in ascending order.
TIFFReadDirectory: Warning, Unknown field with tag 275 (0x113) encountered.
TIFFReadDirectory: Warning, Unknown field with tag 16663 (0x4117) encountered.
MissingRequired: TIFF directory is missing required "StripOffsets" field.


Also ist Eintrag 9 mit hoher Wahrscheinlichkeit das Tag TIFFTAG_STRIPOFFSETS mit ID 273.

Ende Teil1


Sonntag, 11. August 2013

Helferlein Postkorrektur von 'Und'

Mithilfe eines Scriptes, welches eigentlich Worttrennungen aufsammeln sollte habe ich festgestellt, daß in den schon korrigierten Texten ab und an das Wort 'und' mitten im Satz groß geschrieben war, zB.: 'Der Müller Und sein Sohn…'.

Eigentlich sollte ja, wie in meinem Beitrag "Erkennung von OCR- und Tippfehlern in Textdateien" beschrieben, die Verwendung von DPCustomMono2 solche Fehler bei der Korrektur sichtbar machen. Hat leider in diesem Beispiel nicht gut nicht funktioniert…

Da die korrigierten Texte aber Basis für ein Wörterbuch für Tesseract sind, kam es durch die gehäufte Verwendung von 'Und' zu einer sich selbstverstärkenden Schleife, die in neuen OCR-Texten immer öfter das 'Und' mit Großbuchstaben mitten in den Satz einbaut.

Dank Linux ist das Problem schnell behoben, ich habe jetzt ein Script, in welchen ich solche Kandidaten eintrage (Update: Script nun vereinfacht):

#!/bin/bash
if [ ! -e "$1" ]; then
    echo "needs a file on commandline"
    exit 1
fi
grep "[a-z]\+ Und" $1 && echo "#### found 'Und' (und) in $1"
grep "[a-z]\+U[a-z]\+" $1 && echo "#### found 'xxUxx' in $1"
grep "[a-z]\+ dein" $1 && echo "#### found 'dein' (dem) in $1"
grep "[a-z]\+ init" $1 && echo "#### found 'init' (mit) in $1"

Der Ausdruck '[a-z]\+ Und' im Grep heißt nichts anderes als 'Finde alle Stellen mit einem "Und", vor welchem ein Leerzeichen und davor mindestens ein Kleinbuchstabe steht.'

In einer Schleife lasse ich dies über die OCR-Texte laufen:

$> for i in ../txt/img*.txt; do bash find_typical_errors.sh $i; done| less

Voila! :)