Einleitung


Das Entwicklerpaket KADMOS hat einen streng modularen Aufbau:
REC zur Erkennung bereits vollständig isolierter oder segmentierter Einzelzeichen.
REL zur Erkennung von segmentierten Textzeilen und
REP für die Erkennung mehrzeiliger Texte.
Die Namen der Module REC, REL und REP sind aus ihrer jeweiligen Funktionen hergeleitet:
Rec = REcognize Character
REL = REcognize Line
REP = REcognize Page

REC
Dieses Modul erhält als Eingangsinformation ein Bild in einer Datenstruktur ReImage. Das Ergebnis findet sich in der Struktur RecData.
REL
Dieses Modul erhält als Eingangsinformation ein Bild in einer Datenstruktur ReImage. RelData segmentiert die Textzeile in Einzelzeichen und ruft nun jeweils das Modul RecData zur Erkennung dieser Zeichen auf. Von REL zurückgeliefert wird die Struktur der Textzeile mit eventuellen Alternativen der Segmentierung sowie für jedes erkannte Zeichen das Erkennungsresultat in gleicher Konvention wie vom Einzelzeichenerkenner REC ermittelt. Das Ergebnis findet sich in der Struktur RelData.
REP
Für die Erkennung mehrzeiliger Texte dient dieses Modul REC. Es isoliert aus dem gegebenen Bild zunächst die einzelnen Textzeilen und übergibt diese dann an das Modul REL zur Erkennung. Eingangsinformation ist wiederum ein Bild in einer Datenstruktur ReImage. Zurückgeliefert wird ein Feld von Resultaten des Moduls REL entsprechend der Erkennung der einzelnen Textzeilen. Das Ergebnis findet sich in der Struktur RepData.

Für alle unterstützten C-Compiler werden die Module REC , REL und REP als Library oder DLL zur Verfügung gestellt und können so in beliebige Applikationen eingebunden werden. Der Datenaustausch mit den Modulen erfolgt über zugeordnete Datenstrukturen RecData, ReImage und RepData. In diese Strukturen müssen die zu erkennenden Bilder eingetragen werden, die Module ihrerseits füllen die Struktur mit den Resultaten. Beim Aufruf eines dieser Module muss zusätzlich festgelegt werden, welcher Klassifikator zur Erkennung herangezogen werden soll. Dies wird bei der Initialisierung festgelegt. Das heisst andererseits auch, dass eine initialisierte Struktur fest mit einem bestimmten Klassifikator verbunden ist. Sollen in einer Anwendung verschiedene Schriftfelder (z.B. ein numerisches und ein alphanumerisches) zur Erhöhung der Erkennungsgenauigkeit mit unterschiedlichen Klassifikatoren bearbeitet werden, dann sind entsprechend mehrere Strukturen zu vereinbaren und zu initialisieren.
Die Parameter zur Steuerung der Erkennung werden bei allen drei Modulen einheitlich in einer Parameter-Datenstruktur ReParm übergeben. Die Resultate der Erkennung finden sich in der letzten Gruppe der jeweiligen Struktur RecData, RelData oder RepData. So enthält die letzte Gruppe von RecData die Erkennungsergebnisse für ein isoliertes Zeichen. Bei RelData und RepData verweist diese letzte Gruppe auf weitere Strukturen RelGrid, RelGraph, RelResult bzw. RepResult, in denen die Ergebnisse bereitgestellt werden.
Für die Verifikation der Resultate der Zeilenerkennung wurde das Modul ReSpellData entwickelt. Es erhält als Eingangsinformation die Resultate der Zeilenerkennung. ReSpellData vergleicht diese Resultate mit den Einträgen in einem angeschlossenen Wörterbuch. Von ReSpellData zurückgeliefert wird die Struktur der Textzeile in gleicher Konvention, als Alternativen jedoch nunmehr mit den gegebenenfalls im Wörterbuch gefundenen Einträgen.

Links zu den Datenblättern:

Die Einbindung

Beispiel zur Einbindung von REC in Konsol- und UNIX-Programme
#include <stdio.h>
#include "KADMOS.h"

RecData rec={0};

/* ****************** */
   void main (void) { 
/* ****************** */
  rec_init(&rec, "numplus.rec");
  rec.image.imgtype=IMGTYPE_PIXELARRAY // falls Bilder als Punktmatrix erwartet werden
  for (;;) { 
    // hier eigenen Code zur Besetzung von rec.image einfügen.
    rec.image.data=...; 
    rec.image.width=...;
    rec.image.height=...;
    rec_do(&rec);
    printf("\nErkannt: %s", rec.rec_char[0]); 
  }
  rec_end(&rec); 
}    
          
Beispiel zur Einbindung von REC in Windows-Programme
#include <windows.h>
#include "KADMOS.h"
BOOL RcInit=FALSE; RecData rec={0};
// Prozedurteil für die Initialisierung des Klassifikators
...
if (!RcInit){
  if (rec_init(&rec, "numpluseu.rec")==RE_SUCCESS) RcInit=TRUE;
  else {
    MessageBox(NULL, "rec_init() fehlerhaft", "Beoispiel", MB_OK); 
    return FALSE;
  }
} 

// Prozedurteil für Klassifikation.
rec.parm.labels=(char *) ...;
rec.image.data=...;
rec.image.imgtype=IMGTYPE_PIXELARRAY;
// falls Bilder als Punktmatrix erwartet werden. 
rec.image.width=...;
rec.image.height=...;

if rec_do(&rec)!=RE_SUCCESS) {
  MessageBox(NULL, "rec_do() fehlerhaft", "Beispiel", MB_OK); 
  return FALSE;
}
// Bei 'rec.rec_char' und 'rec.rec_value' können nun die 
// Resultate abgeholt werden. rec.rec_char[0] enthält das 
// erste erkannte Zeichen. rec.rec_value[0] enthält einen Wert 
// für die Sicherheit der Erkennung. Werte unter 100 sind gut, 
// grössere Werte sind schlecht ... Prozedurteil für Abschluss
if (RcInit) {
  if (rec_end(&rec)!=RE_SUCCESS) {
    MessageBox(NULL, "rec_end() fehlerhaft", "Beispiel", MB_OK); 
    return FALSE;
  }
  RcInit=FALSE;
}
        
Beispiel zur Datensammlung mit Hook-Funktion

Zur Programm-Kontrolle, zu Testzwecken und zum Sammeln von Bildern aus Live-Material wurden einfache Funktion bereitgestellt. Das Sammeln ist in jedem Fall durch eine Initialisierung mit dem gewünschten Dateinamen einzuleiten (re_collect_init(), re_collect_winit()) und mit der Funktion re_collect_end() abzuschliessen. Es lässt sich mit dem Unsicherheitswert RelResult🗏 RecData🗏 steuern. Sollen alle Einzelzeichen einer Textzeile gesammelt werden, so ist rel_collect_kernel() unmittelbar nach dem Aufruf von rel_do() aufzurufen. Für das Sammeln einzelner Zeichen dient die Funktion rec_collect_kernel(). Die Aufrufe von re_collect... sind in den mitgelieferten C-Quellen wie recwin, recdemo usw. implementiert und können von dort übernommen werden.

Bei einer guten Segmentierung ist es nicht ausreichend, die segmentierten Zeichen oder Linien durch ihr umschreibendes Rechteck zu charakterisieren. Möchte man direkt auf die segmentierten Bilder zugreifen, so müssen diese zunächst zwischengespeichert werden. Eine solche Zwischenspeicherung wird durch Setzen von TYPO_KEEPIMG in der betreffenden ParameterstrukturRelData oder unter typograph erreicht. Im Zeilenmodul REL kann dann nach dem Aufruf von rel_do() über result_image RecData, result_height und result_width auf diese Bilder zugegriffen werden. Durch einen nachfolgenden Aufruf von rel_do() rel_end() werden diese Bilder wieder gelöscht, der zugehörige Speicher wird wieder freigegeben.
Alternativ kann rel_freeimages() zur Speicherfreigabe aufgerufen werden. Die unter result_image abgelegten Bilder sind Binärbilder mit 1 Byte pro Bildpunkt und zeilenweiser Ablage.

Eine weiterführende Beschreibung findet sich in dem mitgelieferten Beispiel reldemo. Die Auswertung des Bildmaterials kann sehr einfach mit unserem Programm sichten.exe vorgenommen werden. Haben Sie die Segmentierung in Zeilen oder Zeichen selbst übernommen, so erhalten Sie hier eventuell Hinweise auf Bildverschnitte oder Zeichenverstümmelungen. In allen anderen Fällen in denen dennoch eine schlechte Erkennungsleistung vorliegt, bitten wir Sie um Zusendung des Datenmaterials.

// Modul REP, Initialisierung:
if (re_collect_init("images.ras", 0, 0)!=RE_SUCCESS)...;
rep->rel_hook=rel_collect_kernel;
rep->parm.typograph|=TYPO_KEEPIMG;

// Modul REP, Sammlung wird von rep_do() durch Hook-Funktion erledigt:
if (rep_do(rep)!=RE_SUCCESS) ...;

// Modul REP, Abschluss:
if (re_collect_end()!=RE_SUCCESS)...;          
        
Links zu den Datenblättern:
Beispiel zur Datensammlung ohne Hook-Funktion

Für Entwickler, die eine eigene Hook-Funktion verwenden möchten.

// Modul REC oder REL, Initialisierung:
if (re_collect_init("Bilder.ras", 0, 0)!=RE_SUCCESS)...; 

// Modul REC, Sammlung nach dem Aufruf von rec_do():
if (rec_do(&rec)!=RE_SUCCESS) ...;
if (rec_collect_kernel(&rec)!=RE_SUCCESS) ...; 

// Modul REL, Sammlung nach dem Aufruf von rel_do():
rel->parm.typograph|=TYPO_KEEPIMG; 
if (rel_do(&rel)!=RE_SUCCESS) ...;

if (rel_collect_kernel(&rel)!=RE_SUCCESS)...;
if (rel_freeimages (&rel)!=RE_SUCCESS) ...; 

// Modul REC oder REL, Abschluss:
if (re_collect_end()!=RE_SUCCESS) ...;
        

Bei jedem Aufruf von re_collect_init(), re_collect_winit() wird eine Datei angelegt. Wenn die Datei bereits existiert, wird sie überschrieben. Diese Datei wird bis zum Aufruf von re_collect_end() fortgeschrieben. rec_collect_kernel() und rel_collect_kernel() können mit beliebig vielen Strukturen RecData bzw. RelData operieren, so dass mehrere Klassifikatoren gleichzeitig zum Sammeln verwendet werden können. Sollen nicht alle, sondern nur relativ schlecht erkannte Zeichen gesammelt werden, so initialisiere man die Sammlung mit den gewünschten Schwellen, wie dies bereits beschrieben wurde:

// Initialisierung:
if (re_collect_init("Bilder.ras", value_min, value_max)!=RE_SUCCESSS) ...;          
        

Es werden so nur Zeichen gesammelt, deren bestes Erkennungsresultat rec_value[0] wischen value_min und value_max liegt. Ist nur eine Schwelle von Null verschieden, so werden nur gute (value_min==0) oder nur schlechte Zeichen (value_max==0) gesammelt.

Links zu den Datenblättern:
Beispiel zur Datensammlung mit KADMOS.ini

Die KADMOS-Module suchen bei jeder Initialisierung (re?_init()) nach einer Datei KADMOS.ini in folgenden Verzeichnissen:

  1. Verzeichnis, das durch die Environment-Variable KADMOS43 spezifiziert ist.
  2. Verzeichnis des aufrufenden Programms.
  3. Verzeichnis über diesem Verzeichnis des aufrufenden Programmes.
  4. Arbeitsverzeichnis (Current Directory).
  5. Systemverzeichnis.
  6. Unter Windows im Windows-Verzeichnis.
[saveimg]
mode= REC, REL, REP
Sammeln der übergebenen Bilder (ReImage *image) aus REC, REL der REP als BMP-Datei.
mode= OFF
(oder andere Angaben) - keine Sammlung.Der wesentliche Teil in KADMOS.ini ist: file= Dateiname inkl. Pfad der Ausgabedatei für die Bildersammlung. Ist kein Dateiname oder nur der Pfad angegeben, wird ein Name generiert. Diese Dateien können nur ein Bild enthalten. Es wird jeweils nur das zuletzt bearbeitete Bild gespeichert.
[collect]
mode= REC, REL, REP
Sammeln der übergebenen Bilder (ReImage *image) aus REC, REL, REP oder KCL in einer RAS-Datei.
mode= OFF
(oder andere Angaben) - keine Sammlung.
file=
Dateiname inkl. Pfad der Ausgabedatei für die Bildersammlung. Ist kein Dateiname oder nur der Pfad angegeben, wird ein Name generiert.
value_min=, value_max=
Angabe einer unteren Schwelle und einer oberen Schwelle des Bereichs von rec_value[0], in dem Zeichen gesammelt werden sollen. Null oder fehlende Angabe bedeutet keine Schwelle.

Die Module haben einheitliche Aufrufe. Im Einzelnen sind dies:

Module REC, REL und REP:

rec_init(), rel_init(), rep_init()🗏
Aktivieren (Laden) eines Klassifikators.
rec_info(), rel_info(), rep_info()🗏
Information über die dem Klassifikator bekannten Zeichenklassen.
rec_filetitle(), rel_filetitle(), rep_filetitle()🗏
Rückgabe des Klassifikatornamens.
rec_do(), rel_do(), rep_do()🗏
Merkmalsbildung, Klassifikation und Segmentierung (REL, REP).
rec_group_labels(), rel_group_labels(), rep_group_labels(), repr_group_labels()🗏
Konvertieren von Basis-Kennungen in Gruppen-Kennungen.
rec_end(), rel_end(), rep_end()🗏
Deaktivierung des Klassifikators, Speicherfreigabe.
rec_get_features()🗏
Liefert Pointer auf interne Merkmale.
rel_freeimages()
Speicherfreigabe gesammelter Einzelzeichen-Bilder.
rel_lineshadow()🗏
Schriftgut nach Hand- oder Maschinenschrift klassifizieren.

Modul RESPELL:

respell_init()🗏
Laden eines Wörterbuchs.
respell_do()🗏
Verifikation oder Verbesserung der Ergebnisse von rel_do() oder rep_do() durch Vergleich mit dem Inhalt eines angeschlossenen Wörterbuches.
respell_end()🗏
Deaktivieren das geladenen Wörterbuchs.
respell_codepage()🗏
Rückgabe der Codepage des Wörterbuchs.
respell_filetitle()🗏
Liefert den Dateinamen des geladenen Wörterbuches.
respell_lookup()🗏
Die Funktion sucht im aktivierten Wörterbuch nach dem gegebenen Wort und liefert die Resultate der Wörterbuch-Suche zurück.
respell_wordchars()🗏
Diese Funktion liefert den Zeichensatz des Wörterbuchs.

Die meisten Funktionen geben nach korrekter Arbeit den Wert RE_SUCCESS zurück, im Fehlerfall einen entsprechenden Fehlertyp. Die Einbindung der Module in ein aufrufendes Programm erfolgt stets nach folgendem Schema, das hier beispielhaft für das Modul REC dargestellt ist:

#include "KADMOS.h" 
RecData rec={0}; 
... 
rec_init&rec, "numpluseu.rec"); 
for (;;) { 
  // hier eigenes Programm zur Besetzung von 'image' einfügen
  rec.image.data=...; 
  rec_do(&rec); 
  printf("\nrecognized: %s", rec.rec_char[0]); 
} 
rec_end(&rec);         
      

Der verwendete Klassifikator (im Beispiel numpluseu.rec) wird über eine Datenstruktur RecData angesprochen. Diese Struktur ist - wie auch die Strukturen RelData und RepData - in KADMOS.h deklariert. In der Praxis gibt es Fälle, in denen etwa auf einem Formular mehrere Lesefelder mit unterschiedlichen Klassifikatoren erkannt werden müssen. Man benötigt dazu für jeden Klassifikator eine eigene zugehörige Datenstruktur, mit der dann die spezifische Initialisierung und Erkennung durchgeführt wird. Im folgenden Beispiel für eine Zeilenerkennung seien ein Maschinenschrift- und ein OCRA-Feld zu erkennen.

#include "KADMOS.h" 
RelData rel_ttfde={0}; 
RelData rel_ocra={0};

rel_ttfde.init.rel_graph_maxlen=...; 
rel_ttfde.init.rel_result_maxlen=...; 
rel_ttfde.rel_graph=...; 
rel_ttfde.rel_result=...;
...
rel_init(&rel_ttfde, "ttfde.rec");

rel_ocra.init.rel_graph_maxlen=...; 
rel_ocra.init.rel_result_maxlen=...; 
rel_ocra.rel_graph=...;
rel_ocra.rel_result=...; 
rel_init(&rel_ocra, "ocra.rec");
... 
for (;;) 
{ 
  rel_ttfde.image.data=...; 
  rel_ttfde.image.imagetype=...;
  rel_do(&rel_ttfde); ... 
  rel_ocra.image.data=...;
  rel_ocra.imiage.imagetype=...;
  rel_do(&rel_ocra); 
... 
} 
rel_end(&rel_ocra); 
rel_end(&rel_ttfde);