In diesem Artikel möchte ich euch einen Weg aufzeigen wie man ein simples PHP OCR Script entwickeln kann. OCR (Optical Character Recognition) heißt das man mit einem Algorithmus verschiedene Muster, zumeist Texte, in einem Dokument wie einem Bild oder PDF erkennt. Dieses PHP OCR Script ist eine sehr simple Methode und weißt in dieser Version eine hohe Fehlerquote bei der Erkennung auf.
Der Berg namens OCR
Genau wie beim Besteigen eines Berges trifft man bei der Entwicklung eines PHP OCR Scriptes auf viele Probleme, welche oft nicht so leicht zu lösen sind.
Ein paar dieser Probleme sind das Filtern der Charaktere aus dem Dokument, das erkennen des gefilterten Charakters, die Lernfähigkeit des PHP OCR Scriptes und vieles mehr.
Beim Filtern der Charaktere sind vor allem Rotation, Verzerrung und Farbwechsel problematisch.
Dieses PHP OCR Script behandelt lediglich das erkennen von einfachen Charakteren welche ohne Rotation, Verzerrung und vielen Farbwechseln dargestellt werden.
Der Weg zum Ziel
Es gibt viele Wege ein PHP OCR Script zu entwickeln.
Manche davon führen über Eigenvektoren und erfordern sehr gute Kenntnisse im Umgang mit Linearer Algebra.
Wir bedienen uns in diesem PHP OCR Script einfacher Logik und Schleifen um die Charaktere zu filtern und zu erkennen.
- function getColor($image, $x, $y) {
- $color = imagecolorat($image, $x, $y);
- return imagecolorsforindex($image, $color);
- }
- function compareColor($color1, $color2, $range = 20) {
-
- if (empty($color1) || empty($color2)) {
- return false;
- }
-
- if ($color1['red'] >= ($color2['red'] + $range) &&
- $color1['green'] >= ($color2['green'] + $range) &&
- $color1['blue'] >= ($color2['blue'] + $range) &&
- $color1['alpha'] >= ($color2['alpha'] + $range))
- {
- return false;
- }
-
- else if ($color1['red'] <= ($color2['red'] - $range) &&
- $color1['green'] <= ($color2['green'] - $range) &&
- $color1['blue'] <= ($color2['blue'] - $range) &&
- $color1['alpha'] <= ($color2['alpha'] - $range))
- {
- return false;
- }
- else {
- return true;
- }
- }
Alles anzeigen
Charaktererkennung
Wir iterieren nun über die X und Y Achse, vergleichen den aktuellen Pixel mit den umliegenden Pixeln und erkennen, anhand der Unterschiede in den Farbwerten der Pixel, die Kanten der Charaktere.
Gehen wir davon aus das Kantenglättung genutzt wurde so müssen wir mittels compareColor() prüfen ob ein Farbwert mit Abweichung die Zielfarbe trifft.
Wenn ein Pixel sich vom Hintergrund unterscheidet gehen wir erst mal davon aus dass dieser zu einem Charakter gehören kann.
Um nur die Kanten eines Charakters zu vergleichen, prüfen wir ob der aktuelle Pixel sich zu den umliegenden Pixeln unterscheidet.
Wenn das zutrifft fügen wir die Koordinaten des Pixels in eine Matrix ein.
Wir erkennen ob das Ende eines Charakters erreicht ist, in dem wir die Streuung der Pixel auf der Vertikalen Y Achse überprüfen.
Dafür Summieren wir alle Farbwerte der Pixel auf dieser Achse und teilen diesen Wert durch die Höhe des Bildes.
Ist die Abweichung dieses Wertes kleiner als 0.7% des Farbwertes vom Hintergrund, so gehen wir davon aus dass wir das Ende eines Charakters erreicht haben.
Diese Methode sorgt dafür das sich Charaktere geringfügig überschneiden dürfen.
Somit erkennt das PHP OCR Script z.B. auch die Charaktere F und A (FA) welche sich auf der Y Achse überlagern.
- $matrix = new Matrix();
- $width = imagesx($image);
- $height = imagesy($image);
- $background = getColor($image, 0, 0);
- $backgroundDecimal = imagecolorat($image, 0, 0);
Matrix Vergleich
Nun haben wir die Pixel der Charaktere in Matrizen zerlegt.
Jetzt folgt das zuordnen der Charakter Matrizen zu den Werten.
Dafür vergleichen wir unsere Matrizen mit vordefinierten Matrizen.
Diese Matrizen stellen die Punkte auf den X und Y Achsen der Pixel unseres Musters dar und haben unterschiedlich hohe Werte auf den für sie passenden Koordinaten.
- 'B' => [
- 'pointAmount' => 1066,
- 'totalPoints' => 6313,
- 'matrix' => [
- [0,1,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,1,0,0,0,0,0,0,0,0,0,],
- [1,3,4,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,5,5,5,4,3,2,2,0,0,0,0,0,0,0,],
- [2,4,6,7,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,7,7,7,6,5,4,2,1,0,0,0,0,0,],
- [2,4,7,8,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,8,8,8,8,7,6,4,3,1,0,0,0,0,],
- [2,4,7,8,9,9,9,9,8,8,8,8,8,8,8,8,8,8,8,8,8,8,9,9,9,8,8,6,5,3,1,0,0,0,],
- [2,4,7,8,9,9,8,7,7,6,6,6,6,6,6,6,6,6,6,6,7,7,7,8,9,9,9,8,7,5,3,0,0,0,],
- [2,4,7,8,9,9,7,6,4,3,3,3,3,3,3,3,3,3,3,3,4,5,5,6,7,8,9,9,8,6,4,2,0,0,],
- [2,4,7,8,9,8,7,5,3,1,0,0,0,0,0,0,0,0,1,1,2,2,3,4,6,7,8,9,9,7,5,3,0,0,],
- [2,4,7,8,9,8,7,4,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,4,6,8,9,9,8,6,4,2,0,],
- [2,4,7,8,9,8,6,4,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,5,7,8,9,8,7,5,2,0,],
- [2,4,7,8,9,8,6,4,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,4,6,8,9,9,7,5,3,0,],
- [2,4,7,8,9,8,6,4,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,6,8,9,9,8,6,3,0,],
- [2,4,7,8,9,8,6,4,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,5,8,9,9,8,6,3,0,],
- [2,4,7,8,9,8,6,4,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,6,8,9,9,8,5,3,0,],
- [2,4,7,8,9,8,6,4,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,6,8,9,8,7,5,2,0,],
- [2,4,7,8,9,8,6,4,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,7,8,9,8,6,4,2,0,],
- [2,4,7,8,9,8,7,4,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,6,7,8,8,7,5,3,0,0,],
- [2,4,7,8,9,8,7,4,2,1,0,0,0,0,0,0,0,0,0,0,1,2,2,4,5,7,8,8,8,6,4,2,0,0,],
- [2,4,7,8,9,9,7,5,4,3,2,2,2,2,2,2,2,2,3,3,3,4,5,6,7,8,8,8,6,4,2,0,0,0,],
- [2,4,7,8,9,9,8,7,6,5,5,5,5,5,5,5,5,5,5,5,6,6,7,8,8,8,8,6,5,3,1,0,0,0,],
- [2,4,7,8,9,9,9,8,8,7,7,7,7,7,7,7,7,7,7,8,8,8,8,9,9,8,7,5,3,2,0,0,0,0,],
- [2,4,7,8,9,9,9,9,9,8,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,8,7,5,4,2,0,0,0,0,],
- [2,4,7,8,9,9,9,9,8,8,8,8,8,8,8,8,8,8,8,8,8,9,9,9,9,9,8,7,5,3,2,0,0,0,],
- [2,4,7,8,9,9,8,8,7,7,6,6,6,6,6,6,6,6,6,7,7,7,7,8,8,8,8,8,7,6,4,2,0,0,],
- [2,4,7,8,9,9,8,6,5,4,4,4,4,4,4,4,4,4,4,4,4,5,5,6,7,8,8,8,8,7,6,4,2,0,],
- [2,4,7,8,9,8,7,5,3,2,2,1,1,1,1,1,1,1,2,2,2,2,3,4,5,6,7,8,9,8,7,6,3,1,],
- [2,4,7,8,9,8,7,4,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,4,6,7,8,9,8,7,5,2,],
- [2,4,7,8,9,8,6,4,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,6,8,9,9,8,6,3,],
- [2,4,7,8,9,8,6,4,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,5,7,8,9,8,7,4,],
- [2,4,7,8,9,8,6,4,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,4,6,8,9,9,7,5,],
- [2,4,7,8,9,8,6,4,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,6,8,9,9,8,6,],
- [2,4,7,8,9,8,6,4,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,5,8,9,9,8,6,],
- [2,4,7,8,9,8,6,4,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,6,8,9,9,8,6,],
- [2,4,7,8,9,8,6,4,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,6,8,9,9,8,5,],
- [2,4,7,8,9,8,6,4,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,7,8,9,8,7,5,],
- [2,4,7,8,9,8,6,4,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,5,7,9,9,8,6,4,],
- [2,4,7,8,9,8,7,4,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,5,7,8,9,9,8,5,3,],
- [2,4,7,8,9,8,7,5,3,1,0,0,0,0,0,0,0,0,0,0,1,1,2,3,4,5,7,8,9,9,8,7,4,2,],
- [2,4,7,8,9,9,7,6,4,3,3,3,3,3,3,3,3,3,3,3,3,4,4,5,6,7,8,9,9,8,7,5,3,1,],
- [2,4,7,8,9,9,8,7,7,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,8,8,9,9,8,7,6,4,2,0,],
- [2,4,7,8,9,9,9,9,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,9,9,8,8,7,6,4,2,0,0,],
- [2,4,7,8,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,8,8,8,8,8,7,6,5,4,2,0,0,0,],
- [2,4,6,7,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,7,7,7,7,6,5,4,3,2,0,0,0,0,],
- [1,3,4,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,5,5,4,4,3,3,2,0,0,0,0,0,0,],
- ]
- ]
Alles anzeigen
Da unsere Matrizen höchst wahrscheinlich unterschiedliche Maße aufweisen, müssen wir diese in ein Verhältnis zu unseren im PHP OCR Script vordefinierten Matrizen setzen.
Nun prüfen wir den ins Verhältnis gesetzten Punkt und die umliegenden Punkte auf einen Treffer.
Von allen Pixeln nehmen wir nun den höchsten Wert und addieren diesen zu einer Variable welche den Treffer Wert des Charakters enthält.
Nachdem wir all unsere vordefinierten Matrizen verglichen haben ermitteln wir die Matrix mit dem höchsten Treffer wert.
Nun hat unser PHP OCR Script zu einer gewissen Wahrscheinlichkeit einen Charakter erkannt.
- $character = [];
- foreach ($characters as $index => $char) {
- $matchedArray = [];
-
- foreach ($letters as $letter => $properties) {
- $matrix = $properties['matrix'];
- $matched = 0;
-
- $xRatio = (sizeof($matrix[0]) - 1) / $char->width;
- $yRatio = (sizeof($matrix) - 1) / $char->height;
- foreach ($char->points as $point) {
- $x = ($point->x * $xRatio);
- $y = ($point->y * $yRatio);
-
- $pointMatch = [];
- if (isset($matrix[$y][$x]))
- $pointMatch[] = $matrix[$y][$x];
- if (isset($matrix[$y+1][$x]))
- $pointMatch[] = $matrix[$y+1][$x];
- if (isset($matrix[$y-1][$x]))
- $pointMatch[] = $matrix[$y-1][$x];
- if (isset($matrix[$y][$x+1]))
- $pointMatch[] = $matrix[$y][$x+1];
- if (isset($matrix[$y][$x-1]))
- $pointMatch[] = $matrix[$y][$x-1];
- if (isset($matrix[$y+1][$x+1]))
- $pointMatch[] = $matrix[$y+1][$x+1];
- if (isset($matrix[$y+1][$x-1]))
- $pointMatch[] = $matrix[$y+1][$x-1];
- if (isset($matrix[$y-1][$x+1]))
- $pointMatch[] = $matrix[$y-1][$x+1];
- if (isset($matrix[$y-1][$x-1]))
- $pointMatch[] = $matrix[$y-1][$x-1];
-
- $matched += max($pointMatch);
- }
- $matchedArray[$letter] = $matched;
- }
-
- arsort($matchedArray);
- $letter = key($matchedArray);
- $match = reset($matchedArray);
- var_dump($letter, $match / (sizeof($char->points) / 100));
- }
Alles anzeigen
Schlusswort zum PHP OCR Script
Nun habt ihr also einen einfachen aber recht Fehler anfälligen Weg kennen gelernt wie man ein PHP OCR Script entwickeln kann.
Ihr werdet mit dem Aktuellen PHP OCR Script Probleme bei dem Vergleich mit der vordefinierten Matrix für den Buchstaben I haben.
Dieser Buchstabe hat nämlich keinen Whitespace und trifft, durch das „ins Verhältnis setzen der Koordinaten“, jeden Punkt.
Man könnte das reduzieren in dem man die Anzahl der gefilterten Punkte, mit den zu vergleichenden Punkten, mit in den Vergleich einbezieht.
Wie man sieht habe ich in der Matrix für den Buchstaben B bereits die Anzahl der gesamten Punkte und der maximal treffbaren Punkte eingetragen.
Damit könnte man den Treffer Wert ins Verhältnis zur gefilterten Matrix setzen und somit diesen Wert noch verfeinern.
Kommentare