1. 程式人生 > >YUV到RGB顏色空間轉換

YUV到RGB顏色空間轉換

三種顏色空間簡介:

YUV:是歐洲電視系統採用的一種顏色編碼格式,Y表示亮度值(Luminance或Luma),U和V表示色彩及飽和度(Chrominance或Chroma)。

YUV主要格式有:YUV444、YUV 422、YUV 411和YUV 420。YUV對原始資料以每4個畫素為單元進行壓縮處理,處理的方式就是對U和V分量進行降取樣,如YUV444就是每4個畫素YUV都會被取樣4次資料無壓縮,YUV422就是每4個畫素YUV取樣次數分別為422,資料減小了1/3,同理YUV411資料就減小了一半。但是根據奈奎斯特取樣定理可以知道,取樣後的資料資訊損失量很小,不會造成影象的視覺效果變差。

假設有四個YUV空間的畫素點{Y0,U0,V0},{Y1,U1,V1},{Y2,U2,V2},{Y3,U3,V3}

(1)YUV444

儲存格式為:{Y0,U0,V0},{Y1,U1,V1},{Y2,U2,V2},{Y3,U3,V3}

畫素表示為:{Y0,U0,V0},{Y1,U1,V1},{Y2,U2,V2},{Y3,U3,V3}

沒有壓縮。

(2) YUV422

儲存格式為:{Y0,U0},{Y1,V1},{Y2,U2},{Y3,V3}

畫素表示為:{Y0,U0,V1}, {Y1,U0,V1},{Y2,U2,V3}, {Y3,U2,V3}

可以看出四個畫素點Y分量全部取樣,U保留了偶數,V保留了奇數,四個畫素點每兩個畫素點共用同一個U和V分量,資料壓縮了1/3。

(3)YUV411

儲存格式為:{Y0,U0,Y1},{Y2,V2,Y3}

畫素表示為:{Y0,U0,V2}, {Y1,U0,V2},{Y2,U0,V2}, {Y3,U0,V2}

可以看出四個畫素點Y分量全部取樣,U和V只採樣了U0和V2兩個值,然後四個畫素點的UV分離都是U0和V2,資料壓縮了1/2。

(3)YUV420

這個有點特殊,上面的幾種每4個畫素點中的YUV三個分量都有采樣,而YUV420則是第一次四個畫素點中只採樣Y和U分量,第二次四個畫素點總只採用Y和V分量,然後依次交替進行。

儲存格式為:{Y0,U0,V0},{Y1,U1,V1},{Y2,U2,V2}, {Y3,U3,V3},{Y5,U5,V5}, {Y6,U6,V6}, {Y7,U7,V7} , {Y8,U8,V8}

畫素表示為:{Y0,U0,V5}, {Y1,U0,V5},{Y2,U2,V7}, {Y3,U2,V7},{Y5,U0,V5}, {Y6,U0,V5},{Y7,U2,V7}, {Y8,U2,V7}

資料壓縮了1/2。

YcbCr:是經過伽馬修正(gamma correction)編碼處理後的YUV版本,Y'為顏色的亮度(luma)成分、而cb和Cr則為藍色和紅色的濃度偏移量成份。

RGB:是現在運用最廣的顏色系統之一,通過紅綠藍三個分量的變化疊加來得到其他顏色。

BGR:把RGB的紅色分量和藍色交換即BGR。

顏色空間轉換格式:

YUV<==>RGB

Y = (B * 1868 + G * 9617 + R * 4899 +8192)/16384;

U = ((B - Y)* 9241 + 8192)/16384 + 128;

V = ((R - Y)*11682 + 8192)/16384 + 128;

R = Y + 1.14V

G = Y - 0.39U - 0.58V

B = Y + 2.03U

YcbCr<==>RGB

Y’ = 0.257*R' + 0.504*G' + 0.098*B' + 16

Cb' = -0.148*R' - 0.291*G' + 0.439*B' + 128

Cr' = 0.439*R' - 0.368*G' - 0.071*B' + 128

R' = 1.164*(Y’-16) + 1.596*(Cr'-128)

G' = 1.164*(Y’-16) - 0.813*(Cr'-128) -0.392*(Cb'-128)

B' = 1.164*(Y’-16) + 2.017*(Cb'-128)

顏色空間轉換程式碼:

在opencv中可以用cvtColor();函式來進行顏色空間的轉換,同樣我們也可以自己寫函式來實現。

(1)自定義顏色空間轉換函式

下面這個函式是根據上面的公式實現的YcbCr2BGR的函式,經過本人除錯成功,不過出來最後的影象上下顛倒了,可以自己改一下,也可以根據YUV2RGB公式稍微改一下便可以進行YUV到RGB的轉換。其中YcbCr為422格式,RGB為888格式。

int YcbCr2RGB(void* pYUV, void* pRGB, int width, int height, bool alphaYUV, bool alphaRGB)
{
    if (NULL == pYUV)
    {
        return -1;
    }
    unsigned char* pYUVData = (unsigned char *)pYUV;
    unsigned char* pRGBData = (unsigned char *)pRGB;
    int Y1, U1, V1, Y2, alpha1, alpha2, R1, G1, B1, R2, G2, B2;
    int C1, D1, E1, C2;
    if (alphaRGB)
    {
        if (alphaYUV)
        {
            for (int i=0; i<height; ++i)
            {
                for (int j=0; j<width/2; ++j)
                {
                    Y1 = *(pYUVData+i*width*3+j*6);    //i*width*3 = i*(width/2)*6
                    U1 = *(pYUVData+i*width*3+j*6+1);
                    Y2 = *(pYUVData+i*width*3+j*6+2);
                    V1 = *(pYUVData+i*width*3+j*6+3);
                    alpha1 = *(pYUVData+i*width*3+j*6+4);
                    alpha2 = *(pYUVData+i*width*3+j*6+5);
                    C1 = Y1-16;
                    C2 = Y2-16;
                    D1 = U1-128;
                    E1 = V1-128;
                    R1 = ((298*C1 + 409*E1 + 128)>>8>255 ? 255 : (298*C1 + 409*E1 + 128)>>8);
                    G1 = ((298*C1 - 100*D1 - 208*E1 + 128)>>8>255 ? 255 : (298*C1 - 100*D1 - 208*E1 + 128)>>8);
                    B1 = ((298*C1+516*D1 +128)>>8>255 ? 255 : (298*C1+516*D1 +128)>>8);
                    R2 = ((298*C2 + 409*E1 + 128)>>8>255 ? 255 : (298*C2 + 409*E1 + 128)>>8);
                    G2 = ((298*C2 - 100*D1 - 208*E1 + 128)>>8>255 ? 255 : (298*C2 - 100*D1 - 208*E1 + 128)>>8);
                    B2 = ((298*C2 + 516*D1 +128)>>8>255 ? 255 : (298*C2 + 516*D1 +128)>>8);
                    *(pRGBData+(height-i-1)*width*4+j*8+2) = R1<0 ? 0 : R1;
                    *(pRGBData+(height-i-1)*width*4+j*8+1) = G1<0 ? 0 : G1;
                    *(pRGBData+(height-i-1)*width*4+j*8) = B1<0 ? 0 : B1;
                    *(pRGBData+(height-i-1)*width*4+j*8+3) = alpha1;
                    *(pRGBData+(height-i-1)*width*4+j*8+6) = R2<0 ? 0 : R2;
                    *(pRGBData+(height-i-1)*width*4+j*8+5) = G2<0 ? 0 : G2;
                    *(pRGBData+(height-i-1)*width*4+j*8+4) = B2<0 ? 0 : B2;
                    *(pRGBData+(height-i-1)*width*4+j*8+7) = alpha2;
                }
            }
        }
        else
        {
            int alpha = 255;
            for (int i=0; i<height; ++i)
            {
                for (int j=0; j<width/2; ++j)
                {
                    Y1 = *(pYUVData+i*width*2+j*4);
                    U1 = *(pYUVData+i*width*2+j*4+1);
                    Y2 = *(pYUVData+i*width*2+j*4+2);
                    V1 = *(pYUVData+i*width*2+j*4+3);
                    C1 = Y1-16;
                    C2 = Y2-16;
                    D1 = U1-128;
                    E1 = V1-128;
                    R1 = ((298*C1 + 409*E1 + 128)>>8>255 ? 255 : (298*C1 + 409*E1 + 128)>>8);
                    G1 = ((298*C1 - 100*D1 - 208*E1 + 128)>>8>255 ? 255 : (298*C1 - 100*D1 - 208*E1 + 128)>>8);
                    B1 = ((298*C1+516*D1 +128)>>8>255 ? 255 : (298*C1+516*D1 +128)>>8);
                    R2 = ((298*C2 + 409*E1 + 128)>>8>255 ? 255 : (298*C2 + 409*E1 + 128)>>8);
                    G2 = ((298*C2 - 100*D1 - 208*E1 + 128)>>8>255 ? 255 : (298*C2 - 100*D1 - 208*E1 + 128)>>8);
                    B2 = ((298*C2 + 516*D1 +128)>>8>255 ? 255 : (298*C2 + 516*D1 +128)>>8);
                    *(pRGBData+(height-i-1)*width*4+j*8+2) = R1<0 ? 0 : R1;
                    *(pRGBData+(height-i-1)*width*4+j*8+1) = G1<0 ? 0 : G1;
                    *(pRGBData+(height-i-1)*width*4+j*8) = B1<0 ? 0 : B1;
                    *(pRGBData+(height-i-1)*width*4+j*8+3) = alpha;
                    *(pRGBData+(height-i-1)*width*4+j*8+6) = R2<0 ? 0 : R2;
                    *(pRGBData+(height-i-1)*width*4+j*8+5) = G2<0 ? 0 : G2;
                    *(pRGBData+(height-i-1)*width*4+j*8+4) = B2<0 ? 0 : B2;
                    *(pRGBData+(height-i-1)*width*4+j*8+7) = alpha;
                }
            }
        }
    }
    else
    {
        if (alphaYUV)
        {
            for (int i=0; i<height; ++i)
            {
                for (int j=0; j<width/2; ++j)
                {
                    Y1 = *(pYUVData+i*width*3+j*4);
                    U1 = *(pYUVData+i*width*3+j*4+1);
                    Y2 = *(pYUVData+i*width*3+j*4+2);
                    V1 = *(pYUVData+i*width*3+j*4+3);
                    C1 = Y1-16;
                    C2 = Y2-16;
                    D1 = U1-128;
                    E1 = V1-128;
                    R1 = ((298*C1 + 409*E1 + 128)>>8>255 ? 255 : (298*C1 + 409*E1 + 128)>>8);
                    G1 = ((298*C1 - 100*D1 - 208*E1 + 128)>>8>255 ? 255 : (298*C1 - 100*D1 - 208*E1 + 128)>>8);
                    B1 = ((298*C1+516*D1 +128)>>8>255 ? 255 : (298*C1+516*D1 +128)>>8);
                    R2 = ((298*C2 + 409*E1 + 128)>>8>255 ? 255 : (298*C2 + 409*E1 + 128)>>8);
                    G2 = ((298*C2 - 100*D1 - 208*E1 + 128)>>8>255 ? 255 : (298*C2 - 100*D1 - 208*E1 + 128)>>8);
                    B2 = ((298*C2 + 516*D1 +128)>>8>255 ? 255 : (298*C2 + 516*D1 +128)>>8);
                    *(pRGBData+(height-i-1)*width*3+j*6+2) = R1<0 ? 0 : R1;
                    *(pRGBData+(height-i-1)*width*3+j*6+1) = G1<0 ? 0 : G1;
                    *(pRGBData+(height-i-1)*width*3+j*6) = B1<0 ? 0 : B1;
                    *(pRGBData+(height-i-1)*width*3+j*6+5) = R2<0 ? 0 : R2;
                    *(pRGBData+(height-i-1)*width*3+j*6+4) = G2<0 ? 0 : G2;
                    *(pRGBData+(height-i-1)*width*3+j*6+3) = B2<0 ? 0 : B2;
                }
            }
        }
        else
        {
            for (int i=0; i<height; ++i)
            {
                for (int j=0; j<width/2; ++j)
                {
                    Y1 = *(pYUVData+i*width*2+j*4);
                    U1 = *(pYUVData+i*width*2+j*4+1);
                    Y2 = *(pYUVData+i*width*2+j*4+2);
                    V1 = *(pYUVData+i*width*2+j*4+3);
                    C1 = Y1-16;
                    C2 = Y2-16;
                    D1 = U1-128;
                    E1 = V1-128;
                    R1 = ((298*C1 + 409*E1 + 128)>>8>255 ? 255 : (298*C1 + 409*E1 + 128)>>8);
                    G1 = ((298*C1 - 100*D1 - 208*E1 + 128)>>8>255 ? 255 : (298*C1 - 100*D1 - 208*E1 + 128)>>8);
                    B1 = ((298*C1+516*D1 +128)>>8>255 ? 255 : (298*C1+516*D1 +128)>>8);
                    R2 = ((298*C2 + 409*E1 + 128)>>8>255 ? 255 : (298*C2 + 409*E1 + 128)>>8);
                    G2 = ((298*C2 - 100*D1 - 208*E1 + 128)>>8>255 ? 255 : (298*C2 - 100*D1 - 208*E1 + 128)>>8);
                    B2 = ((298*C2 + 516*D1 +128)>>8>255 ? 255 : (298*C2 + 516*D1 +128)>>8);
                    *(pRGBData+(height-i-1)*width*3+j*6+2) = R1<0 ? 0 : R1;
                    *(pRGBData+(height-i-1)*width*3+j*6+1) = G1<0 ? 0 : G1;
                    *(pRGBData+(height-i-1)*width*3+j*6) = B1<0 ? 0 : B1;
                    *(pRGBData+(height-i-1)*width*3+j*6+5) = R2<0 ? 0 : R2;
                    *(pRGBData+(height-i-1)*width*3+j*6+4) = G2<0 ? 0 : G2;
                    *(pRGBData+(height-i-1)*width*3+j*6+3) = B2<0 ? 0 : B2;
                }
            }
        }
    }
    return 0;
}

(2)使用opencv進行顏色空間轉換

下面的程式碼主要呼叫了cvtColor();函式來實現YUV2BGR,把CV_YUV2BGR改成CV_ YcbCr2RGB即可把YcbCr轉換成RGB。其中YUV為422格式,RGB為888格式。

(下面的程式碼執行效率較低,需要優化!)

void YUV2RGB (unsigned char *inbuf,Mat&bgrimg, int width, int height)
{
   vector<Mat> yuvchs;
   unsigned char ybuf[width*height];
   unsigned char ubuf[width*height/2];
   unsigned char vbuf[width*height/2];
   int cnt=0,y=0,u=0,v=0;
   for(int i=0;i<width*height*2;i++){
       if(i%2){
           if(cnt==0){
                ubuf[u++]=inbuf[i];
                cnt=1;
           }
            else{
                vbuf[v++]=inbuf[i];
                cnt=0;
           }
       }
       else{
           ybuf[y++]=inbuf[i];
       }
    }
   Mat umat0(1,width*height/2,CV_8UC1,ubuf);
   Mat vmat0(1,width*height/2,CV_8UC1,vbuf);
    Mat ymat1(1,width*height,CV_8UC1,ybuf);
   Mat umat1(1,width*height,CV_8UC1);
   Mat vmat1(1,width*height,CV_8UC1);
   resize(umat0,umat1,umat1.size());
   resize(vmat0,vmat1,vmat1.size());
   Mat yimg=ymat1.reshape(1,height);
   Mat uimg=umat1.reshape(1,height);
   Mat vimg=vmat1.reshape(1,height);
   yuvchs.push_back(yimg);
   yuvchs.push_back(uimg);
   yuvchs.push_back(vimg);
 
   Mat yuvimg;
   merge(yuvchs,yuvimg);
   cvtColor(yuvimg,bgrimg,CV_YUV2BGR);
}