Войти
ПрограммированиеФорумОбщее

YUV2 формат

#0
23:50, 25 авг 2008

Подскажите как сконвертить пиксел из YUV422 (16 бит) в RGB888. Не могу понять (вроде всё просто) как получить значения цветов rgb. В чем отличие YUV422 от YUV420P?

Нашел инфу про конверсию, не въеду где какой Y для YUV422 в этих формулах?  :)

YUV to RGB Conversion
B = 1.164(Y - 16) + 2.018(U - 128)

G = 1.164(Y - 16) - 0.813(V - 128) - 0.391(U - 128)

R = 1.164(Y - 16) + 1.596(V - 128)

#1
11:02, 26 авг 2008


> Нашел инфу про конверсию, не въеду где какой Y для YUV422 в этих формулах? :)

Y - это канал яркости, а U и V - это два сжатых (с учетом цветовоспреимчивости человека) цветоразносных сигнала. 422 - это не биты! Это лишь соотношение 4:2:2, т.е. из 16 бит 50% приходиться на Y и по 25% на U и Y.

RGB -> YUV:

    Y  =      (0.257 * R) + (0.504 * G) + (0.098 * B) + 16

    Cr = V =  (0.439 * R) - (0.368 * G) - (0.071 * B) + 128

    Cb = U = -(0.148 * R) - (0.291 * G) + (0.439 * B) + 128


YUV -> RGB:

    B = 1.164 * (Y - 16)                  + 2.018 * (U - 128)

    G = 1.164 * (Y - 16) - 0.813 * (V - 128) - 0.391 * (U - 128)

    R = 1.164 * (Y - 16) + 1.596 * (V - 128)

YUV420P - это 12 битовый рlanar(упакованный 2x2) формат. Где 4 бита яркости распределены в блоке 2х2 побитово(1 бить на пиксель), а каналы яркоси общие для всего блока.

Что не ясно то?

#2
11:22, 26 авг 2008

en emot
Изображение удалено

Из этого не могу понять (у меня 16-ти битный формат YUYV) как правильно вынуть данные для формул что выше :)

#3
11:29, 26 авг 2008

en emot
>YUV420P - это 12 битовый рlanar(упакованный 2x2) формат. Где 4 бита яркости распределены в блоке 2х2 побитово(1 бить на пиксель), а каналы яркоси общие для всего блока.
Во намудрил...

_vasa_
YUV420P, он же I420, он же YV12 хранит картинку в трех плоскостях (planar). Первая - полноразмерная черно-белая картинка (Y компонента), по 8 бит на пиксел, за ней следом идут две картинки U/V плоскостей (8 бит на пиксел), каждая в два раза меньше основной картинки по обеим осям (т.е. в 4 раза меньше общим числом пикселей).
Канал яркости (Y) хранит 8 бит для каждого пиксела основной картинки, а "пикселы" цветоразностных UV каналов хранят общие цветоразностные компоненты для яркостного блока 2х2 пиксела.
Никакого побитового распределения нет.

YUV422 отличается от YUV420 тем, что цветоразностные каналы меньше основной картинки только по горизонтали (один пиксел U/V компоненты на два соседних пикселя Y компоненты).
Число строк в UV каналах yuv422 такое же, как и в яркостном канале.
YUV422 может быть packed или planar. Если он packed, то данные в строке картинки лежат как [Y0, U, Y1, V] (или U/V наоборот, не помню точно). Т.е. два соседних яркостных пиксела, и по одному пикселу из U/V каналов. Если он planar, то данные лежат отдельными смежными картинками, т.е. (к примеру) 320х240 картинка яркостного канала, а за ней две картинки 160х240 цветоразностных каналов.

Под фразой "цветоразностные пикселы общие для яркостного блока NxM" имеется в виду, что данные цветоразностных каналов равны для всех пикселей яркостного канала, а не делятся побитово или поровну на всех.

#4
11:32, 26 авг 2008

_vasa_
>Из этого не могу понять (у меня 16-ти битный формат YUYV) как правильно вынуть данные для формул что выше :)
Пиксел первый - Y0 U0 V0
Пиксел второй - Y1 U0 V0
третий - Y2 U2 V2
четвертый - Y3 U2 V2

Цветоразностные компоненты общие для двух соседних пикселов.

#5
11:41, 26 авг 2008

Вот конвертор, который когда то для себя пискал. Пользуй на здоровье.

void YUV2RGB( unsigned char *yuv_buffer, unsigned char *rgb_buffer, int width, int height )
{
  int len = width * height;

  unsigned char *u_buffer = yuv_buffer + len;
  unsigned char *v_buffer = u_buffer + len / 2;

  int rgb_index = 0;
  int y_index  = 0;

// обрабатываем по 2 пикселя за цикл
  for (int i = 0; i < len / 2; i++)
  {
    int u = u_buffer - 128;
    int v = v_buffer
- 128;

    int v1  = (5727 * v);
    int uv1 = -(1617 * u) - (2378 * v);
    int u1  = (8324 * u);

  // четный пиксель в пакете

    int y1 = yuv_buffer[y_index] << 12;

    int r = (y1 + v1) >> 12;
    int g = (y1 + uv1) >> 12;
    int b = (y1 + u1) >> 12;

    if (r > 255) r = 255;
    if (g > 255) g = 255;
    if (b > 255) b = 255;

    if (r < 0) r = 0;
    if (g < 0) g = 0;
    if (b < 0) b = 0;

    rgb_buffer[rgb_index + 0] = r;
    rgb_buffer[rgb_index + 1] = g;
    rgb_buffer[rgb_index + 2] = b;

  // нечетный пиксель в пакете

    y1 = yuv_buffer[y_index + 1] << 12;

    r = (y1 + v1) >> 12;
    g = (y1 + uv1) >> 12;
    b = (y1 + u1) > >12;

    if (r > 255) r = 255;
    if (g > 255) g = 255;
    if (b > 255) b = 255;

    if (r < 0) r = 0;
    if (g < 0) g = 0;
    if (b < 0) b = 0;

    rgb_buffer[rgb_index + 3] = r;
    rgb_buffer[rgb_index + 4] = g;
    rgb_buffer[rgb_index + 5] = b;

    rgb_index += 6;
    y_index += 2;
  }
}

PS но это как написал RPGman выше для planar типа.

#6
12:02, 26 авг 2008

Всем пасибо большое.
Небольшой вопрос - в packed формате YUV420P три плоскости идут "смешанным" массивом байтов { [Y0 U0 Y1 V0], [Y2 U0 Y3 V0] }, правильно?

#7
12:27, 26 авг 2008

>в packed формате YUV420P
Суффикс 'P' означает Planar, а не packed.  Пакед 420 как-то не доводилось видеть, да и неправильно дублировать U0 и V0 в соседних блоках. Не для того yuv выдумывали.

#8
22:01, 1 сен 2008

Оцените конверторы

void cYUV420P::Reload()
{
  //W = 352;
  //H = 240;
  if( (W % 2 != 0) || (H % 2 != 0) )
    return;

  D3DLOCKED_RECT lrect;
  UCHAR *pBits = NULL;
  UCHAR *pRow = NULL, *pRow1 = NULL;
  int iPitch = 0;

  if(SUCCEEDED(pTexture->LockRect(0, &lrect, NULL, 0)) )
  {
    DWORD dwColor = 0;
    CHAR cAlpha = 0xFF;
    BYTE *pColor = (BYTE*) &dwColor;
    *(pColor+3) = cAlpha;

    UINT  tx = 0, ty = 0, ty1 = 0;
    UCHAR R = 0, G = 0, B = 0;
    UCHAR Y = 0;        // 0 -- 255
    CHAR  U = 0, V = 0; // -127 -- 128
    UCHAR* pYData = NULL, pY1Data = NULL;
    CHAR*  pUData = NULL, pVData = NULL;
          
    pUData = this->pDataU; // size = W*H / 4
    pVData = this->pDataV; // size = W*H / 4

    // texture init
    pBits = (UCHAR*) lrect.pBits;
    iPitch = lrect.Pitch;
    
    for(ty = 0; ty < H; ty += 2) // 0, 2, 4
    {                             //  1, 3, 5
      ty1 = ty + 1;
      pYData  = this->pData + W*ty;
      pY1Data = this->pData + W*ty1; // pYData + W

      pRow  = pBits + ty  * iPitch;
      pRow1 =  pBits + ty1 * iPitch;

      for(tx = 0; tx < W; tx += 2)
      {  // 2x2 pixels  in 2 rows

        U = *pUData;    
        V = *pVData;
        ++pUData;
        ++pVData;

        // (0, 0)
        Y = *pYData; // [Y+0 U V]
        ++pYData;     
        *(pColor)   = (CHAR)(Y + 1.403*V);           // R
        *(pColor+1) = (CHAR)(Y - 0.344*U - 0.714*V); // G
        *(pColor+2) = (CHAR)(Y + 1.770*U);           // B
        memcpy(pRow, pColor, 4);
        pRow += 4;

        // (0, 1)
        Y = *pY1Data;// [Y+W U V]
        ++pY1Data;         
        *(pColor)   = (CHAR)(Y + 1.403*V);           // R
        *(pColor+1) = (CHAR)(Y - 0.344*U - 0.714*V); // G
        *(pColor+2) = (CHAR)(Y + 1.770*U);           // B
        memcpy(pRow1, pColor, 4);
        pRow1 += 4;

        // (1, 0)
        Y = *pYData; // [Y+1 U V]        
        *(pColor)   = (CHAR)(Y + 1.403*V);           // R
        *(pColor+1) = (CHAR)(Y - 0.344*U - 0.714*V); // G
        *(pColor+2) = (CHAR)(Y + 1.770*U);           // B
        memcpy(pRow, pColor, 4);        

        // (1, 1)
        Y = *pY1Data;// [Y+W+1 U V]          
        *(pColor)   = (CHAR)(Y + 1.403*V);           // R
        *(pColor+1) = (CHAR)(Y - 0.344*U - 0.714*V); // G
        *(pColor+2) = (CHAR)(Y + 1.770*U);           // B
        memcpy(pRow1, pColor, 4);

      } // for W      
    } // for H
    pTexture->UnlockRect(0);
  }// if lock    
}


void cYUV422::Reload()
{
  //W = 352;
  //H = 240;
  if( (W % 2 != 0) )
    return;

  D3DLOCKED_RECT lrect;
  UCHAR *pBits = NULL;
  UCHAR *pRow = NULL;
  int iPitch = 0;

  if(SUCCEEDED(pTexture->LockRect(0, &lrect, NULL, 0)) )
  {
    DWORD dwColor = 0;
    CHAR cAlpha = 0xFF;
    BYTE *pColor = (BYTE*) &dwColor;
    *(pColor+3) = cAlpha;

    UINT  tx = 0, ty = 0;
    UCHAR R = 0, G = 0, B = 0;
    UCHAR Y = 0;        // 0 -- 255
    CHAR  U = 0, V = 0; // -127 -- 128
    UCHAR* pYData = NULL;
    CHAR*  pUData = NULL, pVData = NULL;
          
    pUData = this->pDataU; // size = W/2*H
    pVData = this->pDataV; // size = W/2*H

    // texture init
    pBits = (UCHAR*) lrect.pBits;
    iPitch = lrect.Pitch;
    
    for(ty = 0; ty < H; ++ty)
    {                            
      pYData = this->pData + W*ty;  
      pRow = pBits + ty * iPitch;

      for(tx = 0; tx < W; tx += 2)
      {  // 2 pixels  in row

        U = *pUData;    
        V = *pVData;
        ++pUData;
        ++pVData;

        // (0, 0)
        Y = *pYData; // [Y+0 U V]
        ++pYData;     
        *(pColor)   = (CHAR)(Y + 1.403*V);           // R
        *(pColor+1) = (CHAR)(Y - 0.344*U - 0.714*V); // G
        *(pColor+2) = (CHAR)(Y + 1.770*U);           // B
        memcpy(pRow, pColor, 4);
        pRow += 4;

        // (1, 0)
        Y = *pYData; // [Y+1 U V]          
        *(pColor)   = (CHAR)(Y + 1.403*V);           // R
        *(pColor+1) = (CHAR)(Y - 0.344*U - 0.714*V); // G
        *(pColor+2) = (CHAR)(Y + 1.770*U);           // B
        memcpy(pRow, pColor, 4);
      } // for W      
    } // for H
    pTexture->UnlockRect(0);
  }// if lock 
}
#9
22:42, 1 сен 2008

_vasa_
хорошо работает? :)
в общем, особо со смыслом не разбирался, но вот пара советов по оптимизации:
1) убрать переводы int -> double -> int (они не очень то быстрые) и перейти на целочисленную арифметику, то есть блоки типа

 *(pColor)   = (CHAR)(Y + 1.403*V);           // R
 *(pColor+1) = (CHAR)(Y - 0.344*U - 0.714*V); // G
 *(pColor+2) = (CHAR)(Y + 1.770*U);           // B

меняешь на

 *(pColor)   = (CHAR)(Y + 1437*V / 1024);           // R
 *(pColor+1) = (CHAR)(Y - (352*U - 731*V) / 1024); // G
 *(pColor+2) = (CHAR)(Y + 1812*U / 1024);           // B

деление на 1024 компилятор должен заменить на сдвиг вправо на 10 бит.
если я правильно посчитал, потеря точности при этом довольно небольшая 0.0015 (в сумме относительно значений U,V) так что визуально заметно не будет

2) вместо memcpy(pRow, pColor, 4);
поставить
*(DWORD*)pRow = dwColor;

#10
22:59, 1 сен 2008

gexogen
Насчет работает быстро или нет не знаю еще, мне важно вначале алгоритм правильно создать :) где такие имиджи взять (в таких форматах) не знаю. Самому разве сгенерить...
memcpy заменю, а насчет float, сильно снизит скорость выполнения?

#11
23:06, 1 сен 2008

а, ну если ты пока незнаешь правильно работает или нет, то лучше пока что так оставить, оптимзить уже после.
И еще у меня возникло сомнение не перепутал ли ты R и B каналы

#12
23:10, 1 сен 2008

Насчет перепутал не знаю. В смысле формулы или размещение в DWORD ?

Эти формулы с какого-то семпла, наверно заменю на то что c fourcc.org

*(pColor)  = (CHAR) (1.164 * (Y-16) + 1.596 * (V-128) );                    // R
*(pColor+1) = (CHAR) (1.164 * (Y-16) - 0.813 * (V-128) - 0.391 * (U-128) ); // G
*(pColor+2) = (CHAR) (1.164 * (Y-16) + 2.018 * (U-128) ); // B

#13
9:49, 2 сен 2008

насчет формул я незнаю, я смотрел по по комментариям при записи в DWORD - // R написано напротив самого младшего байта, а это обычно B

ПрограммированиеФорумОбщее

Тема в архиве.