1. 程式人生 > >關於iOS開發中圖片處理的一些積累(CoreGraphic、CoreImage、GPUImage、OpenGL)

關於iOS開發中圖片處理的一些積累(CoreGraphic、CoreImage、GPUImage、OpenGL)

Core Image

前言

貌似公司最近的專案都是和圖片處理有關,拍拍專案中需要將圖片處理成buffer傳到影象匹配拼接演算法中,需要從原圖中摳出一定範圍的影象再貼到新的背景圖中,需要對靜態圖片進行濾鏡操作等等,所以對這方面接觸的相對多一些。
拋開各種各樣的影象編解碼知識,其實影象資料本身就是一個Byte陣列,每一個顏色通道都可以用一個UInt32來表示。我們都知道,拿RGBA影象來說,我們可以用一個包含四個元素的a[] = {1,0,0,1}來表示一個紅色畫素,那這些畫素合起來就可以組成一個影象的buffer。UIImage有很多方法可以轉成影象buffer.
這裡

有一篇對影象進行基本的畫素處理的介紹,可以通過修改影象每個畫素的顏色通道值來改變影象的整體顏色風格,沒錯,濾鏡處理就是這個原理。

Core Graphics

Core Graphics框架是蘋果內建的基於Quartz 2D繪圖引擎的繪圖API,能夠對影象進行一些bitmap操作,總體來說,目前市場上的APP中大部分都用它來寫寫畫畫,像是一個畫板的感覺,能畫出你要的各種東西。這裡是一篇很詳細的教程。這裡(一)(二)詳細的介紹了CoreGrapthics的關係和一些使用細節,quartz2D框架在使用時需要我們自己手動來管理記憶體,而且context上下文的切換是一個非常耗效能的操作,所以使用起來要格外的注意效能的優化。

CoreImage

這是一篇比較早的關於CoreImage框架的介紹,介紹的很詳細,包括如何使用CIFiter內建的幾種濾鏡、使用框架內建的人臉檢測、固定顏色的透明度剔除、能夠移軸的高斯模糊效果等等。
但是文中關於CIImage概念的介紹有一些誤區。CIImage包含生成一個圖片的全部資料,不過確切的講CIImage並不同於UIImage、CGImageRef等是一個圖片物件,而是這個圖片的描述的抽象(引自官方原話)。
雖然使用CoreImage框架能夠滿足大部分日常圖片濾鏡處理之類的需求,而且是系統內建框架,效能上肯定是經過一番考究的,但是其API使用起來比較麻煩,尤其是基於對攝像頭資料流的實時濾鏡(目前大部分直播APP要求這個功能),所以如果專案中有這方面的需求,這裡推薦一個優秀的開源框架

GPUImage

GPUImage

GPUImage是目前比較主流的一款開源影象處理框架,有iOS版和安卓版,前一陣又出了swift版,在多平臺選擇性上沒有顧慮。GPUImage隱藏了在iOS中所有需要使用OpenGL ES的複雜的程式碼,並用極其簡單的介面以很快的速度處理影象。GPUImage的效能甚至在很多時候擊敗了Core Image。
GPUImage最大的特點就是使用簡便,它內部封裝了許多濾鏡,類似亮度濾鏡、對比度濾鏡、灰度濾鏡、雙邊濾波等等,而且還有許多現成的卡通,黑白版,高斯模糊之類的濾鏡效果。可以對stillimage靜態圖片進行處理,也可以建立camera並隨意組合濾鏡效果來構建一個攝像頭實時濾鏡。製作一些常用的濾鏡、磨皮美顏效果都很方便。
但是,GPUImage在記憶體上的處理可能並不是特別友好,使用不當時很容易造成一些記憶體洩露。我第一次使用GPUImage時有一個需求是一次性對32或64張圖片進行處理,濾鏡處理後我發現增加的記憶體沒有降下來,測試很久最終定位到如果去掉對結果圖的引用,就不會出現這個問題,所以這裡需要我們手動拷貝一份結果圖資料再去使用,否則可能會因此造成一些中間過程的記憶體得不到釋放而記憶體激增。下面貼一小段借來的程式碼示例

- (UIImage *)processUsingGPUImage:(UIImage*)input {
  // 1. Create the GPUImagePictures
  GPUImagePicture * inputGPUImage = [[GPUImagePicture alloc] initWithImage:input];
  UIImage * ghostImage = [self createPaddedGhostImageWithSize:input.size];
  GPUImagePicture * ghostGPUImage = [[GPUImagePicture alloc] initWithImage:ghostImage];
  // 2. Set up the filter chain
  GPUImageAlphaBlendFilter * alphaBlendFilter = [[GPUImageAlphaBlendFilter alloc] init];
  alphaBlendFilter.mix = 0.5;
  [inputGPUImage addTarget:alphaBlendFilter atTextureLocation:0];
  [ghostGPUImage addTarget:alphaBlendFilter atTextureLocation:1];
  GPUImageGrayscaleFilter * grayscaleFilter = [[GPUImageGrayscaleFilter alloc] init];
  [alphaBlendFilter addTarget:grayscaleFilter];
  // 3. Process & grab output image
  [grayscaleFilter useNextFrameForImageCapture];
  [inputGPUImage processImage];
  [ghostGPUImage processImage];
  UIImage * output = [grayscaleFilter imageFromCurrentFramebuffer];
  return output;
}

前不久,AR技術非常火,各種大大小小的AR眼鏡,AR頭盔隨處可見,筆者在一次衝動消費中得到了一個賣家贈的簡易版VR、AR一體眼鏡(AR時就是手機放在裡邊,戴在頭上時前邊的一個蓋子可以開啟露出手機攝像頭),研究了一下原理,無非就是將手機螢幕分為兩塊完全相同的區域(如果需要3d效果可以進行一些細小的錯位展示)然後通過內建的光學鏡片改變人眼的聚焦點,兩隻眼分別聚焦在兩塊區域上,出於興趣,自己基於GPUImage的實施濾鏡做了一個小demo,設定了兩塊相同濾鏡的相機預覽圖層。另外還做了一個基於AVFoundation自定義相機的版本,值得一提的是AVFoundation自定義相機並不支援多圖層預覽,要想做類似於這種多個預覽圖層效果的需要我們用在回撥中取得每一幀影象然後用OpenGL自己去繪製(蘋果中的GLKit簡化了openGLES的使用,可以將CVBuffer轉成CIImage後直接draw在GLKView中)。