GPUImage解析(一) —— 基本概覽(一)

版本記錄

版本號 時間
V1.0 2017.09.01

前言

GPUImage是直接利用顯卡實現視頻或者圖像處理的技術。

作者

先看一下GPUImage

下面給出該框架的地址。
GPUImage - GitHub

下面我們就看一下該框架的作者。

從上面可以看見:

  • 作者不僅寫了OC上的框架GPUImage
  • 同樣寫了Swift上的框架GPUImage2,這里我們暫時只關注OC上的代碼。

總體概括

GPUImage框架是一個BSD許可的iOS庫,可讓您將GPU加速過濾器和其他效果應用于圖像,實況相機視頻和電影。

Core Image(iOS 5.0的一部分)相比,GPUImage允許您編寫自己的自定義過濾器,支持部署到iOS 4.0,并具有更簡單的界面。
然而,它目前缺乏Core Image的一些更先進的功能,如面部檢測。

對于大規模并行操作(如處理圖像或實時視頻幀),GPU具有比CPU更顯著的性能優勢。
在iPhone 4上,與基于CPU的等效過濾器相比,簡單的圖像濾鏡可以在GPU上執行的速度提升100倍以上。

然而,在GPU上運行自定義過濾器需要大量代碼來為這些過濾器設置和維護一個OpenGL ES 2.0渲染目標。
我創建了一個示例項目:

http://www.sunsetlakesoftware.com/2010/10/22/gpu-accelerated-video-processing-mac-and-ios

并發現在其創建中有很多樣板代碼。
因此,我將這個框架放在一起,封裝了處理圖像和視頻時遇到的許多常見任務,并使其不需要關心OpenGL ES 2.0基礎。

當處理視頻時,該框架與Core Image相比,在iPhone 4上僅使用2.5 ms從相機上傳幀,應用伽馬濾鏡和顯示,而對于使用Core Image的相同操作,則為106 ms?;贑PU的處理需要460 ms,使GPUImage比Core Image快40倍,在此硬件上進行此操作,比CPU處理速度快184倍。在iPhone 4S上,GPUImage在這種情況下只比Core Image快4倍,比CPU綁定處理快了102倍。

然而,對于更大的半徑處的高斯模糊等更復雜的操作,Core Image目前超過GPUImage。


技術要求

  • OpenGL ES 2.0:使用此功能的應用程序不會在原始iPhone,iPhone 3G以及第一代和第二代iPod觸摸屏上運行
  • iOS 4.1作為部署目標(4.0沒有一些擴展需要電影閱讀)。如果您想在靜態照片中顯示實時視頻預覽,則需要iOS 4.3作為部署目標。
  • iOS 5.0 SDK來構建
  • 設備必須有相機才能使用相機功能(顯然)
  • 該框架使用自動引用計數(ARC),但是如下所述,應支持使用ARC和手動引用計數的項目,如果作為子項目添加。對于針對iOS 4.x的手動引用計數應用程序,您需要為應用程序項目的其他鏈接器標志添加-fobjc-arc。

基本架構

GPUImage使用OpenGL ES 2.0著色器執行圖像和視頻操作比在CPU綁定例程中可以做得更快。然而,它隱藏了在簡化的Objective-C接口中與OpenGL ES API交互的復雜性。此接口允許您定義圖像和視頻的輸入源,在鏈中附加濾鏡,并將結果處理的圖像或視頻發送到屏幕,UIImage或磁盤上的影片。

圖像或視頻幀從源對象(GPUImageOutput的子類)上傳。這些包括GPUImageVideoCamera(用于iOS相機的實時視頻),GPUImageStillCamera(用于拍攝相機),GPUImagePicture(靜態圖像)和GPUImageMovie(適用于電影)。源對象將靜態圖像幀上傳到OpenGL ES作為紋理,然后將這些紋理關閉到處理鏈中的下一個對象。

鏈中的過濾器和其他后續元素符合GPUImageInput協議,允許它們從鏈中的上一個鏈接中獲取提供或處理的紋理,然后使用它。將鏈中的一個進一步的對象視為目標,并且可以通過向單個輸出或過濾器添加多個目標來分支處理。

例如,從攝像機接收實時視頻的應用程序將該視頻轉換為棕褐色調,然后在屏幕上顯示視頻將設置鏈條,如下所示:

GPUImageVideoCamera -> GPUImageSepiaFilter -> GPUImageView

將靜態庫添加到您的iOS項目

注意:如果要在Swift項目中使用它,則需要使用“將其添加為框架”部分而不是以下步驟中的步驟。Swift需要第三方代碼的模塊。

一旦您獲得了框架的最新源代碼,將其添加到您的應用程序是非常簡單的。首先將GPUImage.xcodeproj文件拖到應用程序的Xcode項目中,將框架嵌入到項目中。接下來,轉到應用程序的目標,并將GPUImage添加為目標依賴關系。最后,您將要將libGPUImage.a庫從GPUImage框架的Products文件夾拖動到應用程序目標中的鏈接二進制庫與構建階段。

GPUImage需要將其他幾個框架鏈接到您的應用程序中,因此您需要在應用程序目標中添加以下鏈接庫:

  • CoreMedia
  • Corevideo
  • OpenGLES
  • AVFoundation
  • QuartzCore

您還需要找到框架標題,因此在項目的構建設置中,將標題搜索路徑設置為從應用程序到GPUImage源目錄中的框架/子目錄的相對路徑。使此標題搜索路徑遞歸。

要在應用程序中使用GPUImage類,只需使用以下內容包含核心框架頭:

#import "GPUImage.h"

注意:如果在嘗試使用Interface Builder構建接口時遇到錯誤Interface Builder中的未知類GPUImageView,則可能需要在項目的構建設置中將-ObjC添加到其他鏈接器標志。

另外,如果您需要將其部署到iOS 4.x,似乎當前版本的Xcode(4.3)要求您將最終應用程序中的Core Video框架鏈接到弱鏈接,或者看到有“符號未找到”的崩潰:_CVOpenGLESTextureCacheCreate,當您創建上傳到App Store或臨時分發的存檔。為此,請轉到項目的Build Phases選項卡,展開“使用庫的鏈接二進制”組,然后在列表中找到CoreVideo.framework。將列表中最右側的設置更改為“必需”至“可選”。

另外,這是一個支持ARC的框架,所以如果你想在一個手動引用計數的iOS 4.x的應用程序中使用它,你還需要添加-fobjc-arc到你的其他鏈接器標志。

在命令行中構建一個靜態庫

如果您不想將項目作為依賴項包含在應用程序的Xcode項目中,則可以為iOS模擬器或設備構建通用靜態庫。為此,請build.sh在命令行運行。生成的庫和頭文件將位于build/Release-iphone。您也可以通過更改IOSSDK_VER變量build.sh(可以使用所有可用的版本)來更改iOS SDK的版本xcodebuild -showsdks。


執行常見任務

1. Filtering live video - 過濾直播視頻

要從iOS設備的相機中過濾實時視頻,您可以使用以下代碼:

GPUImageVideoCamera *videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPreset640x480 cameraPosition:AVCaptureDevicePositionBack];
videoCamera.outputImageOrientation = UIInterfaceOrientationPortrait;

GPUImageFilter *customFilter = [[GPUImageFilter alloc] initWithFragmentShaderFromFile:@"CustomShader"];
GPUImageView *filteredVideoView = [[GPUImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, viewWidth, viewHeight)];

// Add the view somewhere so it's visible

[videoCamera addTarget:customFilter];
[customFilter addTarget:filteredVideoView];

[videoCamera startCameraCapture];

這將設置一個來自iOS設備背面攝像頭的視頻源,使用預設可以捕獲640x480。這個視頻被捕獲在界面處于縱向模式,其中橫向左照相機需要在顯示之前使其視頻幀旋轉。然后將使用文件CustomShader.fsh中的代碼的自定義過濾器設置為來自相機的視頻幀的目標。這些過濾的視頻幀最終在UIView子類的幫助下顯示在屏幕上,UIView子類可以呈現由此管道產生的過濾的OpenGL ES紋理。

可以通過設置其fillMode屬性來更改GPUImageView的填充模式,以便如果源視頻的寬高比與視圖的寬高比不同,則視頻將被拉伸,以黑色條為中心,或者縮放以填充。

對于混合過濾器和其他占用多個映像的過濾器,您可以創建多個輸出,并為這兩個輸出添加一個過濾器作為目標。將輸出添加為目標的順序將影響輸入圖像混合或以其他方式處理的順序。

另外,如果要啟用麥克風音頻捕獲來錄制電影,則需要將相機的audioEncodingTarget設置為影片writer,如下所示:

videoCamera.audioEncodingTarget = movieWriter;

2. Capturing and filtering a still photo - 捕獲和過濾靜態照片

要捕獲和過濾靜態照片,您可以使用類似于過濾視頻的過程。而不是GPUImageVideoCamera,您可以使用GPUImageStillCamera

stillCamera = [[GPUImageStillCamera alloc] init];
stillCamera.outputImageOrientation = UIInterfaceOrientationPortrait;

filter = [[GPUImageGammaFilter alloc] init];
[stillCamera addTarget:filter];
GPUImageView *filterView = (GPUImageView *)self.view;
[filter addTarget:filterView];

[stillCamera startCameraCapture];

這將為您提供靜態相機預覽視頻的實時,過濾的Feed。請注意,此預覽視頻僅在iOS 4.3及更高版本上提供,因此如果希望具有此功能,則可能需要將其設置為部署目標。

一旦您想要拍攝照片,您可以使用如下回調塊:

[stillCamera capturePhotoProcessedUpToFilter:filter withCompletionHandler:^(UIImage *processedImage, NSError *error){
    NSData *dataForJPEGFile = UIImageJPEGRepresentation(processedImage, 0.8);

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];

    NSError *error2 = nil;
    if (![dataForJPEGFile writeToFile:[documentsDirectory stringByAppendingPathComponent:@"FilteredPhoto.jpg"] options:NSAtomicWrite error:&error2])
    {
        return;
    }
}];

上述代碼捕獲由預覽視圖中使用的相同過濾器鏈處理的全尺寸照片,并將照片作為JPEG保存到應用程序的文檔目錄中。

請注意,由于紋理尺寸限制,該框架目前無法處理大于2048像素寬或高的舊設備(iPhone 4S,iPad 2或Retina iPad之前)的圖像。這意味著iPhone 4的照相機輸出的照片比此更大,將無法捕獲這樣的照片。正在實施平鋪機制來解決這個問題。所有其他設備都應該能夠使用此方法捕獲和過濾照片。

3. Processing a still image - 處理靜止圖像

有幾種方法來處理靜態圖像并創建結果。您可以通過創建靜態圖像源對象并手動創建過濾器鏈來實現此方法。

UIImage *inputImage = [UIImage imageNamed:@"Lambeau.jpg"];

GPUImagePicture *stillImageSource = [[GPUImagePicture alloc] initWithImage:inputImage];
GPUImageSepiaFilter *stillImageFilter = [[GPUImageSepiaFilter alloc] init];

[stillImageSource addTarget:stillImageFilter];
[stillImageFilter useNextFrameForImageCapture];
[stillImageSource processImage];

UIImage *currentFilteredVideoFrame = [stillImageFilter imageFromCurrentFramebuffer];

請注意,為了手動捕獲來自過濾器的圖像,您需要設置-useNextFrameForImageCapture,以便告知過濾器您將需要從中過濾。默認情況下,GPUImage會重新使用過濾器中的幀緩沖區來節省內存,因此如果您需要持續使用過濾器的幀緩沖區進行手動映像捕獲,則需要事先通知它。

對于您要應用于圖像的單個過濾器,您可以簡單地執行以下操作:

GPUImageSepiaFilter *stillImageFilter2 = [[GPUImageSepiaFilter alloc] init];
UIImage *quickFilteredImage = [stillImageFilter2 imageByFilteringImage:inputImage];

4. Writing a custom filter - 編寫自定義過濾器

這個框架在iOS上的Core Image(iOS 5.0)上的一個顯著優點是能夠編寫自己的自定義圖像和視頻處理過濾器。這些過濾器作為OpenGL ES 2.0片段著色器提供,以C類OpenGL著色語言編寫。

自定義過濾器使用代碼初始化

GPUImageFilter *customFilter = [[GPUImageFilter alloc] initWithFragmentShaderFromFile:@"CustomShader"];

用于片段著色器的擴展名是.fsh。另外,如果您不想在應用程序包中發送片段著色器,則可以使用-initWithFractmentShaderFromString:initializer將片段著色器作為字符串提供。

片段著色器對在該過濾階段渲染的每個像素執行計算。他們使用OpenGL著色語言(GLSL),一種類似C語言的語言,具有特定于2-D和3-D圖形的添加。片段著色器的示例是以下深褐色濾鏡:

varying highp vec2 textureCoordinate;

uniform sampler2D inputImageTexture;

void main()
{
    lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
    lowp vec4 outputColor;
    outputColor.r = (textureColor.r * 0.393) + (textureColor.g * 0.769) + (textureColor.b * 0.189);
    outputColor.g = (textureColor.r * 0.349) + (textureColor.g * 0.686) + (textureColor.b * 0.168);    
    outputColor.b = (textureColor.r * 0.272) + (textureColor.g * 0.534) + (textureColor.b * 0.131);
    outputColor.a = 1.0;

    gl_FragColor = outputColor;
}

對于GPUImage框架中可以使用的圖像過濾器,需要將紋理坐標變化的前兩行(紋理中的當前坐標,歸一化為1.0)和inputImageTexture均勻(對于實際的輸入圖像框架紋理) 。

著色器的其余部分在傳入紋理中的這個位置處獲取像素的顏色,以使其產生棕褐色調的方式進行操作,并將該像素顏色寫出以用于下一階段的處理管道。

在Xcode項目中添加片段著色器時需要注意的一點是,Xcode認為它們是源代碼文件。要解決此問題,您需要手動將著色器從“編譯源”構建階段移動到“復制包資源”,以使著色器包含在應用程序包中。

5. Filtering and re-encoding a movie - 過濾和重新編碼視頻

視頻可以通過GPUImageMovie類加載到框架中,過濾,然后使用GPUImageMovieWriter寫出。GPUImageMovieWriter也足夠快,可以從640x480的iPhone 4的相機實時錄制視頻,因此可以將直接過濾的視頻源投入使用。目前,GPUImageMovieWriter足夠快,可以在iPhone 4上錄制高達20 FPS的720p視頻,以及iPhone 4S(以及新iPad)上的30 FPS的720p和1080p視頻。

以下是一個示例,您將如何加載示例視頻,將其傳遞通過像素過濾器,然后將結果記錄到磁盤為480 x 640 h.264電影

movieFile = [[GPUImageMovie alloc] initWithURL:sampleURL];
pixellateFilter = [[GPUImagePixellateFilter alloc] init];

[movieFile addTarget:pixellateFilter];

NSString *pathToMovie = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/Movie.m4v"];
unlink([pathToMovie UTF8String]);
NSURL *movieURL = [NSURL fileURLWithPath:pathToMovie];

movieWriter = [[GPUImageMovieWriter alloc] initWithMovieURL:movieURL size:CGSizeMake(480.0, 640.0)];
[pixellateFilter addTarget:movieWriter];

movieWriter.shouldPassthroughAudio = YES;
movieFile.audioEncodingTarget = movieWriter;
[movieFile enableSynchronizedEncodingUsingMovieWriter:movieWriter];

[movieWriter startRecording];
[movieFile startProcessing];

錄制完成后,您需要從過濾器鏈中刪除視頻錄像機,并使用以下代碼關閉錄制:

[pixellateFilter removeTarget:movieWriter];
[movieWriter finishRecording];

一部電影在完成之后將無法使用,所以如果這一點之前中斷,錄像將會丟失。

6. Interacting with OpenGL ES - 與OpenGL ES進行交互

GPUImage可以分別通過使用其GPUImageTextureOutput和GPUImageTextureInput類來從OpenGL ES導出和導入紋理。這可以讓您從OpenGL ES場景中錄制一個電影,將其渲染到具有綁定紋理的幀緩沖區對象,或者過濾視頻或圖像,然后將它們作為要在場景中顯示的紋理提供給OpenGL ES。

使用這種方法的一個警告是,這些進程中使用的紋理必須通過共享組或類似的東西在GPUImage的OpenGL ES上下文和任何其他上下文之間共享。

后記

未完,待續~~~

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,283評論 6 530
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 97,947評論 3 413
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 175,094評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,485評論 1 308
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,268評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,817評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,906評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,039評論 0 285
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,551評論 1 331
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,502評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,662評論 1 366
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,188評論 5 356
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,907評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,304評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,563評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,255評論 3 389
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,637評論 2 370

推薦閱讀更多精彩內容

  • 概述 GPUImage框架是一個獲得bsd許可的iOS庫,允許您對圖像、實時攝像機視頻和電影應用gpu加速過濾器和...
    依然小太陽閱讀 908評論 0 1
  • 是的,我要開始寫關于GPUImage 框架的文章了!先來把簡介看一波!借助翻譯工具也得啃出來哇!!!看來我又得早起...
    CC老師_HelloCoder閱讀 2,801評論 1 16
  • 在iOS中框架是一個目錄,包含了共享資源庫,用于訪問該資源庫中儲存的代碼的頭文件,以及圖像、聲音文件等其他資源。共...
    ch123閱讀 1,788評論 0 1
  • iOS 蘋果官方Demo合集 字數10517閱讀21059評論18喜歡144 其實, 開發了這么久, 不得不說, ...
    bingo居然被占了閱讀 10,213評論 2 31
  • 感賞兒子今天早早起床,9:40吃了早餐:一小碗芝麻湯圓、3個刀切饅頭、1個小燒賣、一個荷包蛋。又一次吃早餐了。...
    Lucy艷萍_靜待花開閱讀 383評論 2 5