Статьи

Создайте приложение для фотографий с помощью GPUImage

Из этого туториала вы узнаете, как применять Instagram-фильтры и специальные эффекты к изображениям с помощью невероятно мощного проекта GPUImage. Попутно вы узнаете, как создать простое приложение для камеры, способное делать новые фотографии или получать доступ к существующим изображениям из фотоальбома.


Конечный эффект

Выше приведен коллаж из фильтров изображений, применяемых вместе с приложением, которое этот урок научит вас создавать. Исходное изображение из ep.Sos.de на Flickr .


Запустите Xcode и создайте новое приложение, используя шаблон Single View.

Рисунок 1: Выбор шаблона проекта

В этом уроке мы будем использовать раскадровки и автоматический подсчет ссылок, поэтому не забудьте выбрать оба поля. Назовите проект «PhotoFX» и предоставьте уникальный идентификатор компании для тестирования устройства.

Рисунок 2: Экран настройки проекта

Интерфейс приложения будет состоять из UINavigationBar для заголовка приложения на UIView и кнопке сохранения, UIToolbar для UIToolbar альбома, камеры и фильтра, а также UIImageView, настроенного на заполнение аспекта для просмотра и изменения выбранных изображений.

Рисунок 3: Изображения интерфейса приложения

Откройте файл MainStoryboard.storyboard . Выберите UIView на экране, а затем выберите « Редактор»> « UIView > «Контроллер навигации» .

Рисунок 4: Изображения интерфейса приложения

Выберите UINavigationController а затем перейдите к Инспектору атрибутов на панели «Утилиты». Установите в раскрывающемся списке «Верхняя панель» значение «Черная панель навигации», а в строке состояния — «Нет». Чтобы завершить удаление строки состояния, перейдите в файл PhotoFX-Info.plist и добавьте новый ключ с текстом «Панель состояния изначально скрыта». Установите значение «ДА».

Поскольку мы только что перевели наше приложение в шаблон UINavigationController , теперь вы должны перейти к ViewController.h и добавить следующее объявление делегата:

1
2
3
4
5
#import <UIKit/UIKit.h>
 
@interface ViewController : UIViewController <UINavigationControllerDelegate>
 
@end

Нам это понадобится позже.

Теперь вернитесь к файлу раскадровки и дважды щелкните заголовок в центре UINavigationItem и замените текст по умолчанию на «PhotoFX».

Перетащите UIBarButtonItem из библиотеки объектов на UINavigationItem . Выбрав новый элемент кнопки, перейдите на вкладку «Инспектор атрибутов» панели «Утилиты» и установите для свойства идентификатора кнопки значение «Сохранить». Затем, когда кнопка «Сохранить» все еще выбрана, снимите флажок «Включено» для этой кнопки. Это предотвратит попытку пользователя сохранить изображение перед его загрузкой из фотоальбома или выполнением снимка с помощью камеры (позже мы включим его с кодом).

Рисунок 5: Изображения интерфейса приложения

Вернитесь в библиотеку объектов и перетащите UIToolbar на главный UIView . Затем добавьте в общей сложности три объекта UIBarButtonItem на панели инструментов. Измените текст заголовка для первой кнопки на «Альбом», установите для свойства идентификатора второй значение «Камера» и установите для текста заголовка третьей кнопки значение «Фильтр». Кнопка «Фильтр» должна быть отключена по умолчанию, как и кнопка «Сохранить» сверху.

Чтобы отшлифовать макет панели инструментов, нам нужно выровнять правую кнопку filter на панели инструментов. Вы можете добиться этого эффекта, используя элемент кнопки «Гибкая клавиша пробела», который можно просто перетащить на панель инструментов из библиотеки объектов.

Рисунок 6: Изображения интерфейса приложения

Обратите внимание, насколько яркий белый UIView отличается от черной блестящей панели навигации и панели инструментов? Давайте сделаем что-нибудь об этом. Выберите UIView и установите цвет фона «вольфрам».

Единственное подпредставление, которое осталось добавить, — это основной UIImageView используемый для отображения изображения пользователя. Перетащите UIImageView из библиотеки объектов и расположите его по центру между UINavigationItem и UIToolbar . Поднимите Инспектор Атрибутов и выберите «Подгонка UIImageView » в качестве режима UIImageView в подразделе «Просмотр» Инспектора.

Все основные компоненты интерфейса теперь на месте! Следующим шагом является ViewController этих элементов из Storyboard к классу ViewController .

Откройте файл ViewController.m . Добавьте следующие свойства IBOutlet и методы IBAction в ViewController класса ViewController :

01
02
03
04
05
06
07
08
09
10
11
12
@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)sender;
— (IBAction)saveImageToAlbum;
 
@end

Итак, зачем создавать свойства IBOutlet для вида изображения, кнопки фильтра и кнопки сохранения, но не для других компонентов Interface Builder? Ответ в том, что это единственные объекты, к которым нам нужен программный доступ. Доступ к просмотру изображений будет доступен для установки изображений, выбранных пользователем, а кнопки фильтра и сохранения будут доступны для переключения состояния с отключенного на включенное после того, как пользователь выберет изображение или сделает фотографию.

Методы IBAction должны быть в основном понятными и подключаться непосредственно к селектору UIBarButtonItem , подразумеваемому каждым именем.

После создания свойств IBOutlet вы должны синтезировать их, добавив следующую строку кода в класс @implementation :

1
2
3
@implementation ViewController
 
@synthesize selectedImageView, filterButton, saveButton;

Чтобы завершить настройку Interface Builder, сопоставьте каждый из вышеуказанных объектов IBOutlet и методов IBAction объявленных с соответствующими компонентами Interface Builder (Нужна помощь в этом? Оставьте вопрос в разделе комментариев ниже). Обязательно сохраните изменения, прежде чем двигаться дальше.

Создав интерфейс приложения, мы готовы приступить к написанию кода!


В этом руководстве будет использоваться класс UIImagePickerController для прямого доступа к изображениям в фотоальбоме пользователя. Использование этого класса наложит браузер модальной галереи вида поверх нашего существующего интерфейса. Когда пользователь выбирает желаемое изображение , средство выбора будет использовать делегирование, чтобы уведомить наш класс ViewController о том, что выбор сделан. Если вы новичок в разработке для iOS, не волнуйтесь, это намного проще, чем может показаться.

В файле ViewController.m добавьте следующую реализацию для метода photoFromAlbum :

01
02
03
04
05
06
07
08
09
10
11
@synthesize selectedImageView, filterButton, saveButton;
 
— (IBAction)photoFromAlbum
{
    UIImagePickerController *photoPicker = [[UIImagePickerController alloc] init];
    photoPicker.delegate = self;
    photoPicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
     
    [self presentViewController:photoPicker animated:YES completion:NULL];
 
}

Просто, правда? Теперь нам просто нужно реализовать протокол делегата UIImagePickerController , чтобы отвечать на выбор изображений. Вы сделаете это на шаге 5 этого урока.


Существует два основных подхода к фотографированию с помощью камеры устройства. Вы можете использовать UIImagePickerController для доступа к реализации камеры Apple по умолчанию или создать полностью настраиваемый интерфейс с помощью инфраструктуры AVFoundation. GPUImage фактически основывается на функциональности, предоставляемой AVFoundation, чтобы обеспечить класс специально для этой цели. Однако для этого урока мы будем использовать UIImagePickerController исключительно для выбора фотографий. В следующем руководстве по GPUImage (которое, вероятно, будет опубликовано в ближайшие 1-3 недели), я покажу вам, как использовать более продвинутые классы GPUImage для достижения этой цели.

Код для фотографирования в этом уроке выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
— (IBAction)photoFromCamera
{
    UIImagePickerController *photoPicker = [[UIImagePickerController alloc] init];
 
    photoPicker.delegate = self;
    photoPicker.sourceType = UIImagePickerControllerSourceTypeCamera;
     
    [self presentViewController:photoPicker animated:YES completion:NULL];
 
}

Если вы сравните метод выше с методом photoFromAlbum из шага 3, вы увидите, что единственное отличие состоит в том, установлен ли для sourceType значение UIImagePickerControllerSourceTypePhotoLibrary или UIImagePickerControllerSourceTypeCamera . Из-за этого вы можете легко объединить эти два метода в один. Тем не менее, я решил оставить photoFromCamera как отдельный метод, так как я буду рефакторинг его для использования AVFoundation в будущем уроке, и логика должна быть отделена.


Теперь пользователь может UIImagePickerController библиотеку устройства или использовать камеру устройства для выбора изображения с помощью UIImagePickerController . Независимо от того, как пользователь выбирает изображение, следующим шагом является реализация метода делегата, который будет отвечать за размещение этого изображения на экране.

Сначала перейдите к ViewController.h и объявите, что этот класс будет соответствовать UIImagePickerControllerDelegate :

1
2
3
4
5
#import <UIKit/UIKit.h>
 
@interface ViewController : UIViewController <UINavigationControllerDelegate, UIImagePickerControllerDelegate>
 
@end

Теперь перейдите к ViewController.m и реализуйте imagePickerController:didFinishPickingMediaWithInfo: метод делегата, вызываемый imagePickerController:didFinishPickingMediaWithInfo: выбора фотографий при выборе:

01
02
03
04
05
06
07
08
09
10
11
— (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];
}

В строках 3-4 выше кнопки «Сохранить» и «Фильтр» включены, потому что теперь у нас есть изображение, на котором можно выполнить эти действия.

Строка 6 создает объект UIImage с фотографией, выбранной пользователем, а строка 8 устанавливает свойство изображения UIImageViewController для выбранного изображения, которое будет отображать его на экране.

Наконец, строка 10 отклоняет модальное представление, используемое для выбора фотографии.

Приведенный выше код должен хорошо работать, но есть одно усовершенствование. Вместо простого сохранения изображения, выбранного в selectedImageView , мы также должны сохранить копию во внутреннем UIImage данных UIImage . Это позволит приложению применять каждый выбранный фильтр непосредственно к исходному изображению, а не итеративно накладывать эффекты. Это также позволит пользователю легко вернуться к исходному изображению с отфильтрованной точки зрения. Для этого сначала добавьте объект UIImage в расширение класса в верхней части ViewController.m :

01
02
03
04
05
06
07
08
09
10
#import «ViewController.h»
#import «GPUImage.h»
 
@interface ViewController ()
{
    UIImage *originalImage;
}
 
@property(nonatomic, weak) IBOutlet UIImageView *selectedImageView;
@property(nonatomic, weak) IBOutlet UIBarButtonItem *filterButton;

Затем измените imagePickerController:didFinishPickingMediaWithInfo: метод следующим образом:

01
02
03
04
05
06
07
08
09
10
11
— (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];
}

Если вы соберете и запустите проект сейчас, вы сможете выбрать фотографии прямо из альбома устройства!


Последнее, что нам нужно сделать перед началом работы с GPUImage, — это позволить пользователям сохранять фотографии, сделанные на камеру устройства. Вы можете сделать это с помощью одной строки кода в методе saveImageToAlbum :

1
2
3
4
— (IBAction)saveImageToAlbum
{
    UIImageWriteToSavedPhotosAlbum(self.selectedImageView.image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
}

Приведенная выше строка кода попытается сохранить изображение в фотоальбоме, но вам потребуется реализовать указанный селектор, чтобы реагировать на успех или неудачу:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
— (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo
{
    NSString *alertTitle;
    NSString *alertMessage;
     
    if(!error)
    {
        alertTitle = @»Image Saved»;
        alertMessage = @»Image saved to photo album successfully.»;
    }
    else
    {
        alertTitle = @»Error»;
        alertMessage = @»Unable to save to photo album.»;
    }
 
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:alertTitle
                                                    message:alertMessage
                                                   delegate:self
                                          cancelButtonTitle:@»Okay»
                                          otherButtonTitles:nil];
    [alert show];
}

Вышеуказанные строки кода довольно просты и просто отображают сообщение UIAlertView уведомляющее пользователя о том, было ли изображение успешно сохранено.


Добавление GPUImage к вашему проекту немного сложнее, чем вы могли бы ожидать, но для выполнения этого шага потребуется всего несколько минут.

Во-первых, вам необходимо скачать копию GPUImage с официального проекта GitHub . Разархивируйте загруженный файл и откройте папку «framework». Это основные файлы, необходимые для импорта GPUImage в ваш проект. Вместо того, чтобы копировать все это в ваш проект напрямую, используйте Finder, чтобы перейти в папку, в которой вы сохранили ваш проект XCode в шаге 1 (для меня это ~ / Desktop / PhotoFX). Создайте новую папку с именем «Submodules» с дочерней папкой с именем «GPUImage». Теперь скопируйте папку «framework», загруженную с GitHub, и вставьте ее в папку «GPUImage». Затем откройте папку «framework» и выберите файл GPUImage.xcodeproj. Ваш экран должен выглядеть примерно так:

Рисунок 7: Добавление GPUImage к проекту

Теперь перетащите файл GPUImage.xcodeproj в Xcode Project Navigator. Если вы сделали это успешно, вы должны увидеть что-то вроде следующего:

Рисунок 8: GPUImage в Xcode

После успешного добавления проекта вам нужно добавить GPUImage в качестве зависимости в настройках сборки вашего приложения. Выберите «PhotoFX» в навигаторе проекта, выберите цель «PhotoFX», а затем перейдите на вкладку «Build Phases». Разверните раскрывающийся список «Целевые зависимости» и нажмите значок «+». Выберите «GPUImage» из появившегося списка. Посмотрите на следующее изображение, чтобы понять, как это делается:

Рисунок 9: Зависимость GPUImage

Теперь вам нужно перетащить файл libGPUImage.a (находится в Навигаторе проектов XCode в GPUImage.xcodeproj> Продукты ) в раскрывающийся список «Связать двоичные файлы с библиотеками». После этого вы должны увидеть что-то вроде следующего:

Рисунок 10: GPUImage Framework

Пока вы сосредоточены на раскрывающемся списке «Связать двоичные файлы с библиотеками», добавьте следующие необходимые рамки, нажав кнопку «+» в нижнем левом углу:

  • CoreMedia
  • CoreVideo
  • OpenGLES
  • AVFoundation
  • QuartzCore

Почти сделано! Следующий шаг — выбрать проект PhotoFX и перейти в «Настройки сборки». Выполните поиск «Пути поиска в заголовке» (вам может потребоваться выбрать кнопку «Все» вместо «Основные», чтобы появилась эта опция), а затем дважды щелкните, чтобы добавить Submodules/GPUImage/framework во всплывающем диалоговом окне, которое будет появляются. Установите флажок рядом с записью, чтобы указать, что этот путь следует искать рекурсивно. Если вы сделали это правильно, вы должны выглядеть примерно так:

Рисунок 11: Добавление GPUImage Framework

Последний шаг — вернуться к ViewController.m и добавить следующую строку вверху:

1
2
#import «ViewController.h»
#import «GPUImage.h»

Теперь вы должны быть в состоянии скомпилировать и запустить проект без проблем. Возникли проблемы? Оставьте комментарий ниже.


GPUImage поставляется с впечатляющим количеством фильтров для использования в ваших приложениях. Для этого урока я выбрал следующий пример, чтобы намочить ноги:

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

Чтобы создать собственный список или просто просмотреть все фильтры, которые предлагает GPUImage, ознакомьтесь с официальной документацией на GitHub .

Чтобы предоставить пользователю приведенный выше список фильтров, мы будем использовать простой UIActionSheet . applyImageFilter: метод следующим образом:

01
02
03
04
05
06
07
08
09
10
— (IBAction)applyImageFilter:(id)sender
{
    UIActionSheet *filterActionSheet = [[UIActionSheet alloc] initWithTitle:@»Select Filter»
                                                                   delegate:self
                                                          cancelButtonTitle:@»Cancel»
                                                     destructiveButtonTitle:nil
                                                          otherButtonTitles:@»Grayscale», @»Sepia», @»Sketch», @»Pixellate», @»Color Invert», @»Toon», @»Pinch Distort», @»None», nil];
 
    [filterActionSheet showFromBarButtonItem:sender animated:YES];
}

Чтобы ответить на выбор фильтра, нам нужно реализовать UIActionSheetDelegate . Перейдите к ViewController.h и объявите, что класс будет соответствовать этому делегату следующим образом:

1
2
3
4
5
#import <UIKit/UIKit.h>
 
@interface ViewController : UIViewController <UINavigationControllerDelegate, UIImagePickerControllerDelegate, UIActionSheetDelegate>
 
@end

Теперь вернитесь к ViewController.m и добавьте следующий метод:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
— (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
    GPUImageFilter *selectedFilter;
     
    switch (buttonIndex) {
        case 0:
            selectedFilter = [[GPUImageGrayscaleFilter alloc] init];
            break;
        case 1:
            selectedFilter = [[GPUImageSepiaFilter alloc] init];
            break;
        case 2:
            selectedFilter = [[GPUImageSketchFilter alloc] init];
            break;
        case 3:
            selectedFilter = [[GPUImagePixellateFilter alloc] init];
            break;
        case 4:
            selectedFilter = [[GPUImageColorInvertFilter alloc] init];
            break;
        case 5:
            selectedFilter = [[GPUImageToonFilter alloc] init];
            break;
        case 6:
            selectedFilter = [[GPUImagePinchDistortionFilter alloc] init];
            break;
        case 7:
            selectedFilter = [[GPUImageFilter alloc] init];
            break;
        default:
            break;
    }
     
    UIImage *filteredImage = [selectedFilter imageByFilteringImage:originalImage];
    [self.selectedImageView setImage:filteredImage];
}

Бам! Ваше приложение должно теперь работать как нужно. Как видно из вышесказанного, применение фильтров к существующему изображению с помощью GPUImage не может быть проще. Вам просто нужно создать экземпляр GPUImageFilter и затем вызвать метод imageByFilteringImage:originalImage .


Это приложение просто нуждается в последней вещи: хорошая иконка док-станции. Благодаря нашему дочернему сайту Psdtuts + я смог найти именно то, что искал:


Выше приведен просто обрезка пикселей 57х57 (без сетчатки) и 114х114 (сетчатка) от конечного эффекта, описанного в книге « Как нарисовать камеру Leica в Photoshop » Мохаммада Джепри.

Чтобы вставить их в ваше приложение, вам просто нужно перетащить их в Xcode Project Navigator.


В этом уроке только поверхностно рассказано о возможностях GPUImage. Если вам понравилось это руководство или вы думаете, что в будущем вы сможете воспользоваться мощью GPUImage, найдите @bradlarson и поблагодарите его за создание такого замечательного проекта с открытым исходным кодом.


Хотите увидеть больше контента о GPUImage и обработке изображений? Если так, дайте мне знать! Вы можете оставить свой отзыв в разделе комментариев ниже ( желательно ) или просто отправить мне сообщение в Twitter ( @markhammonds ).

ОБНОВЛЕНИЕ: я теперь опубликовал второй учебник, который детализирует, как использовать камеру GPUImage и показывать фотографии в галерее.
Улучшение приложения Photo с помощью GPUImage и iCarousel .