概述
GPUImage框架是一個獲得bsd許可的iOS庫,允許您對圖像、實時攝像機視頻和電影應用gpu加速過濾器和其他效果。與Core Image (iOS 5.0的一部分)相比,GPUImage允許您編寫自己的自定義過濾器,支持iOS 4.0的部署,并且具有更簡單的界面。然而,它目前缺少核心圖像的一些更高級的功能,比如面部檢測。
對于處理圖像或視頻幀的大規模并行操作,gpu比cpu有一些顯著的性能優勢。在iPhone 4上,一個簡單的圖像過濾器在GPU上的運行速度比一個同等的基于cpu的過濾器快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毫秒就可以從攝像頭上傳一幀圖像,應用gamma filter,然后顯示,而在同樣的操作中使用Core Image只需要106毫秒。基于cpu的處理需要460毫秒,這使得GPUImage比Core Image快40倍,比cpu綁定的處理快184倍。在iPhone 4S上,GPUImage僅比Core Image快4倍,比cpu綁定處理快102X倍。然而,對于更復雜的操作,如高斯模糊在較大的半徑,核心圖像目前超過GPUImage。
許可證
bsd風格,在license .txt中有完整的許可框架。
技術要求
OpenGL ES 2.0:使用這種技術的應用程序不會在最初的iPhone、iPhone 3G和第一代和第二代iPod touch上運行
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項目中使用這個,您需要使用“Adding this as a framework”部分中的步驟,而不是以下步驟。Swift需要第三方代碼模塊。
有了框架的最新源代碼后,將其添加到應用程序中就相當簡單了。從拖動GPUImage開始。在應用程序的Xcode項目中嵌入框架。接下來,轉到應用程序的目標,并將GPUImage添加為目標依賴項。最后,需要拖動libGPUImage。從GPUImage框架的Products文件夾到鏈接二進制與庫的應用程序目標構建階段的庫。
GPUImage需要一些其他框架鏈接到您的應用程序中,因此您需要在您的應用程序目標中添加以下鏈接庫:
CoreMedia
CoreVideo
OpenGLES
AVFoundation
QuartzCore
您還需要找到框架標頭,因此在項目的構建設置中,要將標頭搜索路徑設置為從應用程序到GPUImage源目錄中的框架/子目錄的相對路徑。使這個頭搜索路徑遞歸。
要在應用程序中使用GPUImage類,只需使用以下方法包括核心框架頭:
#import "GPUImage.h"
注意:如果您在嘗試使用Interface Builder構建接口時遇到“未知類GPUImageView在Interface Builder中”之類的錯誤,那么您可能需要在項目的構建設置中向其他鏈接器標記添加-ObjC。
另外,如果您需要將其部署到iOS 4中。x,看來Xcode的當前版本(4.3)要求您在最終應用程序中弱鏈接核心視頻框架,或者在創建用于上傳到應用程序商店或特別發布的存檔時,看到“符號未找到:_CVOpenGLESTextureCacheCreate”的消息崩潰。要做到這一點,請轉到您的項目的構建階段選項卡,使用庫組展開鏈接二進制文件,并在列表中找到corevido .framework。將列表最右側的設置從Required更改為Optional。
此外,這是一個支持arc的框架,所以如果你想在面向iOS 4的手動引用計數應用程序中使用它。x,還需要將-fobjc-arc添加到其他鏈接器標志中。
在命令行中構建靜態庫
如果不希望將項目作為依賴項包含在應用程序的Xcode項目中,可以為iOS模擬器或設備構建通用靜態庫。要做到這一點,運行build。命令行。生成的庫和頭文件將位于build/Release-iphone上。您還可以通過更改構建中的IOSSDK_VER變量來更改iOS SDK的版本。sh(可以使用xcodebuild -showsdks找到所有可用的版本)。
將其作為框架(模塊)添加到Mac或iOS項目中
Xcode 6和iOS 8支持完整框架的使用,Mac也是如此,這簡化了向應用程序添加這個框架的過程。要將其添加到應用程序中,我建議將.xcodeproj項目文件拖放到應用程序的項目中(就像在靜態庫目標中那樣)。
對于您的應用程序,轉到它的目標構建設置并選擇構建階段選項卡。在目標依賴項分組下,在iOS上添加GPUImageFramework(不是構建靜態庫的GPUImage)或在Mac上添加GPUImage。
這將導致GPUImage作為框架構建。在Xcode 6中,這也將作為一個模塊構建,允許您在Swift項目中使用它。在如上所述設置時,您只需使用
import GPUImage
即可。
然后,您需要添加一個新的復制文件構建階段,將目標設置為框架,并為此添加GPUImage.framework構建產品。這將允許框架與您的應用程序捆綁在一起(否則,您將看到“dyld: Library not loaded: @rpath/GPUImage.framework/GPUImage”執行錯誤)。
文檔
文檔是使用appledoc從標題注釋中生成的。要構建文檔,請切換到Xcode中的“文檔”模式。您應該確保“APPLEDOC_PATH”(用戶定義的構建設置)指向appledoc二進制文件,可以在Github上使用,也可以通過Homebrew使用。它還將構建和安裝一個.docset文件,您可以使用自己喜歡的文檔工具查看該文件。
執行常見任務
過濾視頻直播
要從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紋理。
GPUImageView的填充模式可以通過設置它的fillMode屬性來改變,這樣如果源視頻的長寬比與視圖不同,視頻要么被拉伸,以黑色條為中心,要么被縮放以填充。
對于混合過濾器和接受多個圖像的其他過濾器,您可以創建多個輸出并添加一個過濾器作為這兩個輸出的目標。作為目標添加輸出的順序將影響輸入圖像混合或以其他方式處理的順序。
另外,如果你想讓麥克風的音頻捕捉記錄到電影,你需要把攝像機的audioEncodingTarget設置為你的電影編劇,比如:
videoCamera.audioEncodingTarget = movieWriter;
捕捉和過濾靜止照片
要捕獲和過濾靜態照片,可以使用類似于過濾視頻的過程。你用的不是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];
這將給你一個實時的,過濾后的靜止攝像頭的預覽視頻。請注意,這個預覽視頻僅在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;
? ? }
}];
上面的代碼捕獲了由preview視圖中使用的相同篩選器鏈處理的全尺寸照片,并將該照片作為JPEG保存在應用程序的documents目錄中。
請注意,由于紋理大小的限制,該框架目前無法處理超過2048像素寬或高的舊設備(iPhone 4S、iPad 2或Retina iPad之前的設備)上的圖像。這意味著,iPhone 4的攝像頭輸出的照片仍然比這大,無法捕捉到這樣的照片。目前正在實施一種平鋪機制來解決這個問題。所有其他設備都應該能夠使用這種方法捕獲和過濾照片。
處理靜態圖像
有幾種方法可以處理靜態圖像并創建結果。第一種方法是創建一個靜態圖像源對象并手動創建一個過濾器鏈:
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在過濾器中重用framebuffer來保存內存,所以如果您需要保存過濾器的framebuffer以進行手動圖像捕獲,您需要提前讓它知道。
對于您希望應用于圖像的單個過濾器,您可以簡單地執行以下操作:
GPUImageSepiaFilter *stillImageFilter2 = [[GPUImageSepiaFilter alloc] init];
UIImage *quickFilteredImage = [stillImageFilter2 imageByFilteringImage:inputImage];
編寫一個自定義的過濾器
與iOS上的核心映像相比,這個框架的一個顯著優點是能夠編寫自己的自定義映像和視頻處理過濾器。這些過濾器作為OpenGL ES 2.0片段著色器提供,用類似c的OpenGL著色語言編寫。
使用如下代碼初始化自定義篩選器
GPUImageFilter *customFilter = [[GPUImageFilter alloc] initWithFragmentShaderFromFile:@"CustomShader"];
用于片段著色器的擴展名為.fsh。另外,如果您不想在應用程序包中發布片段著色器,可以使用-initWithFragmentShaderFromString: initializer將片段著色器作為字符串提供。
片段著色器對要在該篩選階段呈現的每個像素執行計算。他們使用OpenGL Shading Language (GLSL)來做到這一點,這是一種類似于c語言的語言,添加了特定于2d和3d圖形。片段著色器的一個例子是:
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框架中可用,需要前兩行包含textureCoordinate變化(對于紋理內的當前坐標,歸一化到1.0)和inputImageTexture uniform(對于實際輸入的圖像幀紋理)。
著色器的其余部分在傳入紋理的這個位置抓取像素的顏色,以產生深褐色色調的方式操作它,并將像素顏色寫出來,以便在處理管道的下一階段使用。
在Xcode項目中添加片段著色器時需要注意的一點是,Xcode認為它們是源代碼文件。為了解決這個問題,您需要手動將著色器從編譯源構建階段移動到復制包資源階段,以便將著色器包含到應用程序包中。
過濾和重新編碼一個電影
電影可以通過GPUImageMovie類加載到框架中,過濾,然后使用GPUImageMovieWriter寫出。GPUImageMovieWriter的速度也足夠快,可以在640x480英寸的iPhone 4相機上實時錄制視頻,因此可以直接將經過過濾的視頻源輸入其中。目前,GPUImageMovieWriter的速度足以在iPhone 4上以高達20幀/秒的速度錄制720p的視頻,在iPhone 4S上(以及在新iPad上)以30幀/秒的速度錄制720p和1080p的視頻。
下面是一個示例,演示如何加載示例影片,將其通過像素化過濾器傳遞,然后將結果以480x640的速度記錄到磁盤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];
電影在結束之前是不能用的,所以如果在這之前中斷了,影片就會丟失。
與OpenGL ES交互
GPUImage可以通過分別使用其GPUImageTextureOutput和GPUImageTextureInput類從OpenGL ES導出和導入紋理。這允許您從OpenGL ES場景中錄制電影,該場景通過綁定紋理呈現到framebuffer對象,或過濾視頻或圖像,然后將它們作為紋理輸入到OpenGL ES中以顯示在場景中。
這種方法的一個警告是,在這些過程中使用的紋理必須通過共享組或類似的方式在GPUImage的OpenGL ES上下文和任何其他上下文之間共享。
示例應用程序
幾個示例應用程序與框架源代碼捆綁在一起。大多數都兼容iPhone和ipad級設備。它們試圖展示框架的各個方面,并應該在框架開發期間作為API的最佳示例使用。這些包括:
SimpleImageFilter
在啟動時將綁定的JPEG圖像加載到應用程序中,對其應用過濾器,并將結果呈現到屏幕上。此外,本示例還展示了兩種方法,分別是獲取圖像、對其進行過濾和將其保存到磁盤。
SimpleVideoFilter
一個像素過濾器被應用到實時視頻流中,帶有UISlider控件,允許您調整實時視頻的像素大小。
SimpleVideoFileFilter
從磁盤加載一個電影文件,對其應用一個不清晰的掩碼過濾器,過濾后的結果被重新編碼為另一個電影。
MultiViewFilterExample
從一個攝像機提要,4個視圖被應用到攝像機上的實時過濾器填充。一個是直接拍攝的視頻,一個是預編程的深褐色調,兩個是基于著色程序的自定義濾鏡。
FilterShowcase
這演示了GPUImage提供的每個過濾器。
BenchmarkSuite
這是通過對cpu綁定例程和核心映像進行測試來測試整個框架的性能的。涉及靜態圖像和視頻的基準測試是針對這三種方法運行的,結果顯示在應用程序中。
CubeExample
這證明了GPUImage與OpenGL ES渲染的交互能力。鏡框從相機上捕捉下來,然后用一個烏賊濾鏡對它們進行處理,然后把它們放入一個紋理中,然后應用到一個可以用手指旋轉的立方體的表面。這個立方體依次被渲染到一個有紋理支持的framebuffer對象中,然后這個紋理被反饋回GPUImage,在渲染到屏幕之前應用一個像素化過濾器。
換句話說,這個應用的路徑是camera -> sepia tone filter -> cube -> pixellation filter -> display。
ColorObjectTracking
我在http://www.sunsetlakesoftware.com/2010/10/22/gpu- accelerator -video-processing-mac- ios中移植了一個使用GPUImage的顏色跟蹤示例,這個應用程序在場景中使用顏色來從實時攝像機提要跟蹤對象。四個視圖之間切換可以包括原始相機飼料,相機飼料白色像素匹配顏色閾值,處理視頻編碼的位置在哪里的顏色在像素通過閾值測試,最后點的視頻實時跟蹤選定的顏色。輕觸屏幕可更改顏色以跟蹤匹配手指下像素的顏色。在屏幕上點擊和拖動使顏色閾值或多或少得到了原諒。這在第二種顏色閾值視圖上最明顯。
目前,最后一步中顏色平均的所有處理都是在CPU上完成的,所以這部分非常慢。