Bouw een foto-app met GPUImage

In deze zelfstudie leer je hoe je Instagram-achtige filters en speciale effecten toepast op afbeeldingen met het ongelooflijk krachtige GPUImage-project. Onderweg leert u hoe u een eenvoudige cameratoepassing kunt bouwen waarmee u nieuwe foto's kunt maken of bestaande afbeeldingen uit het fotoalbum kunt openen.


Projectdemo

Het bovenstaande is een collage van afbeeldingsfilters die zijn toegepast met de app. Deze zelfstudie leert je hoe je moet bouwen. De bronafbeelding is van ep.Sos.de op Flickr.


Stap 1: start een nieuw Xcode-project

Start Xcode en maak een nieuwe applicatie met behulp van de Single View-sjabloon.

Voor deze zelfstudie gebruiken we zowel storyboards als automatische referentietellingen, dus zorg ervoor dat u beide vakjes selecteert. Noem het project "PhotoFX" en lever een unieke bedrijfsidentificatie voor apparaattesten.


Stap 2: Maak de applicatie-interface

De applicatie-interface zal bestaan ​​uit een UINavigationBar voor de app-titel op de UIView en bewaar knop, a UIToolbar voor de knoppen voor album-, camera- en filterhulpprogramma's en een UIImageView-instelling voor aspectvulling voor het bekijken en wijzigen van geselecteerde afbeeldingen.

Open de MainStoryboard.storyboard het dossier. Selecteer de UIView op het scherm en selecteer vervolgens Editor> Insluiten> Navigatiecontroller.

Selecteer de UINavigationController en ga dan naar de Attributen Inspector in het paneel Utility. Zet de "Top Bar" drop-down op "Black Navigation Bar" en de "Statusbalk" op "Geen". Om de verwijdering van de statusbalk te voltooien, gaat u naar PhotoFx-Info.plist bestand en voeg een nieuwe sleutel toe met de tekst "Statusbalk is in eerste instantie verborgen". Stel de waarde in op "YES".

Omdat we onze app net hebben overgezet naar een UINavigationController sjabloon, ga nu naar ViewController.h en voeg de volgende gedelegeerde verklaring toe:

 #importeren  @interface ViewController: UIViewController  @einde

Dat hebben we later nodig.

Ga nu terug naar het Storyboard-bestand en dubbelklik op de titel in het midden van de UINavigationItem en vervang de standaardtekst door "PhotoFX".

Sleep een UIBarButtonItem van de Objectbibliotheek naar de UINavigationItem. Ga met het nieuwe knopitem geselecteerd naar het tabblad Kenmerken van het deelvenster Hulpprogramma's en stel de eigenschap Identifier van de knop in op 'Opslaan'. Schakel vervolgens met de knop "Opslaan" nog steeds het selectievakje "Ingeschakeld" voor deze knop uit. Hiermee wordt voorkomen dat de gebruiker probeert een afbeelding op te slaan voordat hij een afbeelding uit het fotoalbum laadt of een foto maakt met de camera (we zullen deze later weer inschakelen met code).

Keer terug naar de objectbibliotheek en sleep een UIToolbar op de main UIView. Voeg vervolgens een totaal van drie toe UIBarButtonItem objecten op de werkbalk. Wijzig de titeltekst voor de eerste knop naar "Album", stel de eigenschap Identifier in voor de tweede knop in op "Camera" en stel de titeltekst van de derde knop in op "Filter". De knop 'Filter' moet standaard zijn uitgeschakeld, net als de knop 'Opslaan' van boven.

Om de lay-out van de werkbalk te verbeteren, moeten we de lay-out rechts uitlijnen filter knop op de werkbalk. U kunt dit effect bereiken door een knooppuntitem met flexibele spatiestaaf te gebruiken, dat u eenvoudig vanuit de objectbibliotheek naar de werkbalk kunt slepen.

Let op hoe het wit in het oog springt UIView staat in contrast met de zwarte, glanzende navigatiebalk en werkbalk? Laten we daar iets aan doen. Selecteer de UIView en stel de achtergrondkleur in op "tungsten".

De enige subweergave die nog moet worden toegevoegd, is de hoofdweergave UIImageView gebruikt om de afbeelding van de gebruiker te tonen. Sleep een UIImageView uit de Objectbibliotheek en centreer het tussen de UINavigationItem en de UIToolbar. Trek de Attributen Inspector omhoog en selecteer "Aspect Fit" als de UIImageView modus onder de subsectie "View" Inspector.

Alle belangrijke componenten van de interface zijn nu aanwezig! De volgende stap is om deze elementen van het Storyboard naar de ViewController klasse.

Open de ViewController.m het dossier. Voeg het volgende toe IBOutlet eigenschappen en IBAction methoden in de ViewController klasse uitbreiding:

 @interface ViewController () @property (nonatomic, weak) IBOutlet UIImageView * selectedImageView; @property (nonatomic, weak) IBOutlet UIBarButtonItem * filterButton; @property (nonatomic, weak) IBOutlet UIBarButtonItem * saveButton; - (IBAction) photoFromAlbum; - (IBAction) photoFromCamera; - (IBAction) applyImageFilter: (id) afzender; - (IBAction) saveImageToAlbum; @einde

Dus, waarom creëren IBOutlet eigenschappen voor de afbeeldingsweergave, filterknop en opslagknop, maar niet voor de andere Interface Builder-componenten? Het antwoord is dat dit de enige objecten zijn die we nodig hebben om programmatisch toegang te krijgen. De beeldweergave wordt geopend om afbeeldingen in te stellen die door de gebruiker zijn geselecteerd, terwijl de filter- en bewaarknoppen worden gebruikt om de status van uitgeschakeld naar ingeschakeld in te schakelen nadat de gebruiker een afbeelding heeft geselecteerd of een foto maakt.

De IBAction methoden moeten grotendeels vanzelfsprekend zijn en rechtstreeks verbinding maken met de UIBarButtonItem selector geïmpliceerd door elke naam.

Na het maken van de IBOutlet eigenschappen, moet u ze synthetiseren door de volgende regel code aan de klas toe te voegen @implementatie:

 @implementation ViewController @synthesize selectedImageView, filterButton, saveButton;

Als u de installatie van Interface Builder wilt voltooien, wijst u elk van de bovenstaande toewijzingen toe IBOutlet objecten en IBAction methoden gedeclareerd bij de juiste Interface Builder-componenten (hulp nodig om dit te doen? Laat een vraag achter in de comments hieronder). Zorg ervoor dat u uw wijzigingen opslaat voordat u verdergaat.

Met de interface van de toepassing gemaakt, zijn we klaar om te beginnen met het coderen van de functionaliteit!


Stap 3: Foto's selecteren uit het album

Deze tutorial gebruikt de UIImagePickerController klasse voor directe toegang tot de afbeeldingen in het fotoalbum van de gebruiker. Als u deze klasse gebruikt, wordt er een modale galerijbrowser op de voorgrond geplaatst bovenop onze bestaande interface. Wanneer een gebruiker de gewenste afbeelding selecteert, gebruikt de kiezer delegatie om ons op de hoogte te stellen ViewController klasse dat er een selectie is gemaakt. Als je nog niet bekend bent met de ontwikkeling van iOS, maak je geen zorgen, dit is een stuk eenvoudiger dan het misschien klinkt.

In de ViewController.m bestand, voeg de volgende implementatie toe voor de photoFromAlbum methode:

 @synthesize selectedImageView, filterButton, saveButton; - (IBAction) photoFromAlbum UIImagePickerController * photoPicker = [[UIImagePickerController alloc] init]; photoPicker.delegate = zelf; photoPicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; [self presentViewController: photoPicker geanimeerd: YES completion: NULL]; 

Eenvoudig, toch? Nu moeten we gewoon het UIImagePickerController delegeer protocol om op beeldselecties te reageren. Je doet dit in stap 5 van deze tutorial.


Stap 4: Foto's maken met de camera

Er zijn twee primaire benaderingen voor het maken van foto's met de camera van het apparaat. U kunt de UIImagePickerController om toegang te krijgen tot de standaard camera-implementatie van Apple, of u kunt een volledig op maat gemaakte ervaring creëren met het AVFoundation-framework. GPUImage bouwt voort op de functionaliteit van AVFoundation om een ​​klasse specifiek met dit doel voor ogen te bieden. Voor deze zelfstudie gebruiken we echter UIImagePickerController uitsluitend voor fotoselectie. In een toekomstige tutorial over GPUImage (waarschijnlijk te publiceren in de komende 1-3 weken), laat ik je zien hoe je de meer geavanceerde GPUImage-klassen kunt gebruiken om dit te bereiken.

De code voor het maken van foto's in deze zelfstudie is als volgt:

 - (IBAction) photoFromCamera UIImagePickerController * photoPicker = [[UIImagePickerController alloc] init]; photoPicker.delegate = zelf; photoPicker.sourceType = UIImagePickerControllerSourceTypeCamera; [self presentViewController: photoPicker geanimeerd: YES completion: NULL]; 

Als u de bovenstaande methode vergelijkt met de photoFromAlbum methode van stap 3, je zult zien dat het enige verschil is of Bron Type ingesteld op UIImagePickerControllerSourceTypePhotoLibrary of UIImagePickerControllerSourceTypeCamera. Hierdoor zou je deze twee methoden gemakkelijk in één kunnen combineren. Ik heb echter besloten om te vertrekken photoFromCamera als een afzonderlijke methode zoals ik het opnieuw wil gaan gebruiken AVFoundation in een toekomstige zelfstudie en de logica zal moeten worden gescheiden.


Stap 5: Codeer de afzender van de fotokiezer

De gebruiker kan nu de apparaatbibliotheek doorbladeren of de apparaatcamera gebruiken om een ​​afbeelding met te selecteren UIImagePickerController. Ongeacht hoe de gebruiker een afbeelding selecteert, is de volgende stap het implementeren van de gedelegeerde methode die verantwoordelijk is voor het plaatsen van die afbeelding op het scherm.

Ga eerst naar ViewController.h en verklaren dat deze klasse zich zal conformeren UIImagePickerControllerDelegate:

 #importeren  @interface ViewController: UIViewController  @einde

Ga nu naar ViewController.m en implementeer de imagePickerController: didFinishPickingMediaWithInfo: delegeren methode genoemd door de fotokiezer bij selectie:

 - (void) imagePickerController: (UIImagePickerController *) photoPicker didFinishPickingMediaWithInfo: (NSDictionary *) info self.saveButton.enabled = YES; self.filterButton.enabled = YES; UIImage * selectedImage = [info valueForKey: UIImagePickerControllerOriginalImage]; [self.selectedImageView setImage: selectedImage]; [photoPicker dismissModalViewControllerAnimated: YES]; 

Op regels 3-4 hierboven zijn de opslag- en filterknoppen ingeschakeld omdat we nu een afbeelding hebben waarop die acties kunnen worden uitgevoerd.

Regel 6 maakt een UIImage object met de foto geselecteerd door de gebruiker, en regel 8 stelt de eigenschap image van de UIImageViewController naar de gekozen afbeelding die het op het scherm zal weergeven.

Ten slotte verwerpt regel 10 de modale weergave die is gebruikt om de foto te selecteren.

De bovenstaande code zou goed moeten werken, maar er is één verbetering nodig. In plaats van gewoon de afbeelding op te slaan die is geselecteerd in de selectedImageView, we zouden ook een kopie in een interne moeten bewaren UIImage data lid. Hierdoor kan de toepassing elk filter rechtstreeks op de originele afbeelding toepassen, in plaats van iteratief in lagen te plaatsen. Het stelt de gebruiker ook in staat om gemakkelijk vanuit een gefilterd perspectief terug te keren naar de originele afbeelding. Om dit te doen, voegt u eerst een toe UIImage bezwaar maken tegen de klasse-extensie bovenaan ViewController.m:

 #import "ViewController.h" #import "GPUImage.h" @interface ViewController () UIImage * originalImage;  @property (nonatomic, weak) IBOutlet UIImageView * selectedImageView; @property (nonatomic, weak) IBOutlet UIBarButtonItem * filterButton;

Wijzig vervolgens de imagePickerController: didFinishPickingMediaWithInfo: methode als volgt:

 - (void) imagePickerController: (UIImagePickerController *) photoPicker didFinishPickingMediaWithInfo: (NSDictionary *) info self.saveButton.enabled = YES; self.filterButton.enabled = YES; originalImage = [info valueForKey: UIImagePickerControllerOriginalImage]; [self.selectedImageView setImage: originalImage]; [photoPicker dismissModalViewControllerAnimated: YES]; 

Als u nu het project bouwt en uitvoert, moet u foto's rechtstreeks vanuit het apparaatalbum kunnen selecteren!


Stap 6: Het gekozen beeld opslaan

Het laatste wat we moeten doen voordat GPUImage wordt aangepakt, is dat gebruikers de foto's die ze maken kunnen opslaan met de camera van het apparaat. U kunt dit doen met een enkele regel code binnen de saveImageToAlbum methode:

 - (IBAction) saveImageToAlbum UIImageWriteToSavedPhotosAlbum (self.selectedImageView.image, self, @selector (afbeelding: didFinishSavingWithError: contextInfo :), nihil); 

De bovenstaande rij code probeert de afbeelding in het fotoalbum op te slaan, maar u moet de geselecteerde selector implementeren om te reageren op succes of mislukking:

 - (void) afbeelding: (UIImage *) afbeelding didFinishSavingWithError: (NSError *) error contextInfo: (void *) contextInfo NSString * alertTitle; NSString * alertMessage; if (! error) alertTitle = @ "Afbeelding opgeslagen"; alertMessage = @ "Afbeelding succesvol opgeslagen in fotoalbum.";  else alertTitle = @ "Fout"; alertMessage = @ "Kan niet opslaan in fotoalbum.";  UIAlertView * alert = [[UIAlertView alloc] initWithTitle: alertTitle-bericht: alertMeldagedenemer: self cancelButtonTitle: @ "Okay otherbuttonTitels: nihil]; [alarm show]; 

De bovenstaande coderegels zijn tamelijk rechtlijnig en geven eenvoudig een weer UIAlertView bericht dat de gebruiker op de hoogte brengt of de afbeelding al dan niet succesvol is opgeslagen.


Stap 7: GPUImage aan uw project toevoegen

Het toevoegen van GPUImage aan je project is een beetje lastiger dan je zou verwachten, maar door deze stap te volgen, zou het maar een paar minuten duren voordat je actief bent.

Eerst moet je een kopie van GPUImage downloaden van het officiële project GitHub. Unarchive het gedownloade bestand en open de map "framework". Dit zijn de essentiële bestanden die nodig zijn om GPUImage in uw project te importeren. In plaats van alles in je project te kopiëren, gebruik je Finder om naar de locatie te gaan waar je je Xcode-project hebt opgeslagen in stap 1 (voor mij is dit ~ / Desktop / PhotoFX). Maak een nieuwe map met de naam "Submodules" met een onderliggende map genaamd "GPUImage". Kopieer nu de map "framework" die is gedownload van GitHub en plak deze in de map "GPUImage". Open vervolgens de map "framework" en selecteer het GPUImage.xcodeproj-bestand. Uw scherm zou er ongeveer zo uit moeten zien:

Sleep nu het GPUImage.xcodeproj-bestand naar de Xcode Project Navigator. Als u dit met succes heeft gedaan, ziet u ongeveer het volgende:

Als het project met succes is toegevoegd, moet u GPUImage toevoegen als afhankelijkheid in de build-instellingen van uw app. Selecteer "PhotoFX" in de project-navigator, selecteer het doel "PhotoFX" en ga vervolgens naar het tabblad "Fasen opbouwen". Vouw de vervolgkeuzelijst "Target afhankelijkheden" uit en klik vervolgens op het pictogram "+". Selecteer "GPUImage" in de lijst die verschijnt. Bekijk de volgende afbeelding om een ​​idee te krijgen hoe dit wordt gedaan:

Nu moet je het libGPUImage.a-bestand slepen (te vinden in Project Navigator van Xcode op GPUImage.xcodeproj> Producten) naar de "Link Binary met bibliotheken" drop-down. Als dit is voltooid, ziet u iets als het volgende:

Terwijl u gefocust bent op het vervolgkeuzemenu Binary met bibliotheken koppelen, kunt u de volgende vereiste frameworks toevoegen door op de knop "+" in de linkerbenedenhoek te klikken:

  • CoreMedia
  • CoreVideo
  • OpenGLES
  • AVFoundation
  • QuartzCore

Bijna klaar! De volgende stap is om het PhotoFX-project te selecteren en naar "Build-instellingen" te gaan. Zoek naar "Header Search Paths" (mogelijk moet u de knop "All" selecteren in plaats van "Basic" om deze optie te laten verschijnen) en dubbelklik om toe te voegen Submodules / GPUImage / framework in het pop-upvenster dat verschijnt. Klik op het selectievakje naast het item om aan te geven dat dit pad recursief moet worden doorzocht. Als je dit goed hebt gedaan, zou je naar zoiets als het volgende moeten kijken:

De laatste stap is om terug te keren naar ViewController.m en voeg de volgende regel toe aan de bovenkant:

 #import "ViewController.h" # import "GPUImage.h"

Je zou nu in staat moeten zijn om het project zonder probleem te compileren en uit te voeren. Problemen hebben? Laat hieronder een reactie achter.


Stap 8: een lijst met filters weergeven

GPUImage wordt geleverd met een indrukwekkend aantal filters voor gebruik binnen uw toepassingen. Voor deze zelfstudie heb ik het volgende voorbeeld geselecteerd om onze voeten nat te krijgen:

  • GPUImageGrayscaleFilter
  • GPUImageSepiaFilter
  • GPUImageSketchFilter
  • GPUImagePixellateFilter
  • GPUImageColorInvertFilter
  • GPUImageToonFilter
  • GPUImagePinchDistortionFilter

Om uw eigen lijst te maken of gewoon alle filters die GPUImage te bieden heeft te bekijken, bekijk de officiële documentatie op GitHub.

Om de bovenstaande lijst met filters aan de gebruiker te presenteren, gebruiken we een eenvoudige UIActionSheet. Implementeer de applyImageFilter: methode als volgt:

 - (IBAction) applyImageFilter: (id) afzender UIActionSheet * filterActionSheet = [[UIActionSheet alloc] initWithTitle: @ "Selecteer Filter" delegate: self cancelButtonTitle: @ "Cancel" destructiveButtonTitle: nil otherButtonTitles: @ "Grayscale", @ "Sepia", @ "Sketch", @ "Pixellate", @ "Color Invert", @ "Toon", @ "Pinch Distort", @ "None", nihil]; [filterActionSheet showFromBarButtonItem: afzender geanimeerd: YES]; 

Stap 9: filterselectie implementeren

Om te reageren op de filterselectie, moeten we het UIActionSheetDelegate. Ga naar ViewController.h en verklaren dat de klas zich als volgt zal schikken naar deze afgevaardigde:

 #importeren  @interface ViewController: UIViewController  @einde

Ga nu terug naar ViewController.m en voeg de volgende methode toe:

 - (void) actionSheet: (UIActionSheet *) actionSheet clickedButtonAtIndex: (NSInteger) buttonIndex GPUImageFilter * selectedFilter; switch (buttonIndex) case 0: selectedFilter = [[GPUImageGrayscaleFilter alloc] init]; breken; case 1: selectedFilter = [[GPUImageSepiaFilter alloc] init]; breken; geval 2: selectedFilter = [[GPUImageSketchFilter alloc] init]; breken; case 3: selectedFilter = [[GPUImagePixellateFilter alloc] init]; breken; case 4: selectedFilter = [[GPUImageColorInvertFilter alloc] init]; breken; case 5: selectedFilter = [[GPUImageToonFilter alloc] init]; breken; case 6: selectedFilter = [[GPUImagePinchDistortionFilter alloc] init]; breken; case 7: selectedFilter = [[GPUImageFilter alloc] init]; breken; standaard: pauze;  UIImage * filteredImage = [selectedFilter imageByFilteringImage: originalImage]; [self.selectedImageView setImage: filteredImage]; 

Bam! Uw app zou nu naar wens moeten werken. Zoals je op basis van het bovenstaande kunt zien, kan het toepassen van filters op een bestaande afbeelding met GPUImage niet eenvoudiger zijn. U hoeft alleen maar een GPUImageFilter en bel dan de imageByFilteringImage: originalImage methode.


Stap 10: voeg een app-pictogram toe

Deze app heeft maar één ding nodig: een goed dock-pictogram. Dankzij onze zuster-site Psdtuts + kon ik precies vinden wat ik zocht:


Het bovenstaande is eenvoudigweg een 57x57 (niet-retina) en 114x114 (netvlies) pixeluitsnijding van het uiteindelijke effect dat wordt geleerd in Hoe een Leica-camera in Photoshop te tekenen door Mohammad Jeprie.

Om deze in uw app te krijgen, hoeft u ze alleen maar naar de Xcode Project Navigator te slepen.


Afronden

Deze tutorial heeft net het oppervlak van wat mogelijk is met GPUImage gekrast. Als je deze tutorial leuk vond of denkt dat je in de toekomst zult profiteren van de kracht van GPUImage, zoek dan @bradlarson en bedank hem voor het maken van zo'n geweldig open-sourceproject.


Meer GPUImage-inhoud?

Wilt u meer inhoud zien op GPUImage en beeldverwerking? Zo ja, laat het me weten! Je kunt je feedback achterlaten in de comments hieronder (voorkeur) of stuur me een bericht op Twitter (@markhammonds).

BIJWERKEN: Ik heb nu een tweede zelfstudie gepost met informatie over het gebruik van de GPUImage-camera en het weergeven van foto's in een galerij.
Een foto-app verbeteren met GPUImage en iCarousel.