2008. 9. 29. 19:09

YUV -> RGB 변환


미디어 세상이 되면서 YUV -> RGB 변환이 많이 중요해졌죠.

동영상 압축 포맷에서 압축을 풀면 영상 기기에 적합한 YUV 포맷으로 데이터가 나오고,

화면에 표현하려면 RGB로 변환이 필요하죠.

함수는 convert() 함수하나면 됩니다.

예제는 qcif 파일 읽어서 한 프레임만 BMP 파일로 저장합니다.(단 BMP로 못 여는 데, 이유는 BMP 헤더를

작성하지 않았기 때문입니다. BMP는 헤더 + 데이터로 구성되는데, 헤더가 없습니다. 어디까지나 동영상 재생용이므로)

변환 테스트였고, convert() 함수를 한 번만 호출합니다.

YUV 플레이어라는 건 1초에 24번씩 convert() 함수를 호출하면서 이미지를 바꾸면 된다는 겁니다.

영상 샘플은
http://daci.digitalsystems.cs.tut.fi/daci_release/ra_daci_videobench_mpeg4_sp_vbr.html

에 종류별로 있습니다.

숫자나 파일명은 전부 하드코드되어 있습니다. ㅎㅎ

GUI 작업하는 사람은 convert()만 호출하면 됩니다. convert() 호출해서 사용법만 알면 되고,

YUV -> RGB 변환에 대해서는

http://wiki.kldp.org/wiki.php/DemuxMPEG
http://minzkn.wowdns.com:2744/tattertools/130
http://www.asmlove.co.kr/zBdC7/viewtopic.php?p=235
http://kelp.or.kr/korweblog/stories.php?story=06/12/14/1451655

이 정도면 충분할 듯.

왜 소스 코드 구현은 위 답변과 다른가? 라는 의문이 있다면

표준 MPEG Decoder 레퍼런스 구현을 이용해서 그렇고,

그 레퍼런스 구현이란 건 최적화 하기 위해 실수 연산을 제거하기 위한 비트 연산으로 대체하기

신공을 사용하기 보단

변환 공식에 따른 미리 계산된 테이블 만들기 신공으로 최적화를 해버렸기 때문.

즉, 변환 공식에 따라 계산하지 않고 y, b, r의 값이 뭐뭐면 결과값은 A라는 식으로 정리된

변환 공식 테이블이라 생각하면 됨.

Visual C++에서 컴파일하려면 cl /Zp2로 정렬 옵션 주면되고,

gcc에서는 어떻게 했더라. __attribute(packed)__ 이렇게 썼던 것 같기도 하네요.(검색하면 나옴)

구현은 C이므로 윈도우, 리눅스, 임베디드 모두 잘 돌아갑니다.

YUV Player 제작에 도움이 되시길. 이해하려 하지 말고 그냥 쓰시길.

ps. 이런 소스 코드 공개에도 망설여야하니,
돈이 개입된 곳은 참 지저분해~

// compile: cl.exe /Zp2     정렬제한을 2로 컴파일

#include <stdio.h>
#include <stdlib.h>

long int tab_76309[256];
long int crv_tab[256];
long int cbu_tab[256];
long int cgu_tab[256];

long int cgv_tab[256];

long int bilevel_tab_76309[256];
unsigned char* clp;

void init_dither_tab();
void init_clp();


void convert( unsigned char *src0, unsigned char *src1,
      unsigned char *src2, unsigned char *dst_ori,
      int width, int height, int imtype );


int main(void)
{
 FILE *in, *out;
 unsigned int picture_size = 176 * 144;
 //int timespan = 1000/atoi(30);
  unsigned char *buffer = (unsigned char *)malloc( 176 * 144 * 3 );
  unsigned char *y_data = (unsigned char *)malloc( 176 * 144 );
  unsigned char *u_data = (unsigned char *)malloc( 88 * 72  );
  unsigned char *v_data = (unsigned char *)malloc( 88 * 72 );
 
  in = fopen( "walk_qcif.yuv", "rb" );
  out = fopen( "walk.rgb", "wb" );
 
  // width * 144 * current_frame_no * 3 / 2
  //  fseek( in, 176 * 144 * 375 * 3 / 2, SEEK_SET );
  fread( y_data, 1, 176 * 144, in );
  fread( u_data, 1, 88 * 72 , in );
  fread( v_data, 1, 88 * 72, in );

  init_dither_tab();
  init_clp();

  convert( y_data, u_data, v_data, buffer, 176, 144, 420 );
 
 fwrite( buffer, sizeof( char ), 176 * 144 * 3, out );
 fclose( in );
 fclose( out );
}

 


// src0 -> y data, src1 -> u data, src2 -> v data
void convert( unsigned char *src0, unsigned char *src1,
      unsigned char *src2, unsigned char *dst_ori,
      int width, int height, int imtype )
{
    int i, j, k, u, v, u2g, u2b, v2g, v2r, y11, y12, y21, y22;
    int width4 = width + (4 - (width%4))%4;
    unsigned char* pucY0, * pucY1, * pucU, * pucV, * pucRaster0, * pucRaster1;
    pucY0 = src0;
    pucY1 = src0 + width;
    pucU = src1;
    pucV = src2;
    pucRaster0 = dst_ori + width4 * (height-1)*3;
    pucRaster1 = pucRaster0 - width4*3;

 switch( imtype )
 {
      case 444:
        for (i = 0; i < height; i++) {
            for (j = 0; j < width; j++) {
                y11 = tab_76309[*pucY0++];
                u = *pucU++;
                v = *pucV++;
                u2g = cgu_tab[u];
                u2b = cbu_tab[u];
                v2g = cgv_tab[v];
                v2r = crv_tab[v];
                *pucRaster0++ = clp[(y11 + u2b)>>16];
                *pucRaster0++ = clp[(y11 - u2g - v2g)>>16];
                *pucRaster0++ = clp[(y11 + v2r)>>16];
            }
            for (;j < width4; j++) {
                for (k = 0; k < 3; k++) {
                    *pucRaster0++ = 0;
                }
            }
            pucRaster0 = pucRaster0 - 2*width4*3;
        }
        break;

  case 422:
        for (i = 0; i < height; i++) {
            for (j = 0; j < width; j+=2) {
                y11 = tab_76309[*pucY0++];
                y12 = tab_76309[*pucY0++];
                u = *pucU++;
                v = *pucV++;
                u2g = cgu_tab[u];
                u2b = cbu_tab[u];
                v2g = cgv_tab[v];
                v2r = crv_tab[v];
                *pucRaster0++ = clp[(y11 + u2b)>>16];
                *pucRaster0++ = clp[(y11 - u2g - v2g)>>16];
                *pucRaster0++ = clp[(y11 + v2r)>>16];

                *pucRaster0++ = clp[(y12 + u2b)>>16];
                *pucRaster0++ = clp[(y12 - u2g - v2g)>>16];
                *pucRaster0++ = clp[(y12 + v2r)>>16];
            }
            for (;j < width4; j++) {
                for (k = 0; k < 3; k++) {
                    *pucRaster0++ = 0;
                }
            }
            pucRaster0 = pucRaster0 - 2*width4*3;
        }
        break;

     case 420:
   for (i = 0; i < height ; i+=2) {
  
            for (j = 0; j < width ; j+=2) {
    
                y11 = tab_76309[*pucY0++];
                y12 = tab_76309[*pucY0++];
                y21 = tab_76309[*pucY1++];
                y22 = tab_76309[*pucY1++];
                u = *pucU++;
                v = *pucV++;
                u2g = cgu_tab[u];
                u2b = cbu_tab[u];
                v2g = cgv_tab[v];
                v2r = crv_tab[v];

                *pucRaster0++ = clp[(y11 + u2b)>>16];
                *pucRaster0++ = clp[(y11 - u2g - v2g)>>16];
                *pucRaster0++ = clp[(y11 + v2r)>>16];
                *pucRaster0++ = clp[(y12 + u2b)>>16];
                *pucRaster0++ = clp[(y12 - u2g - v2g)>>16];
                *pucRaster0++ = clp[(y12 + v2r)>>16];

                *pucRaster1++ = clp[(y21 + u2b)>>16];
                *pucRaster1++ = clp[(y21 - u2g - v2g)>>16];
                *pucRaster1++ = clp[(y21 + v2r)>>16];
                *pucRaster1++ = clp[(y22 + u2b)>>16];
                *pucRaster1++ = clp[(y22 - u2g - v2g)>>16];
                *pucRaster1++ = clp[(y22 + v2r)>>16];

            }
            for (;j < width4; j++) {
                for (k = 0; k < 3; k++) {
                    *pucRaster0++ = 0;
                    *pucRaster1++ = 0;
                }
            }
            pucRaster0 = pucRaster0 - 3*width4*3;
            pucRaster1 = pucRaster1 - 3*width4*3;
            pucY0 += width;
            pucY1 += width;
        }
  break;

      case 24: // 24-bit RGB
        for (i = 0; i < height; i++) {
            for (j = 0; j < width; j++) {
                y11 = *pucY0++;
                u = *pucU++;
                v = *pucV++;
                *pucRaster0++ = v;
                *pucRaster0++ = u;
                *pucRaster0++ = y11;
            }
            for (;j < width4; j++) {
                for (k = 0; k < 3; k++) {
                    *pucRaster0++ = 0;
                }
            }
            pucRaster0 = pucRaster0 - 2*width4*3;
        }
        break;

      case 256: // Gray Level
        for (i = 0; i < height; i++) {
            for (j = 0; j < width; j++) {
                y11 = clp[tab_76309[*pucY0++] >> 16];
                *pucRaster0++ = y11;
                *pucRaster0++ = y11;
                *pucRaster0++ = y11;
            }
            for (;j < width4; j++) {
                for (k = 0; k < 3; k++) {
                    *pucRaster0++ = 0;
                }
            }
            pucRaster0 = pucRaster0 - 2*width4*3;
        }
        break;
 }
 
}

void init_clp()
{
  int i;
  clp = (unsigned char *)malloc(1024);
  clp += 384;

  for (i=-384; i<640; i++)
    clp[i] = (i<0) ? 0 : ((i>255) ? 255 : i);
}

void init_dither_tab()
{
  long int crv,cbu,cgu,cgv;
  int i;  
 
  crv = 104597; cbu = 132201;  /* fra matrise i global.h */
  cgu = 25675;  cgv = 53279;
 
  for (i = 0; i < 256; i++) {
    crv_tab[i] = (i-128) * crv;
    cbu_tab[i] = (i-128) * cbu;
    cgu_tab[i] = (i-128) * cgu;
    cgv_tab[i] = (i-128) * cgv;
    tab_76309[i] = 76309*(i-16);
  }
  bilevel_tab_76309[0] = tab_76309[0];
  for (i = 1; i < 256; i++) {
   bilevel_tab_76309[i] = tab_76309[255];
  }

}