конвертация I420 в RGB
От: temofey  
Дата: 14.05.04 08:45
Оценка:
Работал с камерой, от которой получал картинку — буффер и его длину. Проблема в том, что картинка с камеры
идет в формате i420..экономит канал...нодо конвертнуть i420 в RGB. Изучив информацию, которая приводится на www.fourcc.org
набросал код, который вроде бы должен работать...и он работает, но картинка частично "плывет". Возможно кто-нибудь уже сталкивался с такой проблемой..и если да — имеет кусок кода осуществляющего данное преобразование. В любом случае, привожу код, посмотрите — может у Вас получится увидеть ошибку, которую не вижу я.

Заранее благодарен.

struct TM_YUV
{
        TM_YUV(){ Y=0; U=0; V=0; };
        BYTE Y;
        BYTE U;
        BYTE V;
};
//---------------------------------------------------------------------------

struct TM_RGB24
{
        TM_RGB24(){ R=0; G=0; B=0; };
        BYTE R;
        BYTE G;
        BYTE B;
};
//---------------------------------------------------------------------------
пример набросан в среде борланд с++ билдер 6.0

void __fastcall TForm1::BitBtn1Click(TObject *Sender)
{
  Graphics::TBitmap* bmp = new Graphics::TBitmap();

  bmp->Width  = 352;
  bmp->Height = 288;
  bmp->PixelFormat = pf24bit;

  vector<BYTE>     i420; // данные формата i420
  vector<TM_YUV>   yuv; // данные формата YUV
  vector<TM_RGB24> rgb; // данные RGB

// в файле "i420image.dat" содержатся данные одной картники формата i420

  TFileStream* fs = new TFileStream( ExtractFilePath(Application->ExeName) + "i420image.dat", fmOpenRead );

  i420.resize( fs->Size );
  fs->Read( i420.begin(), fs->Size );
  delete fs;

  yuv.resize(bmp->Width*bmp->Height);

// Переписали все компоненты яркости, в данном формате их по каждой на пиксел
  for( int i = 0; i < bmp->Width*bmp->Height; i++ )
  {
       yuv[i].Y = i420[i];
  }

  int Counter = 0;

// Теперь компоненты цветоразностей, они одинаковы в каждом макропикселе, который представляет собой квадрат размером 2х2 пиксела
  for( int  i = 0; i < bmp->Width*bmp->Height/4; i++ )
  {
       yuv[Counter].U                  =
       yuv[Counter + 1].U              =
       yuv[Counter + bmp->Width].U     =
       yuv[Counter + bmp->Width + 1].U = i420[bmp->Width*bmp->Height + i];

       yuv[Counter].V                  =
       yuv[Counter + 1].V              =
       yuv[Counter + bmp->Width].V     =
       yuv[Counter + bmp->Width + 1].V = i420[bmp->Width*bmp->Height + i + bmp->Width*bmp->Height/4];

       Counter += 2;
       if( Counter%bmp->Width == 0 )
           if( (Counter/bmp->Width)%2 != 0 )
                Counter += bmp->Width;
  }

// теперь конфертнем из YUV в RGB
  rgb.resize( bmp->Width*bmp->Height );
  for( int i = 0; i < rgb.size(); i++ )
  {
       rgb[i].B = 1.164*(yuv[i].Y - 16)                   + 2.018*(yuv[i].U - 128);
       rgb[i].G = 1.164*(yuv[i].Y - 16) - 0.813*(yuv[i].V - 128) - 0.391*(yuv[i].U - 128);
       rgb[i].R = 1.164*(yuv[i].Y - 16) + 1.596*(yuv[i].V - 128);
  }

// выведем на канву формы
  for( int y = 0; y < bmp->Height; y++ )
  {
      Byte* ptr = (Byte*)bmp->ScanLine[y];
      int c=0;
      for(int x = 0; x < bmp->Width; x++)
      {
          ptr[c]   = rgb[y*bmp->Width + x].B;
          ptr[c+1] = rgb[y*bmp->Width + x].G;
          ptr[c+2] = rgb[y*bmp->Width + x].R;
          c+=3;
      }
  }
  Canvas->Draw( 0, 0, bmp );
  delete bmp;
}
Re: конвертация I420 в RGB
От: Sinyagin Dmitry www.astawireless.com
Дата: 14.05.04 14:46
Оценка:
уволил бы за такой код
Re[2]: конвертация I420 в RGB
От: temofey  
Дата: 14.05.04 21:57
Оценка:
Здравствуйте, Sinyagin Dmitry, Вы писали:

SD>уволил бы за такой код


ответ не в тему
давайте не будем ссоритться, ибо меня интересует в первую очередь корректность алгоритма преобразования...
или Вы где-нибудь в моем вопросе встретили фразу — :"мужчины, оцените мой код!?"
Код не претендует ни коим образом!!! на гениальность и ясен — не оптимизирован...он реализует определенную
последовательность действий по шагам так, чтобы человек знакомый с методикой i429->RGB смог достаточно
быстро прикинуть где я ошибся.
Смею Вас уверить нмкогда и никуда в таком виде код не пойдет.

Одно могу сказать точно — читать Ваш ответ мне было неприянто.
Я сказал все.

ЗЫ.Если Вы не удовлетворены моими объяснениями, или считаете что я возмутился несправедливо —
может продолжить дискуссию по эл. почте.
Re: конвертация I420 в RGB
От: c-smile Канада http://terrainformatica.com
Дата: 16.05.04 16:21
Оценка:
Здравствуйте, temofey, Вы писали:

Что такое "плывет"?

На вскидку: если имеем дело с Windows 24bpp bitmap
то там есть специфика — каждый pixel row должен содержать кол-во байтов кратное 4.
Re[2]: конвертация I420 в RGB
От: temofey  
Дата: 17.05.04 13:10
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Здравствуйте, temofey, Вы писали:


CS>Что такое "плывет"?


CS>На вскидку: если имеем дело с Windows 24bpp bitmap

CS>то там есть специфика — каждый pixel row должен содержать кол-во байтов кратное 4.

да, об этом позаботился...размер картинки 352х288...24 бит на пиксель
около 70% картинки декодируется нормально...замечена такая закономерность:
там, где слишком "светло" цвет перебрасывается в черный.
там, где слишком "темно" цвет перебрасывается в зеленый.
кое-где появляются небольшие пятна желтого...
если отбросить компоненты цветоразности, то как и должно быть получается
нормальная черно-белая картинка.
Вероятно изменение цвета наблюдается из-за того, что я не отслеживаю
выхода в результате мат операций за границы 0...255
но это надо еще проверить
...чем и займусь сегодня перед сном
Re[3]: конвертация I420 в RGB
От: romson  
Дата: 17.05.04 20:48
Оценка:
Здравствуйте, temofey, Вы писали:

T>около 70% картинки декодируется нормально...замечена такая закономерность:

T>там, где слишком "светло" цвет перебрасывается в черный.
T>там, где слишком "темно" цвет перебрасывается в зеленый.

Не парься, лучше посмотри исходники какого-нить XviD'а. Вот например небольшой кусочек оттуда, как раз для твоей задачи:

int32_t RGB_Y_tab[256];
int32_t B_U_tab[256];
int32_t G_U_tab[256];
int32_t G_V_tab[256];
int32_t R_V_tab[256];

/*    yuv -> rgb def's */

#define RGB_Y_OUT        1.164
#define B_U_OUT            2.018
#define Y_ADD_OUT        16

#define G_U_OUT            0.391
#define G_V_OUT            0.813
#define U_ADD_OUT        128

#define R_V_OUT            1.596
#define V_ADD_OUT        128


#define SCALEBITS_OUT   13
#define FIX_OUT(x)      ((uint16_t) ((x) * (1L<<SCALEBITS_OUT) + 0.5))

/* initialize rgb lookup tables */

void
colorspace_init(void)
{
  int32_t i;

  for (i = 0; i < 256; i++) {
    RGB_Y_tab[i] = FIX_OUT(RGB_Y_OUT) * (i - Y_ADD_OUT);
    B_U_tab[i] = FIX_OUT(B_U_OUT) * (i - U_ADD_OUT);
    G_U_tab[i] = FIX_OUT(G_U_OUT) * (i - U_ADD_OUT);
    G_V_tab[i] = FIX_OUT(G_V_OUT) * (i - V_ADD_OUT);
    R_V_tab[i] = FIX_OUT(R_V_OUT) * (i - V_ADD_OUT);
  }
}

/* yuv 4:2:0 planar -> rgb24 */

void
yv12_to_rgb24_c(uint8_t * dst,
  int dst_stride,
  uint8_t * y_src,
  uint8_t * u_src,
  uint8_t * v_src,
  int y_stride,
  int uv_stride,
  int width,
  int height)
{
  const uint32_t dst_dif = 6 * dst_stride - 3 * width;
  int32_t y_dif = 2 * y_stride - width;

  uint8_t *dst2 = dst + 3 * dst_stride;
  uint8_t *y_src2 = y_src + y_stride;
  uint32_t x, y;

  if (height < 0) {            /* flip image? */
    height = -height;
    y_src += (height - 1) * y_stride;
    y_src2 = y_src - y_stride;
    u_src += (height / 2 - 1) * uv_stride;
    v_src += (height / 2 - 1) * uv_stride;
    y_dif = -width - 2 * y_stride;
    uv_stride = -uv_stride;
  }

  for (y = height / 2; y; y--) {
    /* process one 2x2 block per iteration */
    for (x = 0; x < (uint32_t) width / 2; x++) {
      int u, v;
      int b_u, g_uv, r_v, rgb_y;
      int r, g, b;

      u = u_src[x];
      v = v_src[x];

      b_u = B_U_tab[u];
      g_uv = G_U_tab[u] + G_V_tab[v];
      r_v = R_V_tab[v];

      rgb_y = RGB_Y_tab[*y_src];
      b = (rgb_y + b_u) >> SCALEBITS_OUT;
      g = (rgb_y - g_uv) >> SCALEBITS_OUT;
      r = (rgb_y + r_v) >> SCALEBITS_OUT;
      dst[0] = MAX(0, MIN(255, b));
      dst[1] = MAX(0, MIN(255, g));
      dst[2] = MAX(0, MIN(255, r));

      y_src++;
      rgb_y = RGB_Y_tab[*y_src];
      b = (rgb_y + b_u) >> SCALEBITS_OUT;
      g = (rgb_y - g_uv) >> SCALEBITS_OUT;
      r = (rgb_y + r_v) >> SCALEBITS_OUT;
      dst[3] = MAX(0, MIN(255, b));
      dst[4] = MAX(0, MIN(255, g));
      dst[5] = MAX(0, MIN(255, r));
      y_src++;

      rgb_y = RGB_Y_tab[*y_src2];
      b = (rgb_y + b_u) >> SCALEBITS_OUT;
      g = (rgb_y - g_uv) >> SCALEBITS_OUT;
      r = (rgb_y + r_v) >> SCALEBITS_OUT;
      dst2[0] = MAX(0, MIN(255, b));
      dst2[1] = MAX(0, MIN(255, g));
      dst2[2] = MAX(0, MIN(255, r));
      y_src2++;

      rgb_y = RGB_Y_tab[*y_src2];
      b = (rgb_y + b_u) >> SCALEBITS_OUT;
      g = (rgb_y - g_uv) >> SCALEBITS_OUT;
      r = (rgb_y + r_v) >> SCALEBITS_OUT;
      dst2[3] = MAX(0, MIN(255, b));
      dst2[4] = MAX(0, MIN(255, g));
      dst2[5] = MAX(0, MIN(255, r));
      y_src2++;

      dst += 6;
      dst2 += 6;
    }

    dst += dst_dif;
    dst2 += dst_dif;

    y_src += y_dif;
    y_src2 += y_dif;

    u_src += uv_stride;
    v_src += uv_stride;
  }
}


Обрати внимание на таблицы RGB_Y_tab и пр. — они и нужны как раз для того, чтобы отсекать значения, выходящие за пределы 0-255, получающиеся при преобразовании.
Re[4]: конвертация I420 в RGB
От: romson  
Дата: 17.05.04 21:05
Оценка:
Пардон, насчёт RGB_Y_tab это я немного попутал с другим алгоритмом, а здесь отсекается обычными минмаксами
Re: конвертация I420 в RGB
От: sashkamaslukov  
Дата: 18.05.04 15:03
Оценка:
Здравствуйте, temofey, Вы писали:

T>Работал с камерой, от которой получал картинку — буффер и его длину. Проблема в том, что картинка с камеры

T>идет в формате i420..экономит канал...нодо конвертнуть i420 в RGB. Изучив информацию, которая приводится на www.fourcc.org
T>набросал код, который вроде бы должен работать...и он работает, но картинка частично "плывет". Возможно кто-нибудь уже сталкивался с такой проблемой..и если да — имеет кусок кода осуществляющего данное преобразование. В любом случае, привожу

Стандартная ошибка потеря точности...
ты когда конвертишь из YUV в RGB используй не байты а для начала float-ы..
и только тогда поймешь что для формулы типа этой
BYTE b1 = 0.36*byte2 — 1.18*byte3
иногда будут переполнения ( как ты сказал иногда слишком черное иногда белое).
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.