Eigene Bild-KI in der Digitalisierungsproduktion

Eigenes ModellMediendigitalisierung · laufend
PyTorchtorchvision (ResNet18)RabbitMQMessagePackDockerKubernetesC#/.NETAngularffmpeg

Projekt-Übersicht

In der industriellen Mediendigitalisierung werden Fotos, Dias, Negative, Super-8-Filme und Videokassetten auf eigens entwickelten Maschinen digitalisiert – in Mengen von vielen Millionen Bildern und Filmminuten. Bei diesen Mengen ist jede Prüfung, die ein Mensch Bild für Bild erledigen muss, ein echter Kostenfaktor.

Für einige dieser Prüfungen habe ich eigene Bildmodelle trainiert: für das Erkennen gedrehter Bilder, für das Erkennen von Fehlern in Digitalisaten und für das Erkennen des Bandendes bei der Videodigitalisierung. Sie übernehmen Arbeitsschritte, die vorher manuell erledigt wurden, oder bereiten sie so vor, dass ein Mensch nur noch bestätigen muss.

In dieser Fallstudie geht es weniger um die Modelle selbst als um die Frage, wie sie in die laufende Produktion eingebunden sind. Zu Architektur, Datenmengen und Kennzahlen der Modelle für Bildwinkel und Bildfehler mache ich hier keine Angaben. Als durchgängiges Beispiel dient die Bandende-Erkennung, weil sich an ihr der Weg vom Modell bis zur Bedienoberfläche gut zeigen lässt. Training und Modell der Bandende-Erkennung beschreibe ich ausführlicher in einer eigenen Fallstudie.

Ausgangslage

In der Digitalisierung entstehen typische, gut abgrenzbare Prüffragen:

  • Liegt ein Bild richtig herum? Fotos und Dias können in unterschiedlicher Ausrichtung digitalisiert werden. Ein falsch gedrehtes Bild ist für den Kunden ein offensichtlicher Mangel.
  • Ist das Digitalisat in Ordnung? Technische Fehler bei der Aufnahme sollen auffallen, bevor ein Auftrag ausgeliefert wird.
  • Wo endet der Inhalt einer Videokassette? Die Aufnahme läuft häufig über das Ende des Inhalts hinaus. Danach folgen Minuten mit Bildrauschen („Schnee“) oder einem grauen Bild, die niemand im fertigen Video sehen will.

Alle drei Fragen haben gemeinsam, dass sie sich an einem einzelnen Bild beantworten lassen, sehr oft gestellt werden und eine überschaubare Zahl möglicher Antworten haben. Das macht sie zu guten Kandidaten für eigene Klassifikationsmodelle statt für allgemeine, große Modelle.

Architektur

flowchart LR
  A["Aufnahmegeräte und Scanner"] --> B["Interne Produktionsplattform (.NET)"]
  B -->|"Bild als MessagePack"| C["RabbitMQ-Queue je Modell"]
  C --> D["Worker-Container: Bandende-Modell"]
  C --> E["Worker-Container: Bildwinkel"]
  D -->|"Klasse + Konfidenz"| B
  E -->|"Winkel"| B
  B --> F["Weboberfläche: Vorschlag prüfen"]
  B --> G["Ergebnisordner je Klasse"]
  F --> H["ffmpeg: Video schneiden"]

Jedes Modell läuft als eigener Container und hängt an einer eigenen Queue in RabbitMQ. Die Produktionsplattform ruft die Modelle nicht direkt auf, sondern stellt eine Anfrage nach dem RPC-Muster in die Queue und wartet auf die Antwort. Dadurch sind Modelle austauschbar, lassen sich unabhängig voneinander aktualisieren und horizontal skalieren, indem man weitere Worker an dieselbe Queue hängt.

Technische Umsetzung

Bandende-Erkennung: ein Bildklassifikator mit drei Klassen

Das Modell für das Bandende unterscheidet drei Klassen: normal, snow (Bildrauschen) und gray (graues Bild). Es ist ein per Transfer Learning weitertrainiertes ResNet18: Die letzte Schicht des auf ImageNet vortrainierten Netzes wird durch eine Ausgabeschicht für die drei Klassen ersetzt und auf Einzelbildern aus eigenen, nach Klassen sortierten Digitalisaten trainiert. Neben der Klasse liefert das Modell eine Konfidenz.

Der Ansatz ist bewusst schlicht. Ein kleines, bewährtes Netz reicht für eine Frage mit drei klar unterscheidbaren Bildtypen, lässt sich schnell neu trainieren und läuft notfalls auch auf der CPU. Ist eine GPU vorhanden, wird sie genutzt.

Modelle als Worker hinter einer Queue

Jeder Worker folgt demselben Muster: Er nimmt eine Nachricht aus seiner Queue, dekodiert das Bild aus MessagePack, führt die Vorhersage aus und schickt die Antwort an die Rückantwort-Queue des Aufrufers, verknüpft über eine Korrelations-ID. Neben dem Ergebnis enthält die Antwort Verarbeitungszeit und Hostnamen des Workers, damit sich im Betrieb nachvollziehen lässt, wer wie lange gerechnet hat.

# do the magic
result = predictor.predict(temp_filename)

response = []
response.append(True)
response.append(result.prediction)
response.append(result.confidence)
time_labsed = time.time() - start_time
response.append(int(time_labsed * 1000))
response.append(args.hostname)
response.append(errorMessage)
packed = msgpack.packb(response, use_bin_type=True)
# ... Antwort an props.reply_to mit props.correlation_id, danach basic_ack

Jeder Worker nimmt immer nur eine Nachricht gleichzeitig an (prefetch_count=1) und meldet sich mit einer konfigurierbaren Priorität an der Queue an. So lassen sich stärkere Rechner bevorzugen, ohne die Aufrufer zu ändern. Fehler werden nicht verschluckt, sondern als Antwort mit success = false und Fehlermeldung zurückgegeben.

Auf der .NET-Seite kapselt ein schlanker Client das RPC-Muster: Anfrage serialisieren, in die Queue stellen, auf die Antwort mit passender Korrelations-ID warten (standardmäßig bis zu 60 Sekunden) und deserialisieren.

Einbindung in die Videoaufnahme

Die Produktionsplattform verwaltet die Aufnahmegeräte für Videokassetten und erhält von jedem Aufnahmekanal laufend ein aktuelles Vorschaubild. Für einen Kanal lässt sich die Bandende-Vorhersage auslösen; das Ergebnis mit Klasse, Konfidenz und Zeitpunkt erscheint in der Geräteansicht und im Bilderverlauf der Aufnahme.

var result = DevicesRepository.Instance.EndOfVideoClient.Execute(imageBytes);

this.EndOfVideoPrediction = result.prediction;
this.EndOfVideoPredictionConfidence = result.confidence;
this.EndOfVideoLastPredictionDateTime = DateTime.Now;

var endOfVideoResultsPath = DevicesRepository.Instance.EndOfVideoResultsPath;
if (!string.IsNullOrEmpty(endOfVideoResultsPath))
{
    var predictionPath = Path.Combine(endOfVideoResultsPath, result.prediction);
    // ... Ordner anlegen, Bild mit Gerät, Kanal und Zeitstempel im Dateinamen ablegen
}

Das Ablegen jedes bewerteten Bildes in einem Ordner seiner vorhergesagten Klasse ist mehr als Protokollierung. So entsteht nebenbei ein Bestand an Beispielen, an dem sich Fehlklassifikationen schnell finden lassen und der als Grundlage für weitere Trainingsrunden dient.

Heute wird die Vorhersage gezielt für eine Aufnahme ausgelöst. Als Ausbau ist ein Rundlauf vorbereitet, der alle aktiven Kanäle reihum bewertet.

Vom Vorschlag zum geschnittenen Video

Für fertige Aufnahmen gibt es eine eigene Oberfläche. Sie zieht aus einer Videodatei Einzelbilder, entweder eine feste Anzahl oder eines pro Zeitintervall, lässt alle parallel klassifizieren und speichert die Ergebnisse zwischen. Danach sucht ein einfacher Algorithmus vom Ende her den längsten zusammenhängenden Bereich, der überwiegend nicht normal ist: Standardmäßig mindestens zehn Einzelbilder, von denen mindestens 90 Prozent als Rauschen oder Grau erkannt sind. Führende normale Bilder werden aus dem Bereich wieder entfernt.

Das Ergebnis ist ein Vorschlag, kein automatischer Schnitt: Die Oberfläche zeigt die empfohlene Schnittstelle mit Zeitstempel und markiert die betroffenen Bilder. Erst wenn ein Mensch den Schnitt auslöst, kürzt ffmpeg das Video ohne Neukodierung an dieser Stelle. Das Original bleibt erhalten, die gekürzte Fassung wird als neue Datei geschrieben.

Bildwinkel

Auch die Modelle für die Erkennung des Bildwinkels und für die Fehlererkennung habe ich selbst trainiert. Für den Bildwinkel gibt es in der Administrationsoberfläche ein eigenes Modul. Es folgt demselben Muster aus Queue und Worker: Das Bild geht an eine Queue, der Worker liefert einen Winkel zurück, und die Oberfläche ordnet ihn der nächstliegenden Standardausrichtung zu (−90, 0, 90 oder 180 Grad).

Betrieb

Die Worker laufen als Container in einem Kubernetes-Cluster; eine neue Modellversion wird als Image gebaut, veröffentlicht und per Neustart des Deployments ausgerollt. Jedes Image trägt eine Versionsmarkierung, deren Erstellungszeit der Worker beim Start ins Log schreibt.

In der Administrationsoberfläche der Produktionsplattform gibt es eine Systemübersicht, die für die Modell-Queues passiv abfragt, wie viele Nachrichten warten und wie viele Worker verbunden sind. Eine Queue ohne Worker oder mit wachsendem Rückstau fällt dort sofort auf, ohne dass jemand in RabbitMQ selbst nachsehen muss.

Ergebnis

Die eigenen Bildmodelle sind über ein einheitliches Muster an die Produktion angebunden: ein Container pro Modell, eine Queue pro Aufgabe, eine schmale RPC-Schnittstelle. Bei der Videodigitalisierung liefert die Bandende-Erkennung einen Schnittvorschlag, den ein Mensch nur noch prüft, statt dass jemand das Video bis zum Ende durchsehen muss. Neue Modelle lassen sich mit wenig Aufwand ergänzen, und vorhandene können ausgetauscht werden, ohne die aufrufenden Systeme zu ändern.

Belastbare Kennzahlen zu Genauigkeit und Zeitersparnis veröffentliche ich hier nicht.

Was ich dabei gelernt habe

  • Kleine, spezifische Modelle sind für Produktionsfragen oft die richtige Wahl. Eine Frage mit drei Antwortmöglichkeiten braucht kein großes Modell, sondern gute Trainingsbeispiele.
  • Die Schnittstelle ist wichtiger als das Modell. Weil alle Modelle hinter derselben Queue-Schnittstelle laufen, kann ich sie austauschen, vergleichen und skalieren, ohne die Produktionssoftware anzufassen.
  • Vorschlagen statt automatisch entscheiden. Beim Schneiden von Kundenvideos ist ein falscher automatischer Schnitt teurer als eine Bestätigung per Klick. Das Modell macht die Arbeit, der Mensch behält die Entscheidung.
  • Jede Vorhersage ist ein potenzielles Trainingsbeispiel. Bewertete Bilder nach Klassen abzulegen, kostet fast nichts und liefert die Daten für die nächste Modellversion.

Technologien

  • Modelltraining: PyTorch, torchvision (ResNet18, Transfer Learning), scikit-learn für die Datenaufteilung, OpenCV für die Bildextraktion
  • Inferenz-Worker: Python, pika, MessagePack, Docker
  • Infrastruktur: RabbitMQ (RPC-Muster mit Korrelations-ID und Consumer-Prioritäten), Kubernetes
  • Produktionsplattform: C#/.NET, MessagePack, Angular-Weboberfläche
  • Videoverarbeitung: ffmpeg und ffprobe