Bygg et bildeapp med GPUImage

Denne opplæringen vil lære deg hvordan du bruker Instagram-lignende filtre og spesialeffekter til bilder med det utrolig kraftige GPUImage-prosjektet. Underveis lærer du hvordan du bygger et enkelt kameraprogram som kan enten ta nye bilder eller få tilgang til eksisterende bilder fra fotoalbumet..


Prosjektdemo

Ovenstående er en collage av bildefiltre som brukes med appen denne opplæringen vil lære deg hvordan du bygger. Kilden bildet er fra ep.Sos.de på Flickr.


Trinn 1: Start et nytt Xcode-prosjekt

Start Xcode og opprett et nytt program ved hjelp av Single View-malen.

For denne opplæringen bruker vi både Storyboards og Automatic Reference Counting, så vær sikker på at du velger begge boksene. Navngi prosjektet "PhotoFX" og leverer et unikt firmanavn for enhetstesting.


Trinn 2: Lag applikasjonsgrensesnittet

Programgrensesnittet vil bestå av a UINavigationBar for app tittelen på UIView og lagre knappen, a UIToolbar for knappene for album, kamera og filterverktøy, og et UIImageView-sett til aspektfylling for visning og modifisering av valgte bilder.

Åpne MainStoryboard.storyboard fil. Velg UIView på skjermen og velg deretter Redigerer> Legge inn i> Navigasjonsstyrer.

Velg UINavigationController og deretter gå til Attributtsinspektøren i Utility-panelet. Sett "Top Bar" ned til "Black Navigation Bar" og "Status Bar" til "None". For å fullføre statuslinjens fjerning, gå til PhotoFX-Info.plist fil og legg til en ny nøkkel med teksten "Statuslinjen er opprinnelig skjult". Sett verdien til "JA".

Fordi vi nettopp har overført vår app til en UINavigationController mal, bør du nå gå til ViewController.h og legg til følgende delegatdeklarasjon:

 #importere  @interface ViewController: UIViewController  @slutt

Vi trenger det senere.

Gå nå tilbake til Storyboard-filen og dobbeltklikk tittelen i midten av UINavigationItem og erstatt standardteksten med "PhotoFX".

Dra a UIBarButtonItem fra Objektbiblioteket til UINavigationItem. Når du har valgt det nye knappelementet, går du til fanen Attributter inspektør i Verktøy-panelet og angir knappens Identifier-egenskap til "Lagre". Deretter merkes avkryssingsboksen "Aktivert" for denne knappen, med "Save" -knappen fortsatt valgt. Dette forhindrer brukeren i å prøve å lagre et bilde før du enten laster inn en fra fotoalbumet eller tar et bilde med kameraet (vi aktiverer det med kode igjen senere).

Gå tilbake til objektbiblioteket og dra a UIToolbar på hovedsiden UIView. Neste legg til totalt tre UIBarButtonItem objekter på verktøylinjen. Endre tittelteksten for den første knappen til "Album", sett inn Identifier-egenskapen for den andre til "Kamera", og sett den tredje knappens titteltekst til "Filter". Filter-knappen skal deaktiveres som standard, akkurat som "Lagre" -knappen ovenfra.

For å polere verktøylinjeoppsettet må vi justere på høyre side filter knappen på verktøylinjen. Du kan oppnå denne effekten ved hjelp av en fleksibel mellomromknapp-knapp, som du bare kan dra på verktøylinjen fra Objekt-biblioteket.

Legg merke til hvordan den hvite er klar UIView står i kontrast til den svarte, skinnende navigeringslinjen og verktøylinjen? La oss gjøre noe med det. Velg UIView og sett bakgrunnsfargen til "wolfram".

Det eneste undervennene som skal legges til er hovedrollen UIImageView pleide å vise brukerens bilde. Dra a UIImageView fra Objektbiblioteket og senter det mellom UINavigationItem og UIToolbar. Trekk på Attribut Inspector og velg "Aspect Fit" som UIImageView modus under "View" Inspector-delen.

Alle hovedkomponentene i grensesnittet er nå på plass! Det neste trinnet er å koble disse elementene fra Storyboard til ViewController klasse.

Åpne ViewController.m fil. Legg til følgende IBOutlet egenskaper og IBAction metoder i ViewController klasse forlengelse:

 @interface ViewController () @property (ikkeatomisk, svak) IBOutlet UIImageView * selectedImageView; @property (ikkeatomisk, svak) IBOutlet UIBarButtonItem * filterButton; @property (ikkeatomisk, svak) IBOutlet UIBarButtonItem * saveButton; - (IBAction) photoFromAlbum; - (IBAction) photoFromCamera; - (IBAction) applyImageFilter: (id) avsender; - (IBAction) saveImageToAlbum; @slutt

Så hvorfor lage IBOutlet egenskaper for bildevisningen, filterknappen og lagre-knappen, men ikke de andre grensesnittbyggerens komponenter? Svaret er at dette er de eneste objektene som vi må ha tilgang til programmatisk. Bildevisningen åpnes for å angi bilder valgt av brukeren mens filteret og lagre knappene blir åpnet for å bytte tilstanden fra deaktivert til aktivert etter at brukeren har valgt et bilde eller tar et bilde.

De IBAction metodene skal være mest forklarende og vil koble direkte til UIBarButtonItem velgeren underforstått av hvert navn.

Etter å ha opprettet IBOutlet egenskaper, bør du syntetisere dem ved å legge til følgende linje med koden til klassen @gjennomføring:

 @implementation ViewController @synthesize selectedImageView, filterButton, saveButton;

For å fullføre oppsettet for grensesnittbygger, kartlegg hver av de ovennevnte IBOutlet objekter og IBAction metoder som er deklarert til de riktige grensesnittbyggerens komponenter (Trenger du hjelp til å gjøre dette? Legg igjen et spørsmål i kommentarfeltet nedenfor). Husk å lagre endringene dine før du går videre.

Med programmets grensesnitt opprettet, er vi klare til å begynne å kode funksjonaliteten!


Trinn 3: Velger bilder fra albumet

Denne opplæringen vil bruke UIImagePickerController klassen for direkte tilgang til bildene i brukerens fotoalbum. Ved å bruke denne klassen legger du over en modal visning galleri nettleser på toppen av vårt eksisterende grensesnitt. Når en bruker velger bildet de vil ha, vil plukkeren bruke delegasjon til å varsle vår ViewController klasse som et valg har blitt gjort. Hvis du er ny i iOS-utvikling, ikke bekymre deg, dette er mye enklere enn det kan høres ut.

I ViewController.m fil, legg til følgende implementering for photoFromAlbum metode:

 @synthesize selectedImageView, filterButton, saveButton; - (IBAction) photoFromAlbum UIImagePickerController * photoPicker = [[UIImagePickerController alloc] init]; photoPicker.delegate = selv; photoPicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; [self presentViewController: photoPicker animert: JA ferdigstillelse: NULL]; 

Enkelt, ikke sant? Nå trenger vi bare å implementere UIImagePickerController delegere protokollen for å svare på bildevalg. Du vil gjøre dette i trinn 5 i denne opplæringen.


Trinn 4: Ta bilder med kameraet

Det er to primære tilnærminger for å ta bilder med enhetskameraet. Du kan enten bruke UIImagePickerController for å få tilgang til Apples standardkameraimplementering, eller du kan opprette en helt tilpasset opplevelse med AVFoundation-rammen. GPUImage bygger faktisk på funksjonaliteten fra AVFoundation for å gi en klasse spesifikt med dette formålet i tankene. Men for denne opplæringen bruker vi UIImagePickerController for bildevalg utelukkende. I en fremtidig opplæring om GPUImage (sannsynligvis bli publisert i de neste 1-3 ukene), viser jeg deg hvordan du bruker de mer avanserte GPUImage-klassene for å oppnå dette.

Koden for å ta bilder i denne opplæringen er som følger:

 - (IBAction) photoFromCamera UIImagePickerController * photoPicker = [[UIImagePickerController alloc] init]; photoPicker.delegate = selv; photoPicker.sourceType = UIImagePickerControllerSourceTypeCamera; [self presentViewController: photoPicker animert: JA ferdigstillelse: NULL]; 

Hvis du sammenligner metoden ovenfor med photoFromAlbum metode fra trinn 3, vil du se at den eneste forskjellen er om Kildetype er satt til UIImagePickerControllerSourceTypePhotoLibrary eller UIImagePickerControllerSourceTypeCamera. På grunn av dette kan du enkelt kombinere disse to metodene i en. Men jeg har bestemt meg for å forlate photoFromCamera som en egen metode som jeg vil refactoring den å bruke Foundation- i en fremtidig opplæring og logikken må skilles.


Trinn 5: Kode for fotovalgsdelegatet

Brukeren kan nå bla gjennom enhetsbiblioteket eller bruke enhetskameraet til å velge et bilde med UIImagePickerController. Uansett hvordan brukeren velger et bilde, er neste trinn å implementere delegatemetoden som vil være ansvarlig for å plassere bildet på skjermen.

Gå først til ViewController.h og erklærer at denne klassen vil overholde UIImagePickerControllerDelegate:

 #importere  @interface ViewController: UIViewController  @slutt

Vend nå til ViewController.m og implementere imagePickerController: didFinishPickingMediaWithInfo: delegere metode kalt av fotografen ved valg:

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

På linjene 3-4 ovenfor er lagre- og filterknappene aktivert fordi vi nå har et bilde hvor disse handlingene kan tas.

Linje 6 skaper en UIImage objekt med bildet valgt av brukeren, og linje 8 setter bildegenskapen til UIImageViewController til det valgte bildet som vil vise det på skjermen.

Endelig avviser linje 10 modalvisningen som brukes til å velge bildet.

Ovennevnte kode skal fungere bra, men det er nødvendig med en forbedring. Snarere enn å bare lagre bildet som er valgt i selectedImageView, Vi bør også beholde en kopi i en intern UIImage data medlem. Dette vil tillate at applikasjonen skal søke hvert filter som er valgt direkte til det opprinnelige bildet, i stedet for iterativt å lagre effektene. Det vil også tillate brukeren å enkelt gå tilbake til det opprinnelige bildet fra et filtrert perspektiv. For å gjøre dette må du først legge til en UIImage motsette seg klassen utvidelsen øverst på ViewController.m:

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

Deretter endrer du imagePickerController: didFinishPickingMediaWithInfo: metode som følger:

 - (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]; 

Hvis du bygger og kjører prosjektet nå, bør du kunne velge bilder direkte fra enhetalbumet!


Trinn 6: Lagre det valgte bildet

Den siste tingen vi må gjøre før du håndterer GPUImage, er at brukerne kan lagre bildene de tar med enhetskameraet. Du kan gjøre dette med en enkelt linje med kode i saveImageToAlbum metode:

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

Linjen med kode over vil forsøke å lagre bildet i fotoalbumet, men du må implementere selgeren som er spesifisert for å svare på suksess eller fiasko:

 - (ugyldig) bilde: (UIImage *) bilde didFinishSavingWithError: (NSError *) feil contextInfo: (void *) contextInfo NSString * alertTitle; NSString * alertMessage; hvis (! feil) alertTitle = @ "Image Saved"; alertMessage = @ "Bilde lagret på fotoalbum med hell.";  else alertTitle = @ "Feil"; alertMessage = @ "Kunne ikke lagre i fotoalbum.";  UIAlertView * alert = [[UIAlertView tildeling] initWithTitle: alertTitle melding: alertMessage delegate: self cancelButtonTitle: @ "Okay" otherButtonTitles: null]; [varslingsutstilling]; 

Ovennevnte kodelinjer er ganske enkle og viser bare a UIAlertView melding som informerer brukeren om hvorvidt bildet ble lagret, eller ikke.


Trinn 7: Legg til GPUImage til prosjektet ditt

Å legge GPUImage til prosjektet ditt er litt vanskeligere enn du kanskje forventer, men ved å følge dette trinnet, bør det bare ta noen minutter for deg å være oppe.

Først må du laste ned en kopi av GPUImage fra det offisielle prosjektet GitHub. Unarchive den nedlastede filen og åpne mappen "ramme". Dette er de essensielle filene som trengs for å importere GPUImage i prosjektet. I stedet for å kopiere alle disse til prosjektet ditt direkte, bruk Finder til å gå til stedet du lagret ditt Xcode-prosjekt i trinn 1 (for meg som er ~ / Desktop / PhotoFX). Lag en ny mappe kalt "Submodules" med en barnemappe som heter "GPUImage". Kopier nå "rammeverk" -mappen lastet ned fra GitHub og lim den inn i mappen "GPUImage". Deretter åpner du mappen "ramme" og velger GPUImage.xcodeproj filen. Skjermen din bør se slik ut:

Dra nå GPUImage.xcodeproj filen i Xcode Project Navigator. Hvis du gjorde dette med hell, bør du se noe som følger:

Med prosjektet lagt til, må du legge til GPUImage som en avhengighet i appens bygginnstillinger. Velg "PhotoFX" fra prosjektnavigatoren, velg "PhotoFX" -mål, og gå deretter til "Build Phases" -fanen. Utvid rullegardinmenyen "Målavhengigheter" og klikk deretter på "+" -ikonet. Velg "GPUImage" fra listen som vises. Ta en titt på følgende bilde for å få en følelse av hvordan dette gjøres:

Nå må du dra libGPUImage.a-filen (funnet i Xcode's Project Navigator at GPUImage.xcodeproj> Produkter) til "Link binære med biblioteker" drop down. Med dette fullført, bør du se noe som følger:

Mens du er fokusert på «Link binære med biblioteker», fortsetter du og legger til følgende påkrevde rammer ved å klikke på "+" -knappen nederst i venstre hjørne:

  • CoreMedia
  • CoreVideo
  • OpenGLES
  • Foundation-
  • QuartzCore

Nesten ferdig! Det neste trinnet er å velge PhotoFX-prosjektet og gå til "Bygg innstillinger". Søk etter "Header Search Paths" (du må kanskje velge "Alle" -knappen i stedet for "Grunnleggende" for at dette alternativet skal vises), og dobbeltklikk for å legge til Submoduler / GPUImage / rammeverk i popup-dialogen som vil vises. Klikk avkrysningsboksen ved siden av oppføringen for å indikere at denne banen skal søkes rekursivt. Hvis du gjorde dette riktig, bør du se på noe som følger:

Det siste trinnet er å gå tilbake til ViewController.m og legg til følgende linje øverst:

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

Du bør nå kunne kompilere og kjøre prosjektet uten problem. Har problemer? Legg igjen en kommentar nedenfor.


Trinn 8: Vis en liste over filtre

GPUImage kommer med et imponerende antall filtre for bruk i dine applikasjoner. For denne opplæringen har jeg valgt følgende prøve for å få føttene våte:

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

For å lage din egen liste eller bare se alle filtre GPUImage har å tilby, sjekk ut den offisielle dokumentasjonen på GitHub.

For å presentere listen over filtre overfor brukeren bruker vi en enkel UIActionSheet. Gjennomfør applyImageFilter: metode som følger:

 - (IBAction) applyImageFilter: (id) sender UIActionSheet * filterActionSheet = [[UIActionSheet alloc] initWithTitle: @ "Velg filter" delegere: self cancelButtonTitle: @ "Cancel" destructiveButtonTitle: ingen andreButtonTitles: @ "Gråskala", @ "Sepia" @ "Sketch", @ "Pixellate", @ "Color Invert", @ "Toon", @ "Pinch Distort", @ "None", null); [filterActionSheet showFromBarButtonItem: sender animert: YES]; 

Trinn 9: Implementer filtervalg

For å reagere på filtervalget må vi implementere UIActionSheetDelegate. Gå til ViewController.h og erklærer at klassen vil overholde denne delegaten som følger:

 #importere  @interface ViewController: UIViewController  @slutt

Gå nå tilbake til ViewController.m og legg til følgende metode:

 - (void) actionSheet: (UIActionSheet *) actionSheet klikketButtonAtIndex: (NSInteger) buttonIndex GPUImageFilter * selectedFilter; bytt (buttonIndex) case 0: selectedFilter = [[GPUImageGrayscaleFilter alloc] init]; gå i stykker; tilfelle 1: selectedFilter = [[GPUImageSepiaFilter alloc] init]; gå i stykker; tilfelle 2: selectedFilter = [[GPUImageSketchFilter alloker] init]; gå i stykker; tilfelle 3: selectedFilter = [[GPUImagePixellateFilter alloc] init]; gå i stykker; tilfelle 4: selectedFilter = [[GPUImageColorInvertFilter alloc] init]; gå i stykker; sak 5: selectedFilter = [[GPUImageToonFilter alloker] init]; gå i stykker; tilfelle 6: selectedFilter = [[GPUImagePinchDistortionFilter alloc] init]; gå i stykker; sak 7: selectedFilter = [[GPUImageFilter alloker] init]; gå i stykker; standard: break;  Ullmage * filteredImage = [selectedFilter imageByFilteringImage: originalImage]; [self.selectedImageView setImage: filteredImage]; 

Bam! Appen din skal nå fungere som ønsket. Som du ser fra ovenstående, kan det ikke være enklere å bruke filtre på et eksisterende bilde med GPUImage. Du trenger bare å instantiere en GPUImageFilter og ring deretter imageByFilteringImage: originalImage metode.


Trinn 10: Legg til et App-ikon

Denne appen trenger bare en siste ting: en god dock-ikon. Takket være vår søster-side Psdtuts +, var jeg i stand til å finne akkurat det jeg lette etter:


Ovenstående er rett og slett en 57x57 (ikke-retina) og 114x114 (retina) pikselavling fra den endelige effekten som læres i Hvordan tegne et Leica-kamera i Photoshop av Mohammad Jeprie.

For å få disse inn i appen din, må du bare dra dem inn i Xcode Project Navigator.


Wrap Up

Denne opplæringen har bare knapt skrapt overflaten av det som er mulig med GPUImage. Hvis du har hatt denne opplæringen eller tror du vil ha glede av GPUImage i fremtiden, kan du finne @bradlarson og takke ham for å skape et så bra åpen kildekodeprosjekt.


Mer GPUImage-innhold?

Ønsker du å se mer innhold på GPUImage og bildebehandling? I så fall, gi meg beskjed! Du kan enten legge igjen din tilbakemelding i kommentarfeltet nedenfor (foretrukket) eller bare send meg en melding på Twitter (@markhammonds).

OPPDATER: Jeg har nå lagt opp en annen opplæring som beskriver hvordan du bruker GPUImage-kameraet og viser bilder i et galleri.
Forbedre en bildeapp med GPUImage og iCarousel.