九色国产,午夜在线视频,新黄色网址,九九色综合,天天做夜夜做久久做狠狠,天天躁夜夜躁狠狠躁2021a,久久不卡一区二区三区

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
用Shader實現(xiàn)的YUV到RGB轉(zhuǎn)換
 

用Shader實現(xiàn)的YUV到RGB轉(zhuǎn)換

分類: opengl

    ////本文由銹水管原創(chuàng)。

    網(wǎng)上有很多YUV到RGB的轉(zhuǎn)化程序,不過他們基本上都是基于CPU進(jìn)行計算,基于CPU計算大體上有一下的一些方法,最原始的肯定是根據(jù)轉(zhuǎn)換公式直接進(jìn)行浮點運算,要想提高速度,可以用左移和右移操作,將浮點運算變成整數(shù)運算,這樣轉(zhuǎn)化的速度會成倍的提高。另外還可以用查表法,因為YUV都是在0~255之間,他們總是有范圍的,先生成一個很大的查找表,直接對每一個YUV分量查找出RGB值,當(dāng)然這個查找表會很大,可以用部分查找的方法來縮小查找表的容量。

    用CPU計算比較好的方法就是利用CPU的SSE指令,有一個專門的名詞是來形容SSE的,那就是“單指令流多數(shù)據(jù)流”,意思就是他可以一次對多個數(shù)據(jù)進(jìn)行計算,當(dāng)然速度是非??斓?。

    在這里,我主要寫一下我用shader做轉(zhuǎn)化的方法,首先,當(dāng)然是介紹一下YUV:

    Windows下YUV格式的介紹在MSDN上介紹的非常全面。YUV的格式有很多種,有444,422,420之分,并且每一種具體的格式也有許多不同的標(biāo)準(zhǔn)。具體在MSDN的以下地址上有詳細(xì)的介紹:
http://www.microsoft.com/china/MSDN/library/enterprisedevelopment/softwaredev/VideoRende8BitYUV.mspx?mfr=true

    不過對于寫程序來說,我只關(guān)心每種格式的內(nèi)存布局方式。
    YUV分為打包格式(packed)和平面格式(planar),平面格式中YUV的每個分量是分開存儲的,一般先存儲所有的Y分量,再存儲所有的U分量,然后存儲所有的V分量;打包方式則是YUV的每個分量并沒有分開存儲,而是存儲在一起。例如:

    打包格式(圖片來自于MSDN),422中的YUY2格式:

          

          
  
平面格式,420中的IMC4格式:

          


          

   

    我用到的就是YUV420的IMC4格式,是一種平面格式。這種格式的特點是先存儲所有的Y分量,總共有多少像素,就有多少個Y分量。緊接著,存儲U分量,再存儲V分量,U和V分別都只占總像素的1/4,所以,我所用的YUV420格式的一幀所占的內(nèi)存空間可以這樣計算:
         m_FrameHeight;    //視頻高度
        m_FrameWidth;    //視頻寬度
        m_ImgSize = m_FrameWidth * m_FrameHeight; //寬 * 高
       m_FrameSize = m_ImgSize + (m_ImgSize >> 1); //YUV視頻一幀的大小,其中的右移相當(dāng)于/2

    所以每個分量的內(nèi)存地址可以按如下方式計算:
         unsigned char* cTemp[3];
         cTemp[0] = m_yuv + m_FrameSize * n;     //y分量地址
        cTemp[1] = cTemp[0] + m_ImgSize;     //u分量地址
         cTemp[2] = cTemp[1] + (m_ImgSize >> 2);     //v分量地址
    m_yuv是整段視頻的首地址,也就是指向整段視頻的指針;n是當(dāng)前幀的序號。
知道了每一幀的YUV各分量的地址,再進(jìn)行轉(zhuǎn)換就比較容易了。有很多公式可以進(jìn)行轉(zhuǎn)換,我所用到的轉(zhuǎn)換公式是這樣的:
        R = y + 1.4022 * (v - 128)
        G = y - 0.3456 * (u - 128) - 0.7145 * (v - 128)
        B = y + 1.771 * (u - 128)

    下面開始利用Shader按照這個轉(zhuǎn)換公式來進(jìn)行計算,由于我也是剛剛開始接觸shader編程,對于shader的工作方式也不是十分熟悉,我也只是按照自己的理解來進(jìn)行編程。
    首先,顯卡在運行shader程序的時候,是很多像素并行計算的,所以轉(zhuǎn)換的速度很快,但是我發(fā)現(xiàn)針對每一個像素,shader只知道他自己的像素信息,包括他自己的顏色(RGB)、深度、紋理坐標(biāo)等等。對于其他像素的信息一無所知,例如,一個紅色像素只知道他自己應(yīng)該顯示成紅色,對于他左邊的像素是什么顏色,他不會知道,更不用說隔他更遠(yuǎn)的像素了。于是他僅僅只能利用自己的信息來進(jìn)行運算。這樣就遇到一個問題,由于YUV的每個分量存儲的位置相隔很遠(yuǎn),要在一個shader程序中分別得到一個像素相應(yīng)的YUV分量數(shù)據(jù),就要對內(nèi)存的數(shù)據(jù)排列格式做相應(yīng)的調(diào)整。我所用的方法是將視頻的數(shù)據(jù)格式調(diào)整成按照YUVYUVYUV...一一對應(yīng)的格式來排列,這樣,我可以先把YUV的數(shù)據(jù)假裝是RGB的數(shù)據(jù)送入到紋理中,騙過顯卡,然后再在shader中用轉(zhuǎn)換公式來進(jìn)行計算。由于YUV分量的數(shù)據(jù)范圍也是0~255之間的,所以這種方式是可以成功的。
    (這里可能說的不對,我認(rèn)為每一個像素在運行shader的時候,應(yīng)該是有方法能夠得到除自己以外其他像素信息的,但是我學(xué)習(xí)shader還只有一個星期的時間,可能還不知道方法,如果有這樣的方法的話,那么轉(zhuǎn)換的效率會更高,因為我不需要在內(nèi)存中再花時間來做數(shù)據(jù)的排列工作。但這個可能要對shader的工作原理非常清楚,等以后我熟悉了,再來重新寫一個程序。)
    下面開始,首先是排列過程,這個過程發(fā)生在內(nèi)存中:
        for(y=0; y < m_FrameHeight; y++)
         {
            for(x=0; x < m_FrameWidth; x++)
             {
                l = y * m_FrameWidth + x;
                  m = (y / 2) * (m_FrameWidth / 2) + x / 2;
                m_Data[3 * l] = cTemp[0][l];    //y
                m_Data[3 * l + 1] = cTemp[1][m];   //u
                  m_Data[3 * l + 2] = cTemp[2][m];   //v
             }
        }
    排列的方法很簡單,只要理解了上面的那個內(nèi)存布局結(jié)構(gòu),就很容易知道了。
    然后將排列好的YUV數(shù)據(jù)送入到紋理中,在每一幀的渲染之前,都運行shader程序。其中shader程序代碼如下:
    頂點著色器Vertex Shader:
         void main()
         {
             gl_TexCoord[0] = gl_MultiTexCoord0;
            gl_Position = ftransform();
        } 
    片段著色器Fragment Shader:
         uniform sampler2D tex;
         void main()
         {
             vec4 yuv = texture2D(tex,gl_TexCoord[0].st);
              vec4 color;
              color.r =yuv.r + 1.4022 * yuv.b - 0.7011;
             color.r = (color.r < 0.0) ? 0.0 : ((color.r > 1.0) ? 1.0 : color.r);
               color.g =yuv.r - 0.3456 * yuv.g - 0.7145 * yuv.b + 0.53005;
             color.g = (color.g < 0.0) ? 0.0 : ((color.g > 1.0) ? 1.0 : color.g);
             color.b =yuv.r + 1.771 * yuv.g - 0.8855;
              color.b = (color.b < 0.0) ? 0.0 : ((color.b > 1.0) ? 1.0 : color.b);
             gl_FragColor = color;
         }
    其中為什么會有-0.7011、0.53005、0.8855這樣的常數(shù),那是因為顏色數(shù)據(jù)從著色器中的到的時候,已經(jīng)轉(zhuǎn)化成了浮點數(shù)的形式,范圍為0.0~1.0之間,所以轉(zhuǎn)換公式后面的系數(shù)也要做相應(yīng)的變化。

    最終效果,其中程序框架就是上次我寫的MFC的框架:


本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
YUV / RGB 格式及快速轉(zhuǎn)換算法
修改了一個YUV/RGB播放器
YUV格式的解析_幸福的起點
談?wù)凴GB、YUY2、YUYV、YVYU、UYVY、AYUV_UU=uClinux ubu...
數(shù)字視頻基礎(chǔ)知識
對YUV格式的詳細(xì)描述,以及存儲形式
更多類似文章 >>
生活服務(wù)
熱點新聞
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服