版本記錄
版本號 | 時間 |
---|---|
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上下文和任何其他上下文之間共享。
后記
未完,待續~~~