core image 入門

原文連接:http://1199game.com/2016/06/B...
core image是一個強大的圖像處理框架,使用core image能夠方便的實現濾鏡,好比能夠修改圖像的形變,色彩和曝光。它使用GPU(或者是CPU)來處理圖像數據,因此特別快,能夠實時處理視頻的每一幀。ios

core image的多個濾鏡能夠合在一塊兒,同一時間處理視頻幀或者圖像。當多個濾鏡合在一塊兒使用的時候效率更高,由於合在一塊兒的時候,只須要建立一個合成的濾鏡來處理圖像,若是分開的話,每個濾鏡都要建立。git

每個濾鏡都本身的參數,這些參數能夠在代碼中獲取。也能夠查詢系統能夠提供的各類濾鏡。iOS平臺上可以使用的濾鏡比Mac上的要少,是Mac上的一個子集。github

在這篇文章中,我會手把手的教你使用core image。會教你使用各類濾鏡,你會發現對圖片實現一個很cool的效果是很簡單的。app

core image概述

開始以前,先簡單介紹一下core image framework裏面重要的幾個類。

· CIContext:core image的全部處理都是由CIContext來完成的,這有點相似於Core Graphics和openGL的context。

· CIImage: 這個類持有圖片的數據,能夠用UIImage來建立,也可使用圖片文件或者像素數據來建立。

· CIFliter:這個類表示一個濾鏡,它有許多屬性,在內部由一個dictionary來維護。濾鏡的種類有不少,好比是圖片形變的濾鏡,能夠改變圖片色彩的濾鏡,能夠裁剪圖片的濾鏡,等等
框架

接下來在你的工程中會使用到這些類。ide

開始

打開Xcode,建立一個新的single view application工程,工程名爲CoreImageFun,選擇Device爲iPhone。優化

建立好工程的第一件事就是把Core Image framework加入到工程中,在Mac平臺,coreimage framework是QuartzCore framework的一部分,在iOS平臺上它是獨立的framework。怎麼給工程添加framework,這裏就不細說了,不會的請查看相關文檔。spa

接下來,下載項目中會用到的資源,資源下下來以後加入到你的工程中。code

接下來,在viewdidload裏面新建一個UIImageView,代碼以下orm

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    self.view.backgroundColor = [UIColor whiteColor];
    
    _imageView = [[UIImageView alloc] init];
    [self.view addSubview:_imageView];
    _imageView.bounds = CGRectMake(0, 0, self.view.width, 300);
    _imageView.centerX = self.view.width / 2.0f;
    _imageView.top = 0;
    //.......
}

注:項目用到的定位

基本的濾鏡效果

接下來實現一個很簡單的濾鏡效果,顯示在imageview上。

每次實現濾鏡的效果,基本上都會包含一下四個步驟:

一、建立CIImage對象:CIImage有不少初始化方法,好比, imageWithURL:, imageWithData:, imageWithCVPixelBuffer:, 以及 imageWithBitmapData:bytesPerRow:size:format:colorSpace:,大多數狀況下,使用imageWithURL: 就能夠了。
二、建立CIContext對象:一個CIContext對象可使用CPU,也可使用GPU。CIContext能夠重用,因此,不須要重複的建立CIContext對象。
三、建立CIFilter對象:當建立一個CIFliter對象的時候,能夠根據你須要實現的濾鏡效果給它設置一系列的屬性。
四、獲取filter的輸出對象:CIFilter會根據你設置的屬性輸出一個符合你預期效果的一個圖片,這個圖片是CIImage類型的對象,能夠用CIContext轉換成UIImage.

請看代碼是如何實現的。在ViewController.m文件的viewDidLoad:方法裏面添加如下代碼:

// 1
NSString *filePath =
  [[NSBundle mainBundle] pathForResource:@"image" ofType:@"png"];
NSURL *fileNameAndPath = [NSURL fileURLWithPath:filePath];
 
// 2
CIImage *beginImage =
  [CIImage imageWithContentsOfURL:fileNameAndPath];
 
// 3
CIFilter *filter = [CIFilter filterWithName:@"CISepiaTone"
                              keysAndValues: kCIInputImageKey, beginImage,
                    @"inputIntensity", @0.8, nil];
CIImage *outputImage = [filter outputImage];
 
// 4
UIImage *newImage = [UIImage imageWithCIImage:outputImage];
self.imageView.image = newImage;

上面代碼的意思是:

一、前兩行代碼建立一個NSURL對象,用來表示image的路徑。

二、接下來用imageWithContentsOfURL方法建立一個CIImage對象。

三、建立一個CIFilter對象,這個對象的構造方法裏面傳了兩個參數,第一個參數是濾鏡的名字,第二個參數是一個dictionary,表示濾鏡的一些參數。

CISepiaTone只須要兩個參數,KCIInputImageKey和@」inputIntensity」,KCIInputImageKey表示一個圖片。inputIntensity是一個float類型的值,用nsnumber包裝,它的範圍是0-1,這裏設置的是0.8。大多數濾鏡都有默認值,若是沒有手動設置的話就用默認值。除了CIImage,它沒有默認值。獲取使用濾鏡以後的圖片很簡單,只須要獲取outputImage屬性就好了。


四、一旦你獲取了output CIImage,就能夠把它轉換成一個UIImage。在iOS6下可使用UIImage的方法 +imageWithCIImage: 來完成這個步驟,這個方法使用一個CIImage對象來建立一個UIImage。一旦你建立了一個UIImage對象,就能夠把它顯示在image view 上了。

編譯運行,你會看到一個通過濾鏡處理後的圖片,恭喜你,如今你已經學會使用CIImage和CIFilter了!!!!,效果以下:

使用Context

在繼續教程以前,你須要知道一個小小的優化。

以前提到,CIContext是用來處理CIFliter的,可是在上面的例子中卻沒有用到CIContext。這是由於上面的例子直接使用了UIImage的imageWithCIImage方法,在imageWithCIImage內部已經把全部的工做都作了,它在內部建立了一個CIContext對象,而後用這個對象獲取了output Image。

這樣作有一個缺點-每次都會建立一個CIContext對象,其實CIContext對象是能夠重複使用的。若是使用一個slide控件更新filter的值,若是用上面的代碼的話,每次都會建立一個CIContext對象,這樣的話效率很是低。

接下來對上面的代碼進行改進,刪除上面的代碼,用下面的代碼替換。

CIImage *beginImage =
  [CIImage imageWithContentsOfURL:fileNameAndPath];
 
// 1
CIContext *context = [CIContext contextWithOptions:nil];
 
CIFilter *filter = [CIFilter filterWithName:@"CISepiaTone"
                              keysAndValues: kCIInputImageKey, beginImage,
                    @"inputIntensity", @0.8, nil];
CIImage *outputImage = [filter outputImage];
 
// 2
CGImageRef cgimg =
  [context createCGImage:outputImage fromRect:[outputImage extent]];
 
// 3
UIImage *newImage = [UIImage imageWithCGImage:cgimg];
self.imageView.image = newImage;
 
// 4
CGImageRelease(cgimg);

下面解釋下上面的代碼:

一、這裏建立了一個CIContext對象,它的構造方法裏面傳了一個dictionary,這個dictionary能夠執行顏色以及是否使用cpu或者gpu,在這裏,咱們使用默認的屬性就好了,因此傳一個nil便可。

二、使用context對象來建立CGImage,調用createCGImage:fromRect:便可,這樣就建立了一個CGImageRef。

三、接下來使用UIImage +imageWithCGImage把上一步建立的CGImage轉換爲UIImage。

四、最後,釋放CGImageRef,CGImage是C API的,須要手動管理內存,即便是在ARC模式下。

編譯運行,確保結果和以前的同樣。

在這裏例子中,單首創建一個CIContext對象和以前的方法差異不是很大,可是,接下來的例子裏,你會發現差異會很大。

改變filter的值

接下來咱們添加一個slide控件,用這個控件實時改變filter的值。

界面以下:

建立的代碼以下:

一、建立slider控件
_amountSlider = [[UISlider alloc] init];
    [self.view addSubview:_amountSlider];
    _amountSlider.bounds = CGRectMake(0, 0, self.view.width - 40, 44);
    _amountSlider.top = _imageView.bottom + 20.0f;
    _amountSlider.centerX = self.view.width / 2.0f;
    [_amountSlider addTarget:self action:@selector(amountSliderValueChanged:) forControlEvents:UIControlEventValueChanged];
二、回調方法
- (void)amountSliderValueChanged:(UISlider *)slider {
    NSLog(@"amountSliderValueChanged...");
    float slideValue = slider.value;
    [filter setValue:@(slideValue) forKey:@"inputIntensity"];
    CIImage *outputImage = [filter outputImage];
    CGImageRef cgimg = [context createCGImage:outputImage fromRect:[outputImage extent]];
    UIImage *newImage = [UIImage imageWithCGImage:cgimg];
    _imageView.image = newImage;
    CGImageRelease(cgimg);
}

每一次改變slider的值,你須要從新對圖片進行濾鏡的效果。可是,你不須要每次都作所有的事情,這樣會效率很低下。你只須要改變一點點東西,因此一些須要改成全局變量。

最重要的事情就是重用CIContext對象,若是每次都去建立,效率過低了。另外就是把cifilter和初始的ciimage對象放到全局變量中,這些對象是能夠重用的。

因此須要把這三個變量看成viewcontroller的成員變量,代碼以下:

@implementation ViewController {
    CIContext *context;
    CIFilter *filter;
    CIImage *beginImage;
}

viewdidload裏面的初始化方法也要相應的改變。

beginImage = [CIImage imageWithContentsOfURL:fileNameAndPath];
context = [CIContext contextWithOptions:nil];
 
filter = [CIFilter filterWithName:@"CISepiaTone" 
  keysAndValues:kCIInputImageKey, beginImage, @"inputIntensity", 
  @0.8, nil];

如今須要實現changevalue的方法,在這個方法裏須要改變CIFilter的dictionary裏的@」inputIntensity」對應的值,一旦改變了這個值,接下來就須要執行如下三步:

一、獲取CIFilter的output image。

二、把ciimage轉爲CGImageRef。

三、把CGImageRef轉爲UIImage,而且顯示在image view上。

因此在amountSliderValueChanged裏的代碼以下:

- (void)amountSliderValueChanged:(UISlider *)slider {
    NSLog(@"amountSliderValueChanged...");
    float slideValue = slider.value;
    [filter setValue:@(slideValue) forKey:@"inputIntensity"];
    CIImage *outputImage = [filter outputImage];
    CGImageRef cgimg = [context createCGImage:outputImage fromRect:[outputImage extent]];
    UIImage *newImage = [UIImage imageWithCGImage:cgimg];
    _imageView.image = newImage;
    CGImageRelease(cgimg);
}

這段代碼從slider獲取float value,slide的value從0-1,恰好和CIFilter裏的值的範圍同樣,這樣很方便,能夠直接拿來用。

編譯運行,你移動slide,你會發現不一樣的效果,效果以下:

從相冊裏獲取照片

如今能夠動態的改變filter的value,這看起來頗有意思。可是僅僅是對這個固定的圖片進行濾鏡很沒勁,下面使用UIImagePickerController獲取相冊的圖片,並驚醒濾鏡處理。

建立一個按鈕,用來打開相冊。界面以下:

實現這個按鈕的點擊方法loadPhoto,代碼以下:

- (IBAction)loadPhoto:(id)sender {
    UIImagePickerController *pickerC = 
      [[UIImagePickerController alloc] init];
    pickerC.delegate = self;
    [self presentViewController:pickerC animated:YES completion:nil];
}

第一行實例化一個UIImagePickerController,並把self設爲它的delegate。

這裏會有一個警告,你須要實現UIImagePickerControllerDelegate 和UINaviationControllerDelegate協議。代碼以下:

@interface ViewController () <UIImagePickerControllerDelegate, UINavigationBarDelegate>
@end

- (void)imagePickerController:(UIImagePickerController *)picker 
  didFinishPickingMediaWithInfo:(NSDictionary *)info {
    [self dismissViewControllerAnimated:YES completion:nil];
    NSLog(@"%@", info);
}
 
- (void)imagePickerControllerDidCancel:
  (UIImagePickerController *)picker {
    [self dismissViewControllerAnimated:YES completion:nil];
}

在這兩個方法中,都是dimiss UIPickerController。

第一個方法尚未徹底實現,下面是完整的實現:

- (void)imagePickerController:(UIImagePickerController *)picker
  didFinishPickingMediaWithInfo:(NSDictionary *)info {
    [self dismissViewControllerAnimated:YES completion:nil];
    UIImage *gotImage =
      [info objectForKey:UIImagePickerControllerOriginalImage];
    beginImage = [CIImage imageWithCGImage:gotImage.CGImage];
    [filter setValue:beginImage forKey:kCIInputImageKey];
    [self amountSliderValueChanged:self.amountSlider];
}

完整的例子在這裏

參考文獻:http://www.raywenderlich.com/22167/beginning-core-image-in-ios-6

相關文章
相關標籤/搜索