Vorverarbeitung mit OpenCV
Skalieren, entrauschen, binarisieren, Morphologie: die Schritte, die vor jedem Modell stehen und über dessen Güte oft mehr entscheiden als das Modell selbst.
Die Idee
Bevor irgendetwas erkannt werden kann, muss das Bild in einen Zustand gebracht werden, in dem das Gesuchte sich vom Rest abhebt. Das ist Vorverarbeitung: das Bild aufräumen, bevor die eigentliche Frage gestellt wird.
Die typische Kette
- 01
Skalieren
Auf eine feste Größe bringen. Das macht alle folgenden Schwellwerte vergleichbar.
- 02
Graustufen
Farbe weglassen, wenn sie nichts zur Frage beiträgt. Spart zwei Drittel der Daten.
- 03
Entrauschen
Weichzeichnen, damit einzelne Störpixel nicht als Kante gelten.
- 04
Binarisieren
Alles wird schwarz oder weiß. Danach ist das Bild eine Maske.
- 05
Morphologie
Löcher schließen, Fransen abschneiden, getrennte Teile verbinden.
Die Schwellwertverfahren
import cv2, numpy as np
grau = cv2.cvtColor(cv2.imread("formular.jpg"), cv2.COLOR_BGR2GRAY)
# 1. Fester Schwellwert - nur bei kontrollierter Beleuchtung sinnvoll.
_, fest = cv2.threshold(grau, 127, 255, cv2.THRESH_BINARY)
# 2. Otsu - waehlt den Wert selbst, braucht ein zweigipfliges Histogramm.
_, otsu = cv2.threshold(cv2.GaussianBlur(grau, (5, 5), 0), 0, 255,
cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# 3. Adaptiv - ein Wert je Nachbarschaft, robust gegen Schatten.
adapt = cv2.adaptiveThreshold(grau, 255,
cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, blockSize=31, C=10)Bei einem Formular, das schräg unter einer Schreibtischlampe fotografiert wurde, liefert nur die dritte Variante ein brauchbares Ergebnis.
Morphologie
| Operation | Wirkung | Wofür |
|---|---|---|
| Erosion | Weiße Flächen schrumpfen | Dünne Verbindungen trennen |
| Dilatation | Weiße Flächen wachsen | Lücken schließen |
| Öffnen | Erosion, dann Dilatation | Kleine Störungen entfernen |
| Schließen | Dilatation, dann Erosion | Löcher in Objekten füllen |
| Gradient | Dilatation minus Erosion | Umriss extrahieren |
kern = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
sauber = cv2.morphologyEx(adapt, cv2.MORPH_OPEN, kern) # Punkte weg
sauber = cv2.morphologyEx(sauber, cv2.MORPH_CLOSE, kern) # Loecher zuDie Größe des Strukturelements ist der einzige wirklich wichtige Parameter. Er gehört an der Größe der zu entfernenden Störung ausgerichtet, nicht geraten.
Otsu formal
Otsu ist damit äquivalent zu einer Minimierung der Varianz innerhalb der Klassen und funktioniert genau dann gut, wenn das Histogramm zwei erkennbare Berge hat. Bei einem einzigen Berg wählt es einen willkürlichen Punkt an dessen Flanke.
Entzerrung eines fotografierten Dokuments
ziel = np.float32([[0,0],[840,0],[840,1188],[0,1188]]) # A4 bei 100 dpi
H = cv2.getPerspectiveTransform(ecken.astype(np.float32), ziel)
flach = cv2.warpPerspective(img, H, (840, 1188))Diese vier Zeilen erhöhen die OCR-Trefferquote bei schräg fotografierten Dokumenten typischerweise von unter 60 auf über 90 Prozent, mehr als jeder Wechsel der OCR-Software bewirken würde. Siehe Text aus Bildern lesen.
Interpolation beim Skalieren
| Verfahren | Wann |
|---|---|
INTER_AREA | Verkleinern. Als einziges vermeidet es Aliasing. |
INTER_LINEAR | Vergrößern, schneller Standardfall |
INTER_CUBIC | Vergrößern, wenn Qualität zählt |
INTER_NEAREST | Nur für Masken und Etikettenbilder |
Der häufigste stille Fehler ist INTER_LINEAR beim Verkleinern von 4000 auf 640
Pixel: feine Strukturen verschwinden nicht, sondern erzeugen Muster, die es im
Original nicht gab. Für Masken ist umgekehrt jede glättende Interpolation falsch,
weil sie Etikettenwerte vermischt und Klassen erzeugt, die nicht existieren.
Passende Kurse und Quellen
OpenCV-Tutorials
Die offiziellen Anleitungen zu Filtern, Kanten, Merkmalen und Kalibrierung, jeweils mit lauffähigem Python-Code.
Für alle, die Bildverarbeitung selbst schreiben; jedes Beispiel ist lauffähig.