opencv4arts: Zeichne meine Stadt, Vincent

OpenCV ist eine Bibliothek mit einer Geschichte der kontinuierlichen Entwicklung in 20 Jahren. Das Alter, in dem Sie anfangen, in sich selbst zu graben und nach einem Ziel zu suchen. Gibt es darauf basierende Projekte, die jemandes Leben besser und glücklicher gemacht haben? Schaffst du es selbst? Auf der Suche nach Antworten und dem Wunsch, bisher unbekannte OpenCV-Module zu entdecken, möchte ich Anwendungen erstellen, die „wunderbar funktionieren“ - so dass es zunächst „wow“ gibt und erst dann „oh ja, es ist Computer Vision“.


Das Recht des ersten Artikels war ein Experiment mit der Übertragung von Stilen von Weltkünstlern in die Fotografie. Aus dem Artikel erfahren Sie, was das Herzstück der Prozedur und die relativ neue OpenCV.js - JavaScript-Version der OpenCV-Bibliothek ist.



Stilübertragung


Gegner des maschinellen Lernens werden mir vergeben, aber die Hauptkomponente im heutigen Artikel wird ein tiefes Faltungsnetzwerk sein. Weil es funktioniert. Es gibt keine Möglichkeit, neuronale Netze in OpenCV zu trainieren, aber Sie können vorhandene Modelle ausführen. Wir werden das vorgefertigte CycleGAN- Netzwerk verwenden. Die Autoren, für die sie sehr dankbar sind, bieten völlig kostenlose Download-Netzwerke an, die Bilder von Äpfeln in Orangen, Pferde in Zebras, Satellitenbilder in Karten, Winterfotos in Sommerfotos und vieles mehr konvertieren. Darüber hinaus können Sie mit dem Netzwerktrainingsverfahren zwei Generatormodelle gleichzeitig in beide Richtungen arbeiten lassen. Das heißt, wenn Sie die Umwandlung von Winter in Sommer lehren, erhalten Sie ein Modell für das Malen von Winterlandschaften in Sommerfotos. Ein einzigartiges Angebot, das man nicht ablehnen kann.


In unserem Beispiel nehmen wir Modelle, die Fotos in Gemälde von Künstlern verwandeln. Nämlich Vincent Van Gogh, Claude Monet, Paul Cezanne oder im gesamten Genre der japanischen Drucke Ukiyo-e. Das heißt, wir werden vier separate Netzwerke zur Verfügung haben. Es ist erwähnenswert, dass für die Ausbildung eines jeden nicht ein Bild des Künstlers verwendet wurde, sondern eine ganze Menge, wodurch die Autoren versuchten, das neuronale Netzwerk zu trainieren, um den Stil eines Werks nicht zu verändern, sondern sozusagen den Schreibstil zu übernehmen.


Opencv.js


OpenCV ist eine in C ++ entwickelte Bibliothek, während für die meisten Funktionen die Möglichkeit besteht, automatische Wrapper zu erstellen, die native Methoden aufrufen. Offiziell werden Wrapper für die Sprachen Python und Java unterstützt. Darüber hinaus gibt es kundenspezifische Lösungen für Go , PHP . Wenn Sie Erfahrung mit anderen Sprachen haben, wäre es großartig zu wissen, in welchen und dank wessen Bemühungen.


OpenCV.js ist ein Projekt, das dank des Google Summer of Code-Programms im Jahr 2017 das Recht auf Leben erhalten hat. Übrigens, sobald das Deep-Learning-Modul OpenCV selbst erstellt und in seinem Framework deutlich verbessert wurde. Im Gegensatz zu anderen Sprachen ist OpenCV.js derzeit kein Wrapper nativer Methoden in JavaScript, sondern eine vollständige Kompilierung mit Emscripten mit LLVM und Clang. Sie können damit eine Datei aus Ihrer C- und C ++ - Anwendung oder Bibliothek .js , die beispielsweise in einem Browser ausgeführt werden kann.


Beispielsweise,


 #include <iostream> int main(int argc, char** argv) { std::cout << "Hello, world!" << std::endl; return 0; } 

Kompilieren in asm.js


 emcc main.cpp -s WASM=0 -o main.js 

Und laden:


 <!DOCTYPE html> <html> <head> <script src="main.js" type="text/javascript"></script> </head> </html> 

Sie können OpenCV.js wie folgt mit Ihrem Projekt verbinden (nächtliche Erstellung):


 <script src="https://docs.opencv.org/master/opencv.js" type="text/javascript"></script> 

Eine zusätzliche Bibliothek zum Lesen von Bildern, Arbeiten mit der Kamera und anderen Dingen, die manuell in JavaScript geschrieben wurde, kann ebenfalls nützlich sein:


 <script src="https://docs.opencv.org/master/utils.js" type="text/javascript"></script> 

Bilder hochladen


Bilder in OpenCV.js können aus Elementen wie canvas oder IMG gelesen werden. Dies bedeutet, dass das direkte Herunterladen von Bilddateien die Aufgabe des Benutzers bleibt. Der addFileInputHandler lädt die Zusatzfunktion addFileInputHandler das Bild automatisch in das gewünschte canvas Element, wenn ein Bild auf Knopfdruck von der Festplatte ausgewählt wird.


 var utils = new Utils(''); utils.addFileInputHandler('fileInput', 'canvasInput'); var img = cv.imread('canvasInput'); 

wo


 <input type="file" id="fileInput" name="file" accept="image/*" /> <canvas id="canvasInput" ></canvas> 

Der wichtige Punkt ist, dass img ein 4-Kanal-RGBA-Image ist, das sich vom üblichen cv::imread , bei dem ein BGR-Image erstellt wird. Dies sollte beispielsweise bei der Portierung von Algorithmen aus anderen Sprachen berücksichtigt werden.


Beim Rendern ist alles einfach - rufen imshow einfach imshow mit der id gewünschten imshow (erwartet RGB oder RGBA).


 cv.imshow("canvasOutput", img); 

Algorithmus


Der gesamte Bildverarbeitungsalgorithmus ist der Start eines neuronalen Netzwerks. Angenommen, was im Inneren passiert, bleibt magisch. Wir müssen nur die richtige Eingabe vorbereiten und die Vorhersage richtig interpretieren (Netzwerkausgabe).


Das in diesem Beispiel betrachtete Netzwerk empfängt einen vierdimensionalen Tensor mit float im Intervall [-1, 1] . Jede der Dimensionen ist in der Reihenfolge der Änderungsgeschwindigkeit der Index des Bildes, der Kanäle, der Höhe und der Breite. Dieses Styling wird als NCHW bezeichnet, und der Tensor selbst wird als blobiges, binäres großes Objekt bezeichnet. Die Vorverarbeitungsaufgabe besteht darin, ein OpenCV-Bild zu konvertieren, dessen Intensitäten verschachtelt sind und ein Intervall von Werten [0, 255] Typ unsigned char Zeichen in einem NCHW-Blob mit einem Wertebereich [-1, 1] .



ein Stück des Nischni Nowgorod Kremls (wie eine Person sieht)



verschachtelte Ansicht (wie OpenCV speichert)



Planaransicht (was das Netzwerk benötigt)


Als Nachbearbeitung müssen die inversen Transformationen durchgeführt werden: Das Netzwerk gibt einen NCHW-Blob mit Werten im Intervall [-1, 1] , der in das Bild umgepackt, auf [0, 255] normalisiert und in unsigned char konvertiert werden muss.


Unter Berücksichtigung aller Funktionen zum Lesen und Schreiben von OpenCV.js-Bildern nehmen die folgenden Schritte Gestalt an:


 imread -> RGBA -> BGR [0, 255] -> NCHW [-1, 1] -> [] [] -> NCHW [-1, 1] -> RGB [0, 255] -> imshow 

Bei Betrachtung der resultierenden Pipeline stellen sich Fragen, warum das Netzwerk nicht sofort mit verschachteltem RGBA arbeiten und verschachteltes RGB zurückgeben kann. Warum sind zusätzliche Transformationen für die Pixelpermutation und -normalisierung erforderlich? Die Antwort ist, dass ein neuronales Netzwerk ein mathematisches Objekt ist, das Transformationen an den Eingabedaten einer bestimmten Verteilung durchführt. In unserem Fall wurde sie geschult, um Daten in dieser Form zu erhalten. Um die gewünschten Ergebnisse zu erzielen, ist es daher erforderlich, die Vorverarbeitung zu reproduzieren, die die Autoren in der Schulung verwendet haben.


Implementierung


Das neuronale Netzwerk, das wir ausführen, wird in Form einer Binärdatei gespeichert, die zuerst in das lokale Dateisystem geladen werden muss.


 var net; var url = 'style_vangogh.t7'; utils.createFileFromUrl('style_vangogh.t7', url, () => { net = cv.readNet('style_vangogh.t7'); }); 

url ist übrigens ein vollständiger Link zur Datei. In diesem Fall laden wir die Datei einfach neben der aktuellen HTML-Seite hoch, aber Sie können sie durch die Originalquelle ersetzen (in diesem Fall kann die Downloadzeit länger sein).


Ein Bild von der canvas lesen und von RGBA zu BGR konvertieren:


 var imgRGBA = cv.imread('canvasInput'); var imgBGR = new cv.Mat(imgRGBA.rows, imgRGBA.cols, cv.CV_8UC3); cv.cvtColor(imgRGBA, imgBGR, cv.COLOR_RGBA2BGR); 

Erstellen eines 4D-Blobs, bei dem die Funktion blobFromImage mithilfe von Normalisierungskonstanten in einen float Datentyp konvertiert wird. Dann - starten Sie das Netzwerk.


 var blob = cv.blobFromImage(imgBGR, 1.0 / 127.5, //  {width: imgBGR.cols, height: imgBGR.rows}, //  [127.5, 127.5, 127.5, 0]); //   net.setInput(blob); var out = net.forward(); 

Das Ergebnis wird zurück in das Bild des gewünschten Typs und das Werteintervall konvertiert [0, 255]


 //     [-1, 1]  [0, 255] var outNorm = new cv.Mat(); out.convertTo(outNorm, cv.CV_8U, 127.5, 127.5); //  interleaved   planar  var outHeight = out.matSize[2]; var outWidth = out.matSize[3]; var planeSize = outHeight * outWidth; var data = outNorm.data; var b = cv.matFromArray(outHeight, outWidth, cv.CV_8UC1, data.slice(0, planeSize)); var g = cv.matFromArray(outHeight, outWidth, cv.CV_8UC1, data.slice(planeSize, 2 * planeSize)); var r = cv.matFromArray(outHeight, outWidth, cv.CV_8UC1, data.slice(2 * planeSize, 3 * planeSize)); var vec = new cv.MatVector(); vec.push_back(r); vec.push_back(g); vec.push_back(b); var rgb = new cv.Mat(); cv.merge(vec, rgb); //   cv.imshow("canvasOutput", rgb); 

Derzeit wird OpenCV.js im halbautomatischen Modus erstellt. In dem Sinne, dass nicht alle Module und Methoden von ihnen die entsprechenden Signaturen in JavaScript erhalten. Für ein dnn-Modul ist die Liste der gültigen Funktionen beispielsweise wie folgt definiert:


 dnn = {'dnn_Net': ['setInput', 'forward'], '': ['readNetFromCaffe', 'readNetFromTensorflow', 'readNetFromTorch', 'readNetFromDarknet', 'readNetFromONNX', 'readNet', 'blobFromImage']} 

Die letzte Konvertierung, bei der der Blob in drei Kanäle aufgeteilt und dann zu einem Bild gemischt wird, kann imagesFromBlob Methode durchgeführt werden, die der obigen Liste einfach noch nicht hinzugefügt wurde. Vielleicht ist dies Ihr erster Beitrag zur Entwicklung von OpenCV? ;)


Fazit


Als Demonstration habe ich eine Seite auf GitHub vorbereitet, auf der Sie den resultierenden Code testen können: https://dkurtaev.imtqy.com/opencv4arts (Achtung! Wenn Sie ein Netzwerk von ca. 22 MB herunterladen, sparen Sie Ihren Datenverkehr. Andernfalls wird empfohlen, die Seite für jedes neue Bild neu zu laden, andernfalls die Qualität nachfolgende Verarbeitung ist irgendwie stark verzerrt). Bereiten Sie sich auf einen langen Verarbeitungsprozess vor oder versuchen Sie, die Bildgröße zu ändern. Das Ergebnis ist ein Schieberegler.


Während ich an dem Artikel arbeitete und genau das Bild auswählte, das ihr Gesicht werden soll, fand ich versehentlich ein Foto meiner Freundin, das den Kreml unserer Stadt zeigt, und alles kam zusammen - kam auf den Namen des Artikels und fand erst dann, dass es so sein sollte. Ich schlage vor, Sie probieren die Anwendung auf Fotos Ihres Lieblingsortes aus und erzählen vielleicht in den Kommentaren oder in einem persönlichen Brief etwas Interessantes darüber.


Von mir - eine lustige Tatsache. Die meisten Einwohner von Nischni Nowgorod und der Region Nischni Nowgorod verwenden das Wort „raus“ im Sinne des Wortes „fit in“ (einen freien Platz finden). Zum Beispiel die Frage "Werden wir Ihr Auto reinigen?" bedeutet "Haben wir genug Platz in Ihrem Auto?", aber nicht "Können wir Ihr Auto aufräumen?". Wenn Studenten aus anderen Bereichen zu Sommerpraktika zu uns kommen, erzählen wir diese Tatsache gerne - viele sind aufrichtig überrascht.


Nützliche Links


Source: https://habr.com/ru/post/de437600/


All Articles