原文連接: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 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上。
每次實現濾鏡的效果,基本上都會包含一下四個步驟:
請看代碼是如何實現的。在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了!!!!,效果以下:
在繼續教程以前,你須要知道一個小小的優化。
以前提到,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對象和以前的方法差異不是很大,可是,接下來的例子裏,你會發現差異會很大。
接下來咱們添加一個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