版本記錄
版本號 | 時間 |
---|---|
V1.0 | 2017.09.01 |
前言
GPUImage
是直接利用顯卡實現(xiàn)視頻或者圖像處理的技術(shù)坑填。
作者
先看一下GPUImage
下面給出該框架的地址抛人。
GPUImage - GitHub
下面我們就看一下該框架的作者。
從上面可以看見:
- 作者不僅寫了OC上的框架
GPUImage
- 同樣寫了Swift上的框架
GPUImage2
脐瑰,這里我們暫時只關(guān)注OC上的代碼妖枚。
總體概括
GPUImage
框架是一個BSD許可的iOS庫,可讓您將GPU加速過濾器和其他效果應(yīng)用于圖像苍在,實況相機視頻和電影绝页。
與Core Image
(iOS 5.0的一部分)相比,GPUImage允許您編寫自己的自定義過濾器寂恬,支持部署到iOS 4.0续誉,并具有更簡單的界面。
然而初肉,它目前缺乏Core Image的一些更先進的功能酷鸦,如面部檢測。
對于大規(guī)模并行操作(如處理圖像或?qū)崟r視頻幀)牙咏,GPU具有比CPU更顯著的性能優(yōu)勢臼隔。
在iPhone 4上,與基于CPU的等效過濾器相比妄壶,簡單的圖像濾鏡可以在GPU上執(zhí)行的速度提升100倍以上摔握。
然而,在GPU上運行自定義過濾器需要大量代碼來為這些過濾器設(shè)置和維護一個OpenGL ES 2.0渲染目標(biāo)丁寄。
我創(chuàng)建了一個示例項目:
http://www.sunsetlakesoftware.com/2010/10/22/gpu-accelerated-video-processing-mac-and-ios
并發(fā)現(xiàn)在其創(chuàng)建中有很多樣板代碼氨淌。
因此泊愧,我將這個框架放在一起,封裝了處理圖像和視頻時遇到的許多常見任務(wù)盛正,并使其不需要關(guān)心OpenGL ES 2.0基礎(chǔ)删咱。
當(dāng)處理視頻時,該框架與Core Image相比蛮艰,在iPhone 4上僅使用2.5 ms從相機上傳幀,應(yīng)用伽馬濾鏡和顯示雀彼,而對于使用Core Image的相同操作壤蚜,則為106 ms』惭疲基于CPU的處理需要460 ms袜刷,使GPUImage比Core Image快40倍,在此硬件上進行此操作莺丑,比CPU處理速度快184倍著蟹。在iPhone 4S上,GPUImage在這種情況下只比Core Image快4倍梢莽,比CPU綁定處理快了102倍萧豆。
然而,對于更大的半徑處的高斯模糊等更復(fù)雜的操作昏名,Core Image目前超過GPUImage涮雷。
技術(shù)要求
-
OpenGL ES 2.0
:使用此功能的應(yīng)用程序不會在原始iPhone,iPhone 3G以及第一代和第二代iPod觸摸屏上運行 - iOS 4.1作為部署目標(biāo)(4.0沒有一些擴展需要電影閱讀)轻局。如果您想在靜態(tài)照片中顯示實時視頻預(yù)覽洪鸭,則需要iOS 4.3作為部署目標(biāo)。
- iOS 5.0 SDK來構(gòu)建
- 設(shè)備必須有相機才能使用相機功能(顯然)
- 該框架使用自動引用計數(shù)(ARC)仑扑,但是如下所述览爵,應(yīng)支持使用ARC和手動引用計數(shù)的項目,如果作為子項目添加镇饮。對于針對iOS 4.x的手動引用計數(shù)應(yīng)用程序蜓竹,您需要為應(yīng)用程序項目的其他鏈接器標(biāo)志添加
-fobjc-arc
。
基本架構(gòu)
GPUImage使用OpenGL ES 2.0著色器執(zhí)行圖像和視頻操作比在CPU綁定例程中可以做得更快储藐。然而梅肤,它隱藏了在簡化的Objective-C接口中與OpenGL ES API交互的復(fù)雜性。此接口允許您定義圖像和視頻的輸入源邑茄,在鏈中附加濾鏡姨蝴,并將結(jié)果處理的圖像或視頻發(fā)送到屏幕,UIImage或磁盤上的影片肺缕。
圖像或視頻幀從源對象(GPUImageOutput
的子類)上傳左医。這些包括GPUImageVideoCamera
(用于iOS相機的實時視頻)授帕,GPUImageStillCamera
(用于拍攝相機),GPUImagePicture
(靜態(tài)圖像)和GPUImageMovie
(適用于電影)浮梢。源對象將靜態(tài)圖像幀上傳到OpenGL ES作為紋理跛十,然后將這些紋理關(guān)閉到處理鏈中的下一個對象。
鏈中的過濾器和其他后續(xù)元素符合GPUImageInput協(xié)議秕硝,允許它們從鏈中的上一個鏈接中獲取提供或處理的紋理芥映,然后使用它。將鏈中的一個進一步的對象視為目標(biāo)远豺,并且可以通過向單個輸出或過濾器添加多個目標(biāo)來分支處理奈偏。
例如,從攝像機接收實時視頻的應(yīng)用程序?qū)⒃撘曨l轉(zhuǎn)換為棕褐色調(diào)躯护,然后在屏幕上顯示視頻將設(shè)置鏈條惊来,如下所示:
GPUImageVideoCamera -> GPUImageSepiaFilter -> GPUImageView
將靜態(tài)庫添加到您的iOS項目
注意:如果要在Swift項目中使用它,則需要使用“將其添加為框架”部分而不是以下步驟中的步驟棺滞。Swift需要第三方代碼的模塊裁蚁。
一旦您獲得了框架的最新源代碼,將其添加到您的應(yīng)用程序是非常簡單的继准。首先將GPUImage.xcodeproj
文件拖到應(yīng)用程序的Xcode項目中枉证,將框架嵌入到項目中。接下來移必,轉(zhuǎn)到應(yīng)用程序的目標(biāo)刽严,并將GPUImage
添加為目標(biāo)依賴關(guān)系。最后避凝,您將要將libGPUImage.a
庫從GPUImage框架的Products文件夾拖動到應(yīng)用程序目標(biāo)中的鏈接二進制庫與構(gòu)建階段舞萄。
GPUImage
需要將其他幾個框架鏈接到您的應(yīng)用程序中,因此您需要在應(yīng)用程序目標(biāo)中添加以下鏈接庫:
CoreMedia
Corevideo
OpenGLES
AVFoundation
QuartzCore
您還需要找到框架標(biāo)題管削,因此在項目的構(gòu)建設(shè)置中倒脓,將標(biāo)題搜索路徑設(shè)置為從應(yīng)用程序到GPUImage源目錄中的框架/子目錄的相對路徑。使此標(biāo)題搜索路徑遞歸含思。
要在應(yīng)用程序中使用GPUImage類崎弃,只需使用以下內(nèi)容包含核心框架頭:
#import "GPUImage.h"
注意:如果在嘗試使用Interface Builder
構(gòu)建接口時遇到錯誤Interface Builder
中的未知類GPUImageView
,則可能需要在項目的構(gòu)建設(shè)置中將-ObjC添加到其他鏈接器標(biāo)志含潘。
另外饲做,如果您需要將其部署到iOS 4.x,似乎當(dāng)前版本的Xcode(4.3)要求您將最終應(yīng)用程序中的Core Video框架鏈接到弱鏈接遏弱,或者看到有“符號未找到”的崩潰:_CVOpenGLESTextureCacheCreate
盆均,當(dāng)您創(chuàng)建上傳到App Store或臨時分發(fā)的存檔。為此漱逸,請轉(zhuǎn)到項目的Build Phases選項卡泪姨,展開“使用庫的鏈接二進制”組游沿,然后在列表中找到CoreVideo.framework
。將列表中最右側(cè)的設(shè)置更改為“必需”至“可選”肮砾。
另外诀黍,這是一個支持ARC的框架,所以如果你想在一個手動引用計數(shù)的iOS 4.x的應(yīng)用程序中使用它仗处,你還需要添加-fobjc-arc到你的其他鏈接器標(biāo)志眯勾。
在命令行中構(gòu)建一個靜態(tài)庫
如果您不想將項目作為依賴項包含在應(yīng)用程序的Xcode項目中,則可以為iOS模擬器或設(shè)備構(gòu)建通用靜態(tài)庫婆誓。為此吃环,請build.sh
在命令行運行。生成的庫和頭文件將位于build/Release-iphone
旷档。您也可以通過更改IOSSDK_VER
變量build.sh(可以使用所有可用的版本)來更改iOS SDK
的版本xcodebuild -showsdks
模叙。
執(zhí)行常見任務(wù)
1. Filtering live video - 過濾直播視頻
要從iOS設(shè)備的相機中過濾實時視頻歇拆,您可以使用以下代碼:
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];
這將設(shè)置一個來自iOS設(shè)備背面攝像頭的視頻源鞋屈,使用預(yù)設(shè)可以捕獲640x480。這個視頻被捕獲在界面處于縱向模式故觅,其中橫向左照相機需要在顯示之前使其視頻幀旋轉(zhuǎn)厂庇。然后將使用文件CustomShader.fsh
中的代碼的自定義過濾器設(shè)置為來自相機的視頻幀的目標(biāo)。這些過濾的視頻幀最終在UIView子類的幫助下顯示在屏幕上输吏,UIView子類可以呈現(xiàn)由此管道產(chǎn)生的過濾的OpenGL ES紋理权旷。
可以通過設(shè)置其fillMode屬性來更改GPUImageView
的填充模式,以便如果源視頻的寬高比與視圖的寬高比不同贯溅,則視頻將被拉伸拄氯,以黑色條為中心,或者縮放以填充它浅。
對于混合過濾器和其他占用多個映像的過濾器译柏,您可以創(chuàng)建多個輸出,并為這兩個輸出添加一個過濾器作為目標(biāo)姐霍。將輸出添加為目標(biāo)的順序?qū)⒂绊戄斎雸D像混合或以其他方式處理的順序鄙麦。
另外,如果要啟用麥克風(fēng)音頻捕獲來錄制電影镊折,則需要將相機的audioEncodingTarget
設(shè)置為影片writer胯府,如下所示:
videoCamera.audioEncodingTarget = movieWriter;
2. Capturing and filtering a still photo - 捕獲和過濾靜態(tài)照片
要捕獲和過濾靜態(tài)照片,您可以使用類似于過濾視頻的過程恨胚。而不是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];
這將為您提供靜態(tài)相機預(yù)覽視頻的實時,過濾的Feed赃泡。請注意侣签,此預(yù)覽視頻僅在iOS 4.3及更高版本上提供塘装,因此如果希望具有此功能,則可能需要將其設(shè)置為部署目標(biāo)影所。
一旦您想要拍攝照片蹦肴,您可以使用如下回調(diào)塊:
[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;
}
}];
上述代碼捕獲由預(yù)覽視圖中使用的相同過濾器鏈處理的全尺寸照片,并將照片作為JPEG保存到應(yīng)用程序的文檔目錄中猴娩。
請注意阴幌,由于紋理尺寸限制,該框架目前無法處理大于2048像素寬或高的舊設(shè)備(iPhone 4S卷中,iPad 2或Retina iPad之前)的圖像矛双。這意味著iPhone 4的照相機輸出的照片比此更大,將無法捕獲這樣的照片蟆豫。正在實施平鋪機制來解決這個問題议忽。所有其他設(shè)備都應(yīng)該能夠使用此方法捕獲和過濾照片。
3. Processing a still image - 處理靜止圖像
有幾種方法來處理靜態(tài)圖像并創(chuàng)建結(jié)果十减。您可以通過創(chuàng)建靜態(tài)圖像源對象并手動創(chuàng)建過濾器鏈來實現(xiàn)此方法栈幸。
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];
請注意,為了手動捕獲來自過濾器的圖像帮辟,您需要設(shè)置-useNextFrameForImageCapture
速址,以便告知過濾器您將需要從中過濾。默認(rèn)情況下由驹,GPUImage會重新使用過濾器中的幀緩沖區(qū)來節(jié)省內(nèi)存芍锚,因此如果您需要持續(xù)使用過濾器的幀緩沖區(qū)進行手動映像捕獲,則需要事先通知它蔓榄。
對于您要應(yīng)用于圖像的單個過濾器并炮,您可以簡單地執(zhí)行以下操作:
GPUImageSepiaFilter *stillImageFilter2 = [[GPUImageSepiaFilter alloc] init];
UIImage *quickFilteredImage = [stillImageFilter2 imageByFilteringImage:inputImage];
4. Writing a custom filter - 編寫自定義過濾器
這個框架在iOS上的Core Image(iOS 5.0)
上的一個顯著優(yōu)點是能夠編寫自己的自定義圖像和視頻處理過濾器。這些過濾器作為OpenGL ES 2.0
片段著色器提供甥郑,以C類OpenGL著色語言編寫逃魄。
自定義過濾器使用代碼初始化
GPUImageFilter *customFilter = [[GPUImageFilter alloc] initWithFragmentShaderFromFile:@"CustomShader"];
用于片段著色器的擴展名是.fsh
。另外壹若,如果您不想在應(yīng)用程序包中發(fā)送片段著色器嗅钻,則可以使用-initWithFractmentShaderFromString:initializer
將片段著色器作為字符串提供。
片段著色器對在該過濾階段渲染的每個像素執(zhí)行計算店展。他們使用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框架中可以使用的圖像過濾器柳弄,需要將紋理坐標(biāo)變化的前兩行(紋理中的當(dāng)前坐標(biāo),歸一化為1.0)和inputImageTexture
均勻(對于實際的輸入圖像框架紋理) 。
著色器的其余部分在傳入紋理中的這個位置處獲取像素的顏色碧注,以使其產(chǎn)生棕褐色調(diào)的方式進行操作嚣伐,并將該像素顏色寫出以用于下一階段的處理管道。
在Xcode項目中添加片段著色器時需要注意的一點是萍丐,Xcode認(rèn)為它們是源代碼文件轩端。要解決此問題,您需要手動將著色器從“編譯源”構(gòu)建階段移動到“復(fù)制包資源”逝变,以使著色器包含在應(yīng)用程序包中基茵。
5. Filtering and re-encoding a movie - 過濾和重新編碼視頻
視頻可以通過GPUImageMovie
類加載到框架中,過濾壳影,然后使用GPUImageMovieWriter
寫出拱层。GPUImageMovieWriter
也足夠快,可以從640x480的iPhone 4的相機實時錄制視頻宴咧,因此可以將直接過濾的視頻源投入使用根灯。目前,GPUImageMovieWriter足夠快掺栅,可以在iPhone 4上錄制高達(dá)20 FPS的720p視頻烙肺,以及iPhone 4S(以及新iPad)上的30 FPS的720p和1080p視頻。
以下是一個示例柿冲,您將如何加載示例視頻茬高,將其傳遞通過像素過濾器兆旬,然后將結(jié)果記錄到磁盤為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];
錄制完成后假抄,您需要從過濾器鏈中刪除視頻錄像機,并使用以下代碼關(guān)閉錄制:
[pixellateFilter removeTarget:movieWriter];
[movieWriter finishRecording];
一部電影在完成之后將無法使用丽猬,所以如果這一點之前中斷宿饱,錄像將會丟失。
6. Interacting with OpenGL ES - 與OpenGL ES進行交互
GPUImage可以分別通過使用其GPUImageTextureOutput和GPUImageTextureInput類來從OpenGL ES導(dǎo)出和導(dǎo)入紋理脚祟。這可以讓您從OpenGL ES場景中錄制一個電影谬以,將其渲染到具有綁定紋理的幀緩沖區(qū)對象,或者過濾視頻或圖像由桌,然后將它們作為要在場景中顯示的紋理提供給OpenGL ES为黎。
使用這種方法的一個警告是,這些進程中使用的紋理必須通過共享組或類似的東西在GPUImage的OpenGL ES上下文和任何其他上下文之間共享行您。
后記
未完铭乾,待續(xù)~~~