About October 2008

This page contains all entries posted to /blog/wvk in October 2008. They are listed from oldest to newest.

September 2008 is the previous archive.

January 2009 is the next archive.

Many more can be found on the main index page or by looking through the archives.

Powered by
Movable Type 3.31

« September 2008 | Main | January 2009 »

October 2008 Archives

October 4, 2008

Das leidige Thema 'Ruby und Strings'

Ruby kennt keine Strings.

Traurig, aber wahr. Ruby kennt nur irgendwie vermurkste Byte-Arrays, und deswegen musste ich leider heute bei einer formatierten Ausgabe wie dieser

User.find(:all).each do |user|
  if user.valid_system_user?
    puts "#{user.name.ljust(12, '.')} #{user.real_name[0..24].ljust(25, '.')} " +
         "#{user.uid.to_s.rjust(5)} #{user.gid.to_s.rjust(5)} " +
         "#{user.home_dir.ljust(23, '.')} #{File.exists?(user.home_dir).to_s.rjust(5)}"
  end
end

folgendes feststellen:

name         real name                   UID   GID home dir                exists?
wvk......... Xxxxxx xxx Xxxxxxx.......  1001  1001 /home/wvk..............  true
phl......... Xxxxxxx Xxxßxxxxxx......  1078   100 /home/phl.............. false
ua.......... Xxx Xxxxxxxxx............   510   100 /home/ua............... false
pg.......... Xxxxxxx Xxxxxx...........   505   505 /home/pg............... false
...

Na, wem fällt's auf? Genau, dem Kollegen mit dem "ß" im Namen fehlt ein '.'. Unschwer zu erraten, woran das liegt: rjust und ljust verwenden intern natürlich die length-Methode von Ruby, um die Länge des darzustellenden Strings zu ermitteln. Diese liefert aber nur bei den Standard-ASCII-Zeichen das richtige Ergebnis, bei anderen UTF8-Codierten Zeichen zwischen 1 und 3 zu viel:

~# irb
irb(main):001:0> "ß".length
=> 2
irb(main):002:0> "€".length
=> 3

Nanu. Um das Problem mit der falsch erkannten Stringlänge zu umgehen, könnte man folgendes verwenden:

$KCODE = 'u'
require 'jcode'

und dann, statt length, jlength verwenden. Dieser hässlichster aller Workarounds die mir diese Woche begegnet sind, funktioniert wie man es von jeder String-Funktion erwarten würde:

irb
irb(main):001:0> $KCODE = 'u'
=> "u"
irb(main):002:0> require 'jcode'
=> true
irb(main):003:0> 'äöü'.jlength
=> 3
irb(main):004:0> '€'.jlength
=> 1

Na bitte, wer sagt's denn. Aber was bringt das jetzt in Verbindung mit rjust? Nur so viel dass man beim selber neu Implementieren der Methoden eine zuverlässige Längenangebe hat (die Methoden jljust bzw. jrjust stellt jcode nämlich nicht zur Verfügung):

class String
  def jljust(len, char = ' ')
    if len > self.jlength
      self + char * (len - self.jlength)
    else
      self
    end
  end

  def jrjust(len, char = ' ')
    if len > self.jlength
      char * (len - self.jlength) + self
    else
      self
    end
  end
end

In verbindung mit dem neuen Code

User.find(:all).each do |user|
  if user.valid_system_user?
    puts "#{user.name.jljust(12, '.')} #{user.real_name[0..24].jljust(25, '.')} " +
         "#{user.uid.to_s.jrjust(5)} #{user.gid.to_s.jrjust(5)} " +
         "#{user.home_dir.jljust(23, '.')} #{File.exists?(user.home_dir).to_s.jrjust(5)}"
  end
end

sieht das Ergebnis dann auch so aus wie es aussehen sollte:

name         real name                   UID   GID home dir                exists?
wvk......... Xxxxxx xxx Xxxxxxx.......  1001  1001 /home/wvk..............  true
phl......... Xxxxxxx Xxxßxxxxxx.......  1078   100 /home/phl.............. false
ua.......... Xxx Xxxxxxxxx............   510   100 /home/ua............... false
pg.......... Xxxxxxx Xxxxxx...........   505   505 /home/pg............... false
...

Mehr zum Thema Unicode-Horror in Verbindung mit Rails gibt's unter http://wiki.rubyonrails.org/rails/pages/HowToUseUnicodeStrings zu lesen.

October 26, 2008

Dokumentenarchivierung und OCR unter Linux

Seit einiger Zeit suche ich nach einer Lösung, um meine ganzen Quittungen, Rechnungen und amtlichen Dokumente elektronisch zu archivieren. Ich weiß, dass es dafür unter MacOS das eine oder andere hervorragende System gibt, aber unter Linux sieht es da ziemlich mau aus.

Das Archivieren von Dokumenten sieht nach meiner Vorstellung so aus:

  • einscannen des Dokuments mit einem normalen Flachbettscanner @ 300dpi
  • automatisches Konvertieren des Bildes mittels ImageMagics convert-Kommando: normalisieren des Kontrastes, entfernen von Staub und Rauschen, speichern als monochrom-Bild
  • automatisches Erkennen der Zeichen (OCR) und Speicherung als Plaintext, zusammen mit dem bild für spätere Volltextsuche
  • Das ganze liegt irgendwo über ein Webinterface erreichbar in einer MySQL-Datenbank.

Der erste Schritt ist der zeitaufwändigste, da das Scannen leider nicht gerade schnell von Statten geht. Der Rest soll automatisch und ohne mein Zutun geschehen, wenn ich ein Bild über besagtes Webinterface hochlade. Insbesondere die Texterkenung ist dabei aber leider ein kleines Sorgenkind, wie die folgenden Beispiele zeigen.

Getestete OCR-Programme

Die Programme testete ich mit (unter anderem) volgendem Bild (Ausschnitt):

Nach der Konvertierung mit convert kscan_0026.png -crop 2450x3450+20+20 -unsharp 8 -normalize -monochrome ocr.tif sah selbiges so aus:

Und nun zu den Programmen:

gOCR: einfach zu bedienen, relativ flott, kommt mit so ziemlich allen Bitmapformaten zurecht.

$ gocr ocr.png

<auszug>
  Erlauterungen
  Untertttzun9sle1stungen der E7tern s1nd nict einkommensteuerpflchtig.


  S1e  aen  Ihre E1nkommensteuererlrung auf nǐchtamtl1chem Vorruck oder auf elektron1schem ege
  agegeen.  Aus  Ko5tengründen  werden  wir  zukünft1g aur de Versendung von amtl1chen Vordrucken
  verz1chten.
</auszug>

Das sieht nicht ganz übel aus, aber die Fehlerrate ist für einen so einfachen Text (im Sinne von Zeichen- und Schriftenvielfalt) eigentlich inakzeptabel. Zusammen mit einem Wörterbuch könnte man hier sicherlich noch mehr herausholen, aber leider unterstützt gOCR das nicht. Das Programm erkennt jedoch Seitenlayoute ohne Fließtext relativ gut, wie sich an einigen Formularen zeigte.

OCRAD: bietet etwas umfangreichere Kommandozeilenargumente. Bei nicht-englischen Texte empfiehlt es sich, den Ausgabe-Zeichensatz auf utf8 zu setzen. Außerdem verfügt OCRAD über die Möglichkeit, "Layouterkennung" in drei Stufen einzustellen. Dabei werden die erkannten Fließtextblöcke nacheinander ausgegeben und nicht, wie in gOCR, unter Beibehaltung des ursprünglichen Layouts.

$ ocrad --format=utf8 --layout=2 ocr.pbm

<auszug>
  Er | autarung_n
  Unterstützung5lelstungen der Eltern s1nd nlcht elnkommen5teuerpfllchtlg.

  Sle haben Inre Elnkommen5teuererklárung avf nichtamtllcnem vordruck oder auf elektronlschem wege
  abgegeben . Au5 Ko5tengründen werden wlr zukünrt1g auf d1e ver5endung von amtl1chen Vordrucken
  verz1c_ten.
</auszug>

Nunja -- nicht bedeutend besser. Ein paar zusätzliche Wörter werden korrekt erkannt, aber die Fehlerrate ist nicht bedeutend geringer. Manche Ausgaben erinneren wirklich an 1337-5p34k ;).

Tesseract-OCR: Diese Software wurde Ende der 1980er von HP entwickelt und ist mittlerweile Open Source. Die Software soll zu den Top-OCR-Programmen gehört haben. Sie verfügt über sprachspezifische Bibliotheken, die auch selbst erstellt werden können. Die Verarbeitung dauert etwas länger als mit den o.g. Programmen und funktioniert auch nur mit TIFF-Dateien, aber das Resultat spricht für sich:

$ tesseract ocr.tif out -l deu

<auszug>
  Erläuterungen
  Unterstützungsleistungen der Eltern sind nicht einkommensteuerpflichtig.
  Sie haben Ihre Einkommensteuererklärung auf nichtamtlichem Vordruck oder auf elektronischem Wege
  abgegeben. Aus Kostengründen werden wir zukünftig auf die Versendung von amtlichen Vordrucken
  verzichten.
</auszug>

WOW sag ich nur! Der kontrollbereich ist komplett fehlerfrei; auch der Rest des Textes weist nur hier und da Unstimmigkeiten auf. Es sieht also so aus, als würde diese Software meine Wünsche erfüllen ;-)

Leider sieht es noch nicht danach aus, als würde Tesseract Layouterkennung unterstützen, was allerdings für den gewünschten Zewck, nämlich Volltextsuche in der Datenbank, völlig unerheblich ist.