解析
GPUImage詳細(xì)解析(一)
上一篇介紹的是GPUImageFramebuffer
和GPUImageFilter
愁铺。
簡(jiǎn)單回顧一下:
-
GPUImageFilter
就是用來(lái)接收源圖像,通過(guò)自定義的頂點(diǎn)铅鲤、片元著色器來(lái)渲染新的圖像司蔬,并在繪制完成后通知響應(yīng)鏈的下一個(gè)對(duì)象。 -
GPUImageFramebuffer
就是用來(lái)管理紋理緩存的格式與讀寫(xiě)幀緩存的buffer。
這一篇介紹的是GPUImageVideoCamera
和GPUImageView
帆啃。
GPUImageVideoCamera
GPUImageVideoCamera是GPUImageOutput的子類(lèi),提供來(lái)自攝像頭的圖像數(shù)據(jù)作為源數(shù)據(jù)窍帝,一般是響應(yīng)鏈的源頭努潘。
1、視頻圖像采集 :AVCaptureSession
GPUImage使用AVFoundation框架來(lái)獲取視頻坤学。
AVCaptureSession類(lèi)從AV輸入設(shè)備的采集數(shù)據(jù)到制定的輸出疯坤。
為了實(shí)現(xiàn)實(shí)時(shí)的圖像捕獲,要實(shí)現(xiàn)AVCaptureSession類(lèi)深浮,添加合適的輸入(AVCaptureDeviceInput)和輸出(比如 AVCaptureMovieFileOutput)
調(diào)用startRunning
開(kāi)始輸入到輸出的數(shù)據(jù)流压怠,調(diào)用stopRunning
停止數(shù)據(jù)流。
需要注意的是startRunning函數(shù)會(huì)花費(fèi)一定的時(shí)間飞苇,所以不能在主線(xiàn)程(UI線(xiàn)程)調(diào)用菌瘫,防止卡頓。
sessionPreset 屬性可以自定義一些設(shè)置布卡。
特殊的選項(xiàng)比如說(shuō)高幀率雨让,可以通過(guò) AVCaptureDevice來(lái)設(shè)置。
AVCaptureSession使用的簡(jiǎn)單示例:
_captureSession = [[AVCaptureSession alloc] init];
[_captureSession beginConfiguration];
// 中間可以實(shí)現(xiàn)關(guān)于session屬性的設(shè)置
[_captureSession commitConfiguration];
- AVCaptureVideoDataOutput
AVCaptureVideoDataOutput
是AVCaptureOutput
的子類(lèi)羽利,用來(lái)處理從攝像頭采集的未壓縮或者壓縮過(guò)的圖像幀宫患。
通過(guò)captureOutput:didOutputSampleBuffer:fromConnection: delegate
,可以訪(fǎng)問(wèn)圖像幀。
通過(guò)下面這個(gè)方法娃闲,可以設(shè)置delegate虚汛。
- (void)setSampleBufferDelegate:
(id<AVCaptureVideoDataOutputSampleBufferDelegate>)sampleBufferDelegate
queue:(dispatch_queue_t)sampleBufferCallbackQueue;
需要注意的是,當(dāng)一個(gè)新的視頻圖像幀被采集后皇帮,它會(huì)被傳送到output卷哩,調(diào)用這里設(shè)置的delegate。所有的delegate函數(shù)會(huì)在這個(gè)queue中調(diào)用属拾。如果隊(duì)列被阻塞将谊,新的圖像幀到達(dá)后會(huì)被自動(dòng)丟棄(默認(rèn)alwaysDiscardsLateVideoFrames = YES)。這允許app處理當(dāng)前的圖像幀渐白,不需要去管理不斷增加的內(nèi)存尊浓,因?yàn)樘幚硭俣雀簧喜杉乃俣龋却幚淼膱D像幀會(huì)占用內(nèi)存纯衍,并且不斷增大栋齿。
必須使用同步隊(duì)列處理圖像幀,保證幀的序列是順序的襟诸。
- frameRenderingSemaphore 幀渲染的信號(hào)量
下面有一個(gè)這樣的調(diào)用瓦堵,用于等待處理完一幀后,再接著處理下一幀歌亲。
if (dispatch_semaphore_wait(frameRenderingSemaphore, DISPATCH_TIME_NOW) != 0)
{
return;
}
runAsynchronouslyOnVideoProcessingQueue(^{
dispatch_semaphore_signal(frameRenderingSemaphore);
});
- rotateCamera
前后攝像頭翻轉(zhuǎn):更改videoInput的設(shè)置菇用。
2、顏色空間:YUV
YUV是被歐洲電視系統(tǒng)所采用的一種顏色編碼方法陷揪。
采用YUV色彩空間的重要性是它的亮度信號(hào)Y和色度信號(hào)U惋鸥、V是分離的。如果只有Y信號(hào)分量而沒(méi)有U鹅龄、V分量揩慕,那么這樣表示的圖像就是黑白灰度圖像。彩色電視采用YUV空間正是為了用亮度信號(hào)Y解決彩色電視機(jī)與黑白電視機(jī)的兼容問(wèn)題扮休,使黑白電視機(jī)也能接收彩色電視信號(hào)迎卤。
YCbCr或Y'CbCr有的時(shí)候會(huì)被寫(xiě)作:YCBCR或是Y'CBCR,是色彩空間的一種玷坠,通常會(huì)用于影片中的影像連續(xù)處理蜗搔,或是數(shù)字?jǐn)z影系統(tǒng)中。Y'為顏色的亮度(luma)成分八堡、而CB和CR則為藍(lán)色和紅色的濃度偏移量成份樟凄。
YUV主要用于優(yōu)化彩色視頻信號(hào)的傳輸,使其向后相容老式黑白電視兄渺。與RGB視頻信號(hào)傳輸相比缝龄,它最大的優(yōu)點(diǎn)在于只需占用極少的頻寬(RGB要求三個(gè)獨(dú)立的視頻信號(hào)同時(shí)傳輸)。
CbCr 則是在世界數(shù)字組織視頻標(biāo)準(zhǔn)研制過(guò)程中作為ITU - R BT.601 建議的一部分,其實(shí)是YUV經(jīng)過(guò)縮放和偏移的翻版叔壤。其中Y與YUV 中的Y含義一致,Cb,Cr 同樣都指色彩瞎饲,只是在表示方法上不同而已。在YUV 家族中炼绘,YCbCr 是在計(jì)算機(jī)系統(tǒng)中應(yīng)用最多的成員嗅战,其應(yīng)用領(lǐng)域很廣泛,JPEG俺亮、MPEG均采用此格式驮捍。一般人們所講的YUV大多是指YCbCr。YCbCr 有許多取樣格式脚曾,如4∶4∶4,4∶2∶2,4∶1∶1 和4∶2∶0东且。
百度百科的介紹
YUV數(shù)據(jù)格式-圖文詳解
GPUImage中的YUV
GLProgram *yuvConversionProgram;
將YUV顏色空間轉(zhuǎn)換成RGB顏色空間的GLSL。
CVPixelBufferGetPlaneCount()
返回緩沖區(qū)的平面數(shù)本讥。
通過(guò)CVOpenGLESTextureCacheCreateTextureFromImage()
創(chuàng)建兩個(gè)紋理luminanceTextureRef(亮度紋理)和chrominanceTextureRef(色度紋理)苇倡。
convertYUVToRGBOutput()
把YUV顏色空間的紋理轉(zhuǎn)換成RGB顏色空間的紋理
頂點(diǎn)著色器-通用kGPUImageVertexShaderString
片元著色器:
1、kGPUImageYUVFullRangeConversionForLAFragmentShaderString
2囤踩、kGPUImageYUVVideoRangeConversionForLAFragmentShaderString
區(qū)別在不同的格式
video-range (luma=[16,235] chroma=[16,240])
full-range (luma=[0,255] chroma=[1,255])
3、紋理繪制
glActiveTextue 并不是激活紋理單元晓褪,而是選擇當(dāng)前活躍的紋理單元堵漱。每一個(gè)紋理單元都有GL_TEXTURE_1D, 2D, 3D 和 CUBE_MAP。
glActiveTexture(GL_TEXTURE1);
glGenTextures(1, &_texture);
glBindTexture(GL_TEXTURE_2D, _texture);
GPUImageView
GPUImageView是響應(yīng)鏈的終點(diǎn)涣仿,一般用于顯示GPUImage的圖像勤庐。
1、填充模式
GPUImageFillModeType fillMode
圖像的填充模式好港。
sizeInPixels
像素區(qū)域大小愉镰。
recalculateViewGeometry()
重新計(jì)算圖像頂點(diǎn)位置數(shù)據(jù)。
AVMakeRectWithAspectRatioInsideRect()
在保證寬高比不變的前提下钧汹,得到一個(gè)盡可能大的矩形丈探。
如果是kGPUImageFillModeStretch
圖像拉伸,直接使寬高等于1.0即可拔莱,原圖像會(huì)直接鋪滿(mǎn)整個(gè)屏幕碗降。
如果是kGPUImageFillModePreserveAspectRatio
保持原寬高比,并且圖像不超過(guò)屏幕塘秦。那么以當(dāng)前屏幕大小為準(zhǔn)讼渊。
widthScaling = insetRect.size.width / currentViewSize.width;
如果是kGPUImageFillModePreserveAspectRatioAndFill
保持原寬高比,并且圖像要鋪滿(mǎn)整個(gè)屏幕尊剔。那么圖像大小為準(zhǔn)爪幻。
widthScaling = currentViewSize.height / insetRect.size.height;
imageVertices存放著頂點(diǎn)數(shù)據(jù),上面的修改都會(huì)存放在這個(gè)數(shù)組。
2挨稿、OpenGL ES繪制
- (void)newFrameReadyAtTime:(CMTime)frameTime atIndex:(NSInteger)textureIndex;
源圖像已經(jīng)準(zhǔn)備好仇轻,開(kāi)始繪制。
setDisplayFramebuffer()
會(huì)綁定GPUImageView的幀緩存叶组,同時(shí)調(diào)試視口大小為view的大小拯田。
glActiveTexture
上面已經(jīng)介紹過(guò),是選擇一個(gè)紋理單元甩十。先選擇紋理單元4船庇,然后把源圖像數(shù)據(jù)綁定到GL_TEXTURE_2D的位置上。最后告訴片元著色器侣监,紋理單元是4鸭轮。
glActiveTexture(GL_TEXTURE4);
glBindTexture(GL_TEXTURE_2D, [inputFramebufferForDisplay texture]);
glUniform1i(displayInputTextureUniform, 4);
這兩行是分別綁定頂點(diǎn)坐標(biāo)數(shù)據(jù)和紋理坐標(biāo)數(shù)據(jù)。
glVertexAttribPointer(displayPositionAttribute, 2, GL_FLOAT, 0, 0, imageVertices);
glVertexAttribPointer(displayTextureCoordinateAttribute, 2, GL_FLOAT, 0, 0, [GPUImageView textureCoordinatesForRotation:inputRotation]);
這兩行是設(shè)定輸入的源圖像數(shù)據(jù)緩存橄霉,并且對(duì)緩存加鎖窃爷。
inputFramebufferForDisplay = newInputFramebuffer;
[inputFramebufferForDisplay lock];
在準(zhǔn)備好著色器、紋理data姓蜂、頂點(diǎn)位置坐標(biāo)和紋理坐標(biāo)后按厘,就可以調(diào)用
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
繪制圖像。
demo
這里有一個(gè)簡(jiǎn)單的示例钱慢,介紹如何用GPUImageVideoCamera采集圖像并且用GPUImageView顯示出來(lái)逮京。
十分簡(jiǎn)單,核心代碼不過(guò)十行束莫。