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
= RE
cognize C
haracter
REL
= RE
cognize L
ine
REP
= RE
cognize P
age
REC
ReImage
.
Das Ergebnis findet sich in der Struktur RecData
.REL
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
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.
#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); }
#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; }
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)...;
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.
Die KADMOS-Module suchen bei jeder Initialisierung (re?_init()) nach einer Datei KADMOS.ini in folgenden Verzeichnissen:
[saveimg]
mode= REC, REL, REP
mode= OFF
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
mode= OFF
file=
value_min=, value_max=
rec_value[0]
,
in dem Zeichen gesammelt werden sollen. Null
oder fehlende Angabe bedeutet keine Schwelle.
rec_init(), rel_init(), rep_init()
🗏rec_info(), rel_info(), rep_info()
🗏rec_filetitle(), rel_filetitle(), rep_filetitle()
🗏rec_do(), rel_do(), rep_do()
🗏rec_group_labels(), rel_group_labels(), rep_group_labels(), repr_group_labels()
🗏rec_end(), rel_end(), rep_end()
🗏rec_get_features()
🗏rel_freeimages()
rel_lineshadow()
🗏respell_init()
🗏respell_do()
🗏rel_do()
oder rep_do()
durch Vergleich mit dem
Inhalt eines angeschlossenen Wörterbuches.respell_end()
🗏respell_codepage()
🗏respell_filetitle()
🗏respell_lookup()
🗏respell_wordchars()
🗏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);