Einsteigerguide: Einführung in Core Image.
Das Beispielprojekt ist eine iOS-Anwendung, die eine große Auswahl an vom System bereitgestellten Bildfiltern auflistet und eine Benutzeroberfläche zum Anpassen der Parameter und Beobachten der Effekte bietet.
Obwohl der Beispielcode in Swift für iOS geschrieben ist, können die Konzepte problemlos auf Objective-C und OS X übertragen werden.
Grundlegende Konzepte.
Um über Core Image zu sprechen, müssen wir zunächst einige grundlegende Konzepte vorstellen.
Ein Filter ist ein Objekt, das eine Reihe von Ein- und Ausgängen hat und eine Art Transformation durchführt. Beispielsweise kann ein Weichzeichner ein Eingangsbild und einen Weichzeichnungsradius aufnehmen und ein entsprechend unscharfes Ausgangsbild erzeugen.
Ein Filtergraph ist ein Netzwerk (gerichteter azyklischer Graph) von Filtern, die so miteinander verbunden sind, dass die Ausgabe eines Filters die Eingabe eines anderen sein kann. Auf diese Weise können aufwendige Effekte erzielt werden. Wir werden unten sehen, wie man Filter verbindet, um einen Vintage-Fotoeffekt zu erzeugen.
Kennenlernen der Core Image API.
Mit diesen Konzepten in unserem Toolkit können wir beginnen, die Besonderheiten der Bildfilterung mit Core Image zu erforschen.
Core Image Architektur.
Core Image hat eine PlugIn-Architektur, was bedeutet, dass es Benutzern erlaubt, ihre Funktionalität zu erweitern, indem sie benutzerdefinierte Filter schreiben, die mit den vom System bereitgestellten Filtern integriert werden. Wir werden die Erweiterbarkeit von Core Image in diesem Artikel nicht nutzen. Wir erwähnen es nur, weil es die API des Frameworks beeinflusst.
Core Image wurde geschrieben, um die Hardware, auf der es läuft, optimal zu nutzen. Die eigentliche Implementierung jedes Filters, der Kernel, ist in einer Untermenge von GLSL, der Shading-Sprache von OpenGL, geschrieben. Wenn mehrere Filter zu einem Filtergraphen verbunden sind, fügt Core Image diese Kernel zu einem effizienten Programm zusammen, das auf dem Grafikprozessor ausgeführt werden kann.
Wenn immer möglich, verschiebt Core Image die Arbeit in die Zukunft. Oftmals finden keine Zuweisungen oder Bearbeitungen statt, bis die Ausgabe des Endfilters in einem Filtergraphen angefordert wird.
Um arbeiten zu können, benötigt Core Image ein Objekt mit der Bezeichnung Kontext. Der Kontext ist das eigentliche Arbeitspferd des Frameworks, das den notwendigen Speicherplatz bereitstellt und die Filterkerne, die die Bildverarbeitung übernehmen, kompiliert und ausführt. Kontexte sind sehr teuer in der Erstellung, so dass Sie meistens einen Kontext erstellen und ihn wiederholt verwenden wollen. Wir werden unten sehen, wie man einen Kontext erstellt.
Abfrage nach verfügbaren Filtern.
Core Image Filter werden nach Namen erstellt. Um eine Liste der Systemfilter zu erhalten, fragen wir Core Image nach den Namen der Filter in der Kategorie kCICategoryBuiltin:
Die Liste der unter iOS verfügbaren Filter ist fast eine Untermenge der unter OS X verfügbaren Filter. Unter OS X sind 169 Filter eingebaut, unter iOS sind es 127:
Erstellen eines Filters nach Name.
Jetzt, da wir eine Liste der verfügbaren Filter haben, können wir einen Filter erstellen und verwenden. Um beispielsweise einen Gaußschen Weichzeichner zu erzeugen, übergeben wir den Filternamen an den entsprechenden CIFilter-Initializer:
Filterparameter einstellen.
Aufgrund der PlugIn-Struktur von Core Image werden die meisten Filtereigenschaften nicht direkt gesetzt, sondern auf Key-Value-Codierung (KVC) zurückgegriffen. Um beispielsweise den Weichzeichnungsradius des Weichzeichnungsfilters einzustellen, verwenden wir KVC, um dessen Eigenschaft inputRadius einzustellen:
Da dies die Methode AnyObject (id in Objective-C) als Wertparameter nutzt, ist er nicht besonders typsicher. Daher erfordert das Setzen von Filterparametern eine gewisse Wachsamkeit, um sicherzustellen, dass Sie den erwarteten Typ übergeben.
Filterattribute abfragen.
Um zu wissen, welche Eingangs- und Ausgangsparamter ein Filter bietet, können wir nach seinen inputKeys bzw. outputKeys-Arrays fragen. Diese geben jeweils ein Array von NSStrings zurück.
Um mehr Informationen über die einzelnen Parameter zu erhalten, können wir im Attributverzeichnis des Filters nachschlagen. Jeder Ein- und Ausgabeparametername wird einem eigenen Wörterbuch zugeordnet, das beschreibt, um welche Art von Parametern es sich handelt und ggfs. seine Minimal- und Maximalwerte. Hier ist zum Beispiel das Wörterbuch, das den Parameter inputBrightness des Filters CIColorControls entspricht:
Für numerische Parameter enthält das Wörterbuch die Tasten KCIAttributeSliderMin und kCIAttributeSliderMax, die die erwarteten Eingabemengen begrenzen. Die meisten Parameter enthalten auch einen kCIAttributeDefault-Schlüssel, der auf den Standardwert des Parameters abbildet.
Ein Bild in der Praxis filtern.
Die Arbeit der Filterung eines Bildes besteht aus drei Teilen: Erstellen und Konfigurieren eines Filtergraphen, Senden eines zu filternden Bildes und Abrufen des gefilterten Bildes. Die folgenden Abschnitte behandeln dies im Detail.
Erstellen eines Filtergraphen.
Die Erstellung eines Filtergraphen besteht aus der Instanziierung von Filtern für die gewünschte Arbeit, der Einstellung ihrer Parameter und deren Verdrahtung, so dass die Bilddaten nacheinander durch jeden Filter fließen.
In diesem Abschnitt werden wir eine Filtergrafik für die Herstellung von Bildern im Stil einer Zinnfotografie des 19. Jahrhunderts erstellen. Wir verketten zwei Effekte, um diesen Effekt zu erzeugen; einen monochromen Filter, um das Bild gleichzeitig zu entsättigen und zu färben und einen Vignettenfilter, um einen Schatteneffekt zu erzeugen, der das Bild umrahmt.
Quartz Composer, der von der Apple Developer-Website heruntergeladen werden kann, ist für das Prototyping von Core Image Filtergraphen nützlich. Im Folgenden haben wir den gewünschten Fotofilter zusammengestellt, indem wir den Color Monochrome-Filter und den Vignettenfilter zusammengeschaltet haben.
Sobald wir mit dem Effekt zufrieden sind, können wir die Filtergrafik im Code wiederherstellen:
Beachten Sie, dass das Ausgangsbild des Monochromefilters zum Eingangsbild des Vignettenfilters wird. Dadurch wird die Vignette auf das getönte monochrome Bild aufgebracht. Beachten Sie auch, dass wir Parameter im Initializer angeben können, anstatt sie einzeln mit KVC zu setzen.
Erstellen des Eingabebildes.
Core Image Filter setzen voraus, dass ihr Eingangsbild vom Typ Climage ist. Für iOS-Programmierer, die an UIImage gewöhnt sind, mag dies etwas ungewöhnlich sein, aber die Aufzeichnung ist nicht ungerechtfertigt. Ein CIImage ist eigentlich eine allgemeinere Entität als ein UIImage, da ein CIImage eine unendliche Ausdehnung haben kann. Natürlich können wir kein unendliches Bild im Speicher ablegen, aber konzeptionell bedeutet dies, dass Sie Bilddaten aus einem beliebigen Bereich in der 2D-Ebene anfordern und ein aussagekräftiges Ergebnis erhalten können.
Alle Bilder, die wir in diesem Artikel verwenden werden, sind endlich und es ist einfach ausreichend, ein CIImage aus einem UIImage zu erstellen. Eigentlich ist es nur eine Zeile Code:
Es gibt auch Komfort-Initialisierer, um CIImages direkt aus Bilddaten oder einer Datei-URL zu erstellen.
Sobald wir ein CIImage haben, können wir es als Eingangsbild des Filtergraphen einstellen, indem wir den Parameter inputImage des Filters setzen:
Ein gefiltertes Bild holen.
Filter haben eine Eigenschaft mit der Bezeichnung outputImage. Wie Sie vielleicht vermuten, hat es den Typ CIImage. Wie führen wir also die umgekehrte Operation der Erstellung eines UIImage aus einem CIImage durch?, Nun, obwohl wir unsere ganze Zeit damit verbracht haben, einen Filtergraphen aufzubauen, ist es jetzt an der Zeit, die Macht des GIContextes aufzurufen und die eigentliche Arbeit der Filterung des Bildes zu tun.
Der einfachste Weg, einen Kontext zu erstellen, ist, ein Null-Optionen-Wörterbuch an seinen Konstruktor zu übergeben:
Um ein Bild aus dem Filtergraphen zu erhalten, bitten wir unseren CIContext, aus einem Rechteck im Ausgabebild ein CGImage zu erzeugen, das den Umfang des Eingabebildes übergibt:
Der Grund für die Verwendung des Eingangsbildes ist, dass das Ausgangsbild oft andere Dimensionen hat als das Eingangsbild. Zum Beispiel hat ein unscharfes Bild einige zusätzliche Pixel an seinem Rand, da es über den Rand des Eingangsbildes hinaus abgetastet wird.
Wir können nun ein UIImage aus diesem neu erstellten CGImage erstellen:
Es ist möglich, ein UIImage direkt aus einem CIImage zu erstellen, aber dieser Ansatz ist schwierig: Wenn Sie versuchen, ein solches Bild in einem UIImageView anzuzeigen, wird dessen Eigenschaft contentMode ignoriert. Die Verwendung eines Zwischen-CGImage macht einen zusätzlichen Schritt, vermeidet aber diese Unannehmlichkeiten.
Leistungssteigerung mit OpenGL.
Es ist zeitaufwendig und verschwenderisch, die CPU zum Zeichnen eines CGImage zu verwenden, nur um das Ergebnis direkt an UIKit zum Compositing zu übergeben. Wir würden es vorziehen, das gefilterte Bild auf den Bildschirm zu zeichnen, ohne eine Reise durch Core Graphics machen zu müssen. Glücklicherweise können wir aufgrund der Interoperabilität von OpenGL und Core Image genau das tun.
Um Ressourcen zwischen einem OpenGL-Kontext und einem Core Image-Kontext zu teilen, müssen wir unseren CIContext auf eine etwas andere Weise erstellen:
Hier erstellen wir einen EAGLContext mit dem OpenGL ES 2.0 Funktionsumfang. Dieser GL-Kontext kann als Backing-Kontext für ein GLKView oder zum Zeichnen in eine CAEAGLLayer verwendet werden. Der Beispielcode verwendet diese Technik, um Bilder effizient zu zeichnen.
Wenn ein CIContext einen zugehörigen GL-Kontext hat, kann mit dem folgenden Aufruf ein gefiltertes Bild mit OpenGL gezeichnet werden:
Der fromRect-Parameter ist wie bisher der Teil des Bildes, der in den Koordinatenraum des gefilterten Bildes gezeichnet werden soll. Der Parameter inRect ist das Rechteck im Koordinatenraum des GL-Kontextes, in das das Bild gezeichnet werden soll. Wenn Sie das Seitenverhältnis des Bildes respektieren wollen, müssen Sie eventuell etwas rechnen, um das entsprechende inRect zu berechnen.
Filterung auf die CPU erzwingen.
Wenn immer möglich, führt Core Image eine Filterung auf dem Grafikprozessor durch. Es hat jedoch die Möglichkeit, auf die CPU zurückzugreifen. Die Filterung auf der CPU kann eine bessere Genauigkeit haben, da GPUs bei ihren Fließkomma-Berechnungen oft eine gewisse Treue gegen Geschwindigkeit eintauschen. Sie können die Ausführung von Core Image auf der CPU erzwingen, indem Sie den Wert des Schlüssels kCIContextUseSoftwareRenderer im Options Dictionary auf true setzen, wenn Sie einen Kontext erstellen.
Sie können feststellen, ob ein CPU- oder GPU-Renderer verwendet wird, indem Sie die Umgebungsvariable CI_PRINT_TREE in ihrer Schema-Konfiguration in Xcode auf 1 setzen. Dies führt dazu, dass Core Image jedes Mal, wenn ein gefiltertes Bild gerendert wird, Diagnoseinformationen ausgibt. Diese Einstellung ist auch nützlich, um den zusammengesetzten Bildfilterbaum zu untersuchen.
Eine Tour durch die Beispielapplikation.
Der Beispielcode für diesen Artikel besteht aus einer iPhone-Applikation, die eine Vielzahl der in Core Image für iOS verfügbaren Bildfilter zeigt.
GUI als Filterparametern erstellen.
Um eine maximale Anzahl von Filtern zu demonstrieren, nutzt die Beispielanwendung den introspektiven Charakter von Core Image und generiert eine Benutzeroberfläche zur Steuerung der Parameter der von ihr unterstützten Filter.
Die Beispielanwendung ist auf Filter beschränkt, die ein einzelnes Eingabebild und Null oder mehr numerische Eingaben haben. Es gibt einige interessante Filter, die nicht in diese Kategorie fallen. Dennoch gibt die App einen guten Überblick über die Funktionalität von Core Image.
Für jeden Eingangsparameter des Filters wird ein Schieberegler mit dem minimalen und maximalen Wert des Parameters konfiguriert und sein Wert auf den Standardwert gesetzt. Wenn sich der Wert des Schiebereglers ändert, übermittelt er die Änderung an seinen Delegaten, der eine UIImageView-Unterklasse ist, die eine CIFilter-Referenz enthält.
Verwendung der eingebauten Fotofilter.
Neben zahlreichen anderen eingebauten Filtern demonstriert die Beispiel-App die in iOS 7 eingeführten Fotofilter. Diese Filter haben keine Parameter, die wir einstellen können, aber sie verdienen die Aufnahme, da sie zeigen, wie man die Effekte ub der Photos App für iOS emulieren kann.
Schlußfolgerungen.
Dieser Artikel ist eine kurze Einführung in Core Image, ein Framework für leistungsstarke Bildverarbeitung. Wir haben versucht, so viele Funktionen des Frameworks wie möglich in diesem kurzen Format abzudecken. Sie haben gelernt, wie man Core Image Filter instanziiert und verkabelt, Bilder in und aus Filtergraphen holt und Parameter einstellt, um das gewünschte Ergebnis zu erzielen. Sie haben auch gelernt, wie Sie auf die vom System bereitgestellten Fotofilter zugreifen können, mit denen Sie das Verhalten der Photo-Apps unter iOS emulieren können.
Sie wissen jetzt genug, um ihre eigenen Bildbearbeitungsprogramme zu schreiben. Mit ein wenig mehr Übung können Sie ihre eigenen Filter schreiben, die die erstaunliche Leistung ihres Mac oder iPhone nutzen, um bisher ungeahnte Effekte zu erzielen.
Vielen Dank für ihren Besuch.