Tistory View

Android Develop/image

Android YV12, stride and RGB conversion

God Dangchy What should I do? 2019. 8. 6. 16:42

Android YV12[Kor,한글]

Android NDK Access memory of Bitmap directly

 

YV12 Structure

YV12 has separeted planes of Y, U and V. Y demension is identical to image, U and V plane has demension of half of image width and height. Colors of pixels are composed like next. 4Y share 1 UV.

 

YV21 6x4 image(not considering of padding)

[Y00][Y01][Y02][Y03][Y04][Y05]

[Y06][Y07][Y08][Y09][Y10][Y11]

[Y12][Y13][Y14][Y15][Y16][Y17]

[Y18][Y19][Y20][Y21][Y22][Y23]

+

[V0][V1][V2]

[V2][V3][V2]

+

[U0][U1][U2]

[U2][U3][U2]

 

 

 

 

YV12 STRIDE

NV21 has no paddings, but YV12 has padding, so, must compute this first.

 

 

fomulas( asume only even w and h )

y_stride   = ALIGN(w, 16) 

y_size     = stride * height 
c_stride   = ALIGN(stride/2, 16)   // U and V plane stride
c_size     = c_stride * height/2 
size       = y_size + c_size * 2  // Total YV12 bytes size
v_offset   = y_size 
u_offset   = y_size + c_size

 

 

There are y_stride, and also c_stride, These must be aligned by 16bytes. by this, YV12 has paddings

 

 

It's Convinience when make fomulas like next.

 

Demension info in C/C++

struct YV12DemensionInfo {

    int w;              // image w
    int h;              // image h

    int strideY;        // Y Plane stride
    int strideC;        // UV Plane stride( c is by Cr, Cb)

    int offsetV;        // offset of V
    int offsetU;        // offset of U

    int yPlaneW;        // Y Plane W : same to image W
    int yPlaneH;        // Y Plane H : same to image H

    int cPlaneW;        // UV Plane Width
    int cPlaneH;        // UV Plane Height

    int yPlaneSize;     // Y Plane  byte size
    int uvPlaneSize;    // UV Plane  byte size
    int totalBytes;     // total byte size of YV12

};

void computeYVDemensionInfo( YV12DemensionInfo* p, int w, int h )
{
    int pad;
    int strideYPlane;
    int strideCPlane; // UV plane stide(Cr,Cb)

    pad = 16 - w % 16;
    if( pad == 16 ) {
        pad = 0;
    }

    strideYPlane = w + pad;

    pad = 16 - ( strideYPlane / 2 ) % 16;
    if( pad == 16 ) pad = 0;
    strideCPlane = ( strideYPlane / 2 ) + pad;

    p->w           = w;
    p->h           = h;
    p->yPlaneW     = w;
    p->yPlaneH     = h;
    p->cPlaneW     = strideYPlane / 2;
    p->cPlaneH     = h / 2;
    p->strideY     = strideYPlane;
    p->strideC     = strideCPlane;
    p->offsetV     = strideYPlane * h;
    p->offsetU     = strideYPlane * h + strideCPlane * (h / 2);
    p->yPlaneSize  = strideYPlane * h;
    p->uvPlaneSize = strideCPlane * (h / 2);
    p->totalBytes  = p->yPlaneSize + p->uvPlaneSize * 2;
}

 

 

 

 

Let't calculate by hand, where image has demension of 176x144

strIdeYPlane = 176 // 16 * 11, no remainer no padding

width = 176;
height = 144;
yPlaneW = 176;
yPlaneH = 144;
cPlaneW = 176 / 2 = 88;
cPlaneH = 144 / 2 = 72;

88( 16 * 5 + 8 ) % 16 
strideC  => = 88 + ( 16 - 8 ) = 96;  <- padding 8
offsetV = 176 * 144= 25344;
offsetU =176 * 144 + 96 * 72 = 25344 + 6912 = 32256;
yPlanSize = 176 * 144 = 25344;
uvPlaneSize = 96 * 72;
totalBytes = 25344 + 6912 * 2 = 39168;

 

 

 

 

If your camera on android device support previewSize, 176x144, If not considering padding, image shows like next

Not considering of stride

 

YV12 Structure in considering padding

onPreviewFrame of Camera api(1), image of 6X4 is reached as next.

 

[Y00][Y01][Y02][Y03][Y04][Y05][padding 10Bytes]

[Y06][Y07][Y08][Y09][Y10][Y11][padding 10Bytes]

[Y12][Y13][Y14][Y15][Y16][Y17][padding 10Bytes]

[Y18][Y19][Y20][Y21][Y22][Y23][padding 10Bytes]

+

[V0][V1][V2][padding 13Bytes]

[V2][V3][V2][padding 13Bytes]

+

[U0][U1][U2][padding 13Bytes]

[U2][U3][U2][padding 13Bytes]

 

 

 

 

 

 

The code of Converting YV12 to RGB(A)

inline uint8_t clamp16bits( int v )
{
    v >>= 16;

    // most case 1 ~ 255
    if( ( v & 0xffffff00 ) == 0 ) {
        return v;
    }

    if( v <= 0 ) return 0;

    return 255;
}

void image_yv12_to_rgba( uint8_t* dst, uint8_t* yv12, int w, int h, int nThread )
{

    const int strideDst  = w*4; // in byte

    YV12DemensionInfo demInfo;
    computeYVDemensionInfo(  &demInfo, w, h );

    for( int y = 0 ; y < demInfo.cPlaneH ; y++ )
    {
        uint8_t* pSrcY1 = yv12 + ( y * 2 ) * demInfo.strideY;
        uint8_t* pSrcY2 = pSrcY1 + demInfo.strideY;


        uint8_t* pSrcV = yv12 + demInfo.offsetV + y * demInfo.strideC;
        uint8_t* pSrcU = yv12 + demInfo.offsetU + y * demInfo.strideC;


        uint8_t* pd1  = dst + y * 2 * strideDst;
        uint8_t* pd2  = pd1 + strideDst;


        for( int x = 0 ; x < demInfo.cPlaneW ; x++ )
        {
            const int cU = (int)pSrcU[0] - 128;
            const int cV = (int)pSrcV[0] - 128;

            const int f1 = 89831 * cV; // 1.370705F * 65536 * ( (int)cV - 128 );
            const int f2 = -45744 * cV -22127 * cU;
            const int f3 = 113538 * cU; // 1.732446F * 65536 * ( (int)cU - 128 );

            int _y;

            _y = pSrcY1[0] << 16;
            pd1[0] = clamp16bits( _y + f1 ); // r
            pd1[1] = clamp16bits( _y + f2 ); // g
            pd1[2] = clamp16bits( _y + f3 ); // b
            pd1[3] = 0xff; // a

            _y = pSrcY1[1] << 16;
            pd1[4] = clamp16bits( _y + f1 );
            pd1[5] = clamp16bits( _y + f2 );
            pd1[6] = clamp16bits( _y + f3 );
            pd1[7] = 0xff;


            _y = pSrcY2[0] << 16;
            pd2[0] = clamp16bits( _y + f1 );
            pd2[1] = clamp16bits( _y + f2 );
            pd2[2] = clamp16bits( _y + f3 );
            pd2[3] = 0xff;

            _y = pSrcY2[1] << 16;
            pd2[4] = clamp16bits( _y + f1 );
            pd2[5] = clamp16bits( _y + f2 );
            pd2[6] = clamp16bits( _y + f3 );
            pd2[7] = 0xff;


            pSrcY1+=2;
            pSrcY2+=2;

            pSrcU++;
            pSrcV++;

            pd1 += 8;
            pd2 += 8;
        }

    }
}



Android NDK Access memory of Bitmap directly

 

 

Replies
Reply Write