最近在學習圖像處理相關(guān)的內(nèi)容,看了GPUImage的源碼,查閱了相關(guān)資料,收集了許多人博客的基礎(chǔ)上做出了整理,再次感謝給博主!GPUImage 是一個開源的基于GPU的圖片或視頻的處理框架,其本身內(nèi)置了多達120多種常見的濾鏡效果,并且支持照相機和攝像機的實時濾鏡,并且能夠自定義圖像濾鏡。
美顏基本概念
OpenGL ES:(Open Graphics Library For Embedded Systems)即開源嵌入式系統(tǒng)圖形處理框架,一套圖形與硬件接口,創(chuàng)造了軟件與圖形加速間靈活強大的底層交互接口,用于把處理好的圖片顯示到屏幕上。
GPU:(Graphic Processor Unit圖形處理單元)手機或者電腦用于圖像處理和渲染的硬件。
GPU工作原理:CPU指定顯示控制器工作,顯示控制器根據(jù)CPU的控制到指定的地方去取數(shù)據(jù)和指令, 目前的數(shù)據(jù)一般是從顯存里取,如果顯存里存不下,則從內(nèi)存里取, 內(nèi)存也放不下,則從硬盤里取,當然也不是內(nèi)存放不下,而是為了節(jié)省內(nèi)存的話,可以放在硬盤里,然后通過指令控制顯示控制器去取。
濾鏡處理的原理:就是把靜態(tài)圖片或者視頻的每一幀進行圖形變換后再顯示到屏幕上,其本質(zhì)就是像素點的坐標和顏色的變化。
OpenGL ES程序處理圖片步驟:
1、初始化OpenGL ES環(huán)境,編譯、鏈接頂點著色器和片元著色器;
2、緩存頂點、紋理坐標數(shù)據(jù),傳送圖像數(shù)據(jù)到GPU;
3、繪制圖元到特定的幀緩存;
4、在幀緩存取出繪制的圖像。
GPUImage基本概念
GPUImage是采用鏈式方法來處理畫面,通過addTarget方法添加對象到鏈中,處理完一個target,就會把上一個環(huán)節(jié)處理好的圖像數(shù)據(jù)傳遞到下一個target處理,稱為GPUImage處理鏈。
GPUImage的四大輸入基礎(chǔ)類,都可以作為響應(yīng)鏈的起點,這些基礎(chǔ)類會把圖像作為紋理傳給OpenGL ES處理,然后把紋理傳遞給響應(yīng)鏈的下一個target對象。
GPUImage處理環(huán)節(jié)
source(視頻、圖片源)->filter(濾鏡)-> final target(處理后的視頻、圖片)
source
GPUImageVideoCamera 攝像頭用于實時拍攝視頻
GPUImageStillCamera 攝像頭用于實時拍攝照片
GPUImagePicture 用于處理已經(jīng)拍攝好的圖片
GPUImageMovie 用于處理已經(jīng)拍攝好的視頻
filter
GPUImageFilter:就是用來接收源圖像,通過自定義的頂點、片元著色器來渲染新的圖像,并在繪制完成后通知響應(yīng)鏈的下一個對象。
GPUImageFramebuffer:就是用來管理紋理緩存的格式與讀寫幀緩存的buffer。
GPUImage的filter:GPUImageFilter類或者子類,這個類繼承自GPUImageOutput,遵循GPUImageInput協(xié)議,既可以流進數(shù)據(jù),又可以流出
GPUImage的final target:GPUImageView,GPUImageMovieWriter最終輸入目標,顯示圖片或者視頻。
解析
GPUImageVideoCamera
GPUImageVideoCamera是GPUImageOutput的子類,提供來自攝像頭的圖像數(shù)據(jù)作為源數(shù)據(jù),一般是響應(yīng)鏈的源頭。
1、視頻圖像采集 :AVCaptureSession
GPUImage使用AVFoundation框架來獲取視頻。AVCaptureSession類從AV輸入設(shè)備的采集數(shù)據(jù)到制定的輸出。
AVCaptureSession創(chuàng)建:
_captureSession = [[AVCaptureSession alloc] init];
[_captureSession beginConfiguration];
// 中間可以實現(xiàn)關(guān)于session屬性的設(shè)置
[_captureSession commitConfiguration];
要實現(xiàn)AVCaptureSession類,需要添加合適的輸入(AVCaptureDeviceInput)和輸出(AVCaptureMovieFileOutput)設(shè)備
AVCaptureVideoDataOutput
AVCaptureVideoDataOutput是AVCaptureOutput的子類,用來處理從攝像頭采集的未壓縮或者壓縮過的圖像幀。
通過captureOutput:didOutputSampleBuffer:fromConnection: delegate方法,可以訪問圖像幀。
設(shè)置delegate
-(void)setSampleBufferDelegate:(id<AVCaptureAudioDataOutputSampleBufferDelegate>)sampleBufferDelegate queue:(dispatch_queue_t)sampleBufferCallbackQueue;
當新的視頻圖像幀被采集后,會被傳送到output中,調(diào)用delegate,delegate函數(shù)會在隊列中調(diào)用,必須使用同步隊列處理圖像幀,保證幀序列的順序。
frameRenderingSemaphore 幀渲染的信號量
if (dispatch_semaphore_wait(frameRenderingSemaphore, DISPATCH_TIME_NOW) != 0)
{
return;
}
runAsynchronouslyOnVideoProcessingQueue(^{
dispatch_semaphore_signal(frameRenderingSemaphore);
});
這個方法用于等待處理完一幀后,再接著處理下一幀。
2、顏色空間:YUV
YUV是被歐洲電視系統(tǒng)所采用的一種顏色編碼方法。采用YUV色彩空間的重要性是它的亮度信號Y和色度信號U、V是分離的。如果只有Y信號分量而沒有U、V分量,那么這樣表示的圖像就是黑白灰度圖像。彩色電視采用YUV空間正是為了用亮度信號Y解決彩色電視機與黑白電視機的兼容問題,使黑白電視機也能接收彩色電視信號。
YUV主要用于優(yōu)化彩色視頻信號的傳輸,使其向后相容老式黑白電視。與RGB視頻信號傳輸相比,它最大的優(yōu)點在于只需占用極少的頻寬(RGB要求三個獨立的視頻信號同時傳輸)。
GPUImage中的YUV
GLProgram *yuvConversionProgram:將YUV顏色空間轉(zhuǎn)換成RGB顏色空間的GLSL。
CVPixelBufferGetPlaneCount:返回緩沖區(qū)的平面數(shù)。
CVOpenGLESTextureCacheCreateTextureFromImage():
創(chuàng)建兩個紋理luminanceTextureRef(亮度紋理)和chrominanceTextureRef(色度紋理)。
convertYUVToRGBOutput():把YUV顏色空間的紋理轉(zhuǎn)換成RGB顏色空間的紋理。
頂點著色器-通用kGPUImageVertexShaderString
片元著色器:
1.kGPUImageYUVFullRangeConversionForLAFragmentShaderString
2.kGPUImageYUVVideoRangeConversionForLAFragmentShaderString
3、紋理繪制
glActiveTexture(GL_TEXTURE1);
glGenTextures(1, &_texture);
glBindTexture(GL_TEXTURE_2D, _texture);
GPUImageView
GPUImageView是響應(yīng)鏈的終點,用于顯示GPUImage圖像
1、填充模式
GPUImageFillModeType fillMode圖像的填充模式。
sizeInPixels 像素區(qū)域大小。
recalculateViewGeometry() 重新計算圖像頂點位置數(shù)據(jù)。
AVMakeRectWithAspectRatioInsideRect() 在保證寬高比不變的前提下,得到一個盡可能大的矩形。
2、OpenGL ES繪制
源圖像已經(jīng)準備好,開始繪制。
-(void)newFrameReadyAtTime:(CMTime)frameTime atIndex:(NSInteger)textureIndex;
setDisplayFramebuffer()會綁定GPUImageView的幀緩存,同時調(diào)試視口大小為view的大小。
glActiveTexture是選擇一個紋理單元。先選擇紋理單元4,然后把源圖像數(shù)據(jù)綁定到GL_TEXTURE_2D的位置上。最后告訴片元著色器,紋理單元是4。
glActiveTexture(GL_TEXTURE4);
glBindTexture(GL_TEXTURE_2D, [inputFramebufferForDisplay texture]);
glUniform1i(displayInputTextureUniform, 4);
接下來分別綁定頂點坐標數(shù)據(jù)和紋理坐標數(shù)據(jù)
glVertexAttribPointer(displayPositionAttribute, 2, GL_FLOAT, 0, 0, imageVertices);
glVertexAttribPointer(displayTextureCoordinateAttribute, 2, GL_FLOAT, 0, 0, [GPUImageView textureCoordinatesForRotation:inputRotation]);
然后設(shè)定輸入的源圖像數(shù)據(jù)緩存,并緩存加鎖。
inputFramebufferForDisplay = newInputFramebuffer;
[inputFramebufferForDisplay lock];
最后準備好著色器、紋理數(shù)據(jù)、頂點坐標、紋理坐標數(shù)據(jù)后,就可以繪制圖像了
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
相關(guān)資料:
http://www.tuicool.com/articles/6bIbQbQ
http://www.itdecent.cn/p/945fc806a9b4
http://www.itdecent.cn/p/7a58a7a61f4c
http://www.itdecent.cn/p/4646894245ba