1. 程式人生 > >IOS影象處理之 coreImage.

IOS影象處理之 coreImage.

   最近的專案中,要用到影象處理,做一些圖片的模糊效果,查了很多資料,最後用一些網上比較山寨的模糊演算法完成了任務。但是總覺得模糊處理的過程中記憶體消耗多、且不流暢。在趕完專案進度後,我又繼續尋找改進方法,最終還是回到了蘋果自有框架之 coreImage。coreImage的強大,是我們很難想象的,不僅可以做圖片處理,還可以做人臉識別等多種工作,我不是專業搞圖片處理軟體的,只是用了其中很小的一部分功能,下面轉載一篇別人寫的coreImage使用的文章,大家可以熟悉一下流程。另外,github上也有許多非常好的關於coreImage的開原始碼,在github主頁搜尋 coreImage就可以了。

請大家看下面的轉載內容。

轉自 風雲的blog ,連結為。

http://blog.csdn.net/miracle_of_thinking/article/details/8141051

1.coreImage的介紹

         coreImage是IOS5中新加入的一個Objective-c的框架,提供了強大高效的影象處理功能,用來對基於畫素的影象進行操作與分析。IOS提供了很多強大的濾鏡(Filter),其中IOS5中有48種,而到了最新的IOS6 Filter已經增加到了93種之多,並且這一數字會繼續增加。這些Filter提供了各種各樣的效果,並且還可以通過濾鏡鏈將各種效果的Filter疊加起來,形成強大的自定義效果,如果你對該效果很滿意,還可以子類化濾鏡。

2.coreImage框架中的物件

2.1 CIImage

CIImage是CoreImage框架中最基本代表影象的物件,他不僅包含元影象資料,還包含作用在原影象上的濾鏡鏈。這裡我想特別強調的是CIImage和其他影象是不同的,在CIImage被CIContext渲染出來之前,他是依賴於濾鏡鏈的,濾鏡是不會更改CIImage中的影象資料。這個需要正確理解,不然會給你的程式造成錯誤。說到了CIImage的不同,就必須得提一下如何建立CIImage了,CIImage是不能直接有UIImage轉化而來的,有以下幾種建立CIImage的類方法:

  1. 1.CIImage*image=[CIImage imageWithContentsOfURL:myURL];  
  2. 2.CIImage*image=[CIImage imageWithData:myData];  
  3. 3.CIImage*image=[CIImage imageWithCGImage:myCgimage];  
  4. 4.CIImage*image=[CIImage imageWithCVPixelBuffer:CVBuffer];  

2.2 CIFilter

CIFilter用來表示CoreImage提供的各種濾鏡。濾鏡使用鍵-值來設定輸入值,一旦這些值設定好,CIFilter就可以用來生成新的CIImage輸出影象了。記住,這裡的輸出的影象不會進行實際的影象渲染,他只包含一個對輸入影象的引用以及需要應用與資料上的濾鏡鏈。IOS永遠在最佳的時間選擇渲染影象。

CIFilter提供了一個簡單的方法查詢可用的濾鏡種類:[CIFilterfilterNamesInCategory:kCICategoryBuiltIn];//搜尋屬於 kCICategoryBuiltIn類別的所有濾鏡名字,返回一個數組;

[CIFilterfilterNamesInCategories];//搜尋所有可用的濾鏡名稱;

呼叫[CIFilter attributes]會返回filter詳細資訊,下面我們以一個具體列子來看看他返回的資訊。

下面是我程式返回的一個叫做CISepiaTone濾鏡返回的詳細資訊:

  1. 2012-09-18 16:17:09.155 SZFYKJHomeWorkVersion1[2836:f803] {  
  2.     CIAttributeFilterCategories =     (//濾鏡所示種類,通常一個濾鏡可以屬於幾種
  3.         CICategoryColorEffect,       //總類,這只是根據濾鏡效果,作用來分類的
  4.         CICategoryVideo,             //可以用種類名來搜尋Fileter;
  5.         CICategoryInterlaced,  
  6.         CICategoryNonSquarePixels,  
  7.         CICategoryStillImage,  
  8.         CICategoryBuiltIn  
  9.     );  
  10.     CIAttributeFilterDisplayName = "Sepia Tone";  
  11.     CIAttributeFilterName = CISepiaTone;        //濾鏡的名稱,通過該名稱來
  12.                                     //呼叫濾鏡,具體見下面例項
  13.  inputImage =     {                 //濾鏡使用需要輸入的引數,該
  14.         CIAttributeClass = CIImage;     //引數型別為CIImage。
  15.         CIAttributeType = CIAttributeTypeImage;  
  16.     };  
  17.     inputIntensity =     {              //輸入強度,引數的名稱
  18.         CIAttributeClass = NSNumber;        //型別
  19.         CIAttributeDefault = 1;         //預設值
  20.         CIAttributeIdentity = 0;              
  21.         CIAttributeMax = 1;             //最大值
  22.         CIAttributeMin = 0;             //最小值
  23.         CIAttributeSliderMax = 1;  
  24.         CIAttributeSliderMin = 0;  
  25.         CIAttributeType = CIAttributeTypeScalar;  
  26.     };  
  27. }  
  28. 程式中使用CISepiaTone的程式碼為:CIFilter *filter = [CIFilter filterWithName:@"CISepiaTone"];   
  29. [filter setValue:inputImage forKey:@"inputImage"];  
  30. [filter setValue:[NSNumber numberWithFloat:0.8] forKey:@"inputIntensity"];  

大家可以 [CIFilterfilterNamesInCategories]返回所有的濾鏡,並檢視他們的引數來熟悉各個濾鏡的使用方法。

2.3 CIContext

CIContext用來渲染CIImage,將作用在CIImage上的濾鏡鏈應用到原始的圖片資料中。CIContext可以是基於CPU的,也可以是基於GPU的,這兩種渲染的區別是:使用CPU渲染的IOS會採用GCD來對影象進行渲染,這保證了CPU渲染在大部分情況下更可靠,比CPU渲染更容易使用,他可以在後臺實現渲染過程;而GPU渲染方式使用OpenGL ES2.0來渲染影象,這種方式CPU完全沒有負擔,應用程式的執行迴圈不會受到影象渲染的影響,而且他渲染比CPU渲染更快但是GPU渲染無法在後臺執行。

對於如何選擇更好的渲染方式,我認為應該視具體情況而定:對於複雜的影象濾鏡使用GPU更好,但是如果在處理視訊並儲存檔案,或儲存照片到照片庫中時為避免程式退出對圖片儲存造成影響,這時應該使用CPU進行渲染。預設情況是用CPU渲染的。
  1. CIContext *context = [CIContext contextWithOptions:[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:kCIContextUseSoftwareRenderer]];//CPU渲染

渲染後的圖片使用:

1.imageView中使用:

  1. // Create the CIContext to render into
  2.   CIContext *context = [CIContext context];  
  3. // Get outputImage from the last filter in chain
  4.   CIImage *ciimage = [filter outputImage];  
  5. // Render the CIImage into a CGImageRef
  6.   CGImageRef cgimg = [context createCGImage:ciimage fromRect:[ciimage extent]];  
  7. // Create a UIImage from the CGImageRef
  8. UIImage *uiimage = [UIImage imageWithCGImage:cgimg scale:1.0f  
  9. orientation:ui_orientation([ciimage properties])];  
  10. CGImageRelease(cgimg);  
  11. // Use the UIImage in an UIImageView
  12. imageView.image = uiimage;  

2.將圖片儲存到photoLibrary

  1. // Create a CGImage from the CIImage
  2.  CIImage *outputImage = [filter outputImage];  
  3.  CGImageRef cgimage = [cpu_context createCGImage:outputImage  
  4.                              fromRect:[outputImage extent]];  
  5.  // Add the CGImage to the photo library
  6.  ALAssetsLibrary *library = [ALAssetsLibrary new];  
  7.  [library writeImageToSavedPhotosAlbum:cgimage  
  8.                               metadata:[outputImage properties]  
  9.       completionBlock:^(NSURL *assetURL NSError *error) {  
  10.         CGImageRelease(cgimg);  
  11. }];  

2.4 CIDetector和CIFeature

         CIDetector用來分析CIImage,得到CIFeature。每個CIDetector都要用一個探測器來初始化,這個型別高數探測器要在影象中尋找什麼特徵。

         當一個CIDetector分析一張圖片時,返回一個探測到的CIFeature的陣列,如果CIDetector 被初始化為尋找面孔,那麼返回的陣列會被填上CIFaceFeature物件,每個CIFaceFeature都包含一個面部的CGrect引用(按照影象的座標系),以及檢測到的面孔的左眼,右眼,嘴部位置的CGPoint;

  1. CIDetector *faceDetector = [CIDetector   
  2.                                     detectorOfType:CIDetectorTypeFace  
  3.                                     context:self.imageContext   
  4.                                     options:options];                
  5. NSArray *faces = [faceDetector featuresInImage:coreImage  
  6.                                                options:nil];  
  7. for(CIFaceFeature *face in faces){  
  8.             coreImage = [CIFilter filterWithName:@"CISourceOverCompositing"
  9.                                    keysAndValues:kCIInputImageKey, [self makeBoxForFace:face],  
  10.                          kCIInputBackgroundImageKey, coreImage, nil].outputImage;  
  11.         }  

3 注意事項

1 CoreImage在IOS上有很高的效率,但是濾鏡和渲染操作也會對主執行緒造成影響。應該將CoreImage濾鏡渲染操作放在後臺執行緒執行,當這些操作介紹後在返回主執行緒進行介面的更新。

  1. dispatch_async(  
  2.        dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),  
  3.        ^(void){  
  4.            //CGImageRef cgImage = [self autoAdjustImage];
  5.            NSArray *filters;  
  6.            // Create Core Image
  7.            CGImageRef cgImg = self.imageView.image.CGImage;  
  8.            CIImage *coreImage = [CIImage imageWithCGImage:cgImg];  
  9.            // Iterate through all of our filters and apply
  10.            // them to the CIImage
  11.            for(CIFilter *filter in filters){  
  12.                [filter setValue:coreImage forKey:kCIInputImageKey];  
  13.                coreImage = filter.outputImage;  
  14.            }  
  15.            // Create a new CGImageRef by rendering through CIContext
  16.            // This won't slow down main thread since we're in a background
  17.            // dispatch queue
  18.            CGImageRef newImg = [self.imageContext createCGImage:coreImage   
  19.                                                        fromRect:[coreImage extent]];  
  20.            dispatch_async(dispatch_get_main_queue(), ^(void){  
  21.                // Update our image view on the main thread
  22.                // You can also perform any other UI updates needed
  23.                // here such as hidding activity spinners
  24.                self.imageView.image = [UIImage imageWithCGImage:newImg];  
  25.                [self.adjustSpinner stopAnimating];  
  26.                [sender setEnabled:YES];  
  27.            });  
  28.        });  

上面這段程式碼,就是為了防止阻塞主執行緒,用GCD非同步執行濾鏡與渲染操作,在獲取渲染後的照片以後,返回主執行緒進行介面的更新。(完整的程式見本文末連線)

2 不要重複應用濾鏡,即使是同一個濾鏡也不要應用兩次,因為濾鏡後輸出照片包含濾鏡鏈,在進行照片渲染是會將濾鏡鏈效果疊加到原始資料上,這時會造成問題。比如,有一個CIImage,上面配置了強度為0.5的棕色濾鏡,現在通過滑塊將強度改為0.6,這個濾鏡應該用在新的CIImage上,如果不是新的CIImage上,那麼原來的CIImage中將包含強度為0.5和0.6的棕色濾鏡,而我們只想0.6的棕色濾鏡,這樣就造成錯誤,這一點在編寫程式的時候一定要切忌。

3 app中應用的濾鏡太多,改變速率太快,如果是根據滑塊來產生事件的話,一定要注意在使用滑條值前要首先判斷更改的濾鏡當前是否正在起作用,如果該濾鏡正在生成新的渲染圖片,則應該這次滑塊的更新。這一點也是很重要的,弄的不好常常導致程式崩潰,出現記憶體洩露問題。

這些問題常常會導致程式的崩潰.

4 總結

CoreImage處理影象的流程:

1:建立一個新的CIImage;

2:建立一個行的CIFIlter,並通過鍵-值設定各種輸入值,這些值有些是有預設值的,有些沒有預設值,需要程式設計者的設定;

3:衝CIFilter中生成輸出影象,如果存在濾鏡鏈則將輸出影象作為輸入引數傳入到下一個濾鏡,跳回步驟2繼續進行,如果到達濾鏡末,則呼叫CIContext渲染CIImage物件。這個context可以是基於CPU或GPU的,基於CPU的產出CGImageRef物件,基於GPU的呼叫OpenGL ES在螢幕上畫出結果,預設是基於CPU的。

在使用CoreImage時,一定要記住CIImage物件在開始時不會操作影象資料,知道使用CIContext渲染圖片是才會這麼做。還要記住最好在後臺執行影象處理的操作,然後在主執行緒中修改介面。

大家有機會可以參考一下IOS5核心框架中的CoreImage章節和AVFoundation章節,下面在貼一個書本中的列子的連線,我認為很有助理解。http://ioscoreframeworks.com/download/