'개발'에 해당되는 글 46건

  1. 2008.09.29 BMP 파일에서 RGB 데이터 보기
  2. 2008.09.29 YUV -> RGB 변환
  3. 2008.09.29 컬러 모델
  4. 2008.09.29 YUV(YCbCr) 변환에 대하여
  5. 2008.09.29 YUV to RGB conversion
2008. 9. 29. 23:19

BMP 파일에서 RGB 데이터 보기

http://cafe.naver.com/carroty/12008

그럼, 이번에 공구한 TFT-LCD는 보통 QVGA급(320 x 240; Quarter VGA, 즉, 640 x 480 VGA의 1/4에 해당하는 크기를 말함)이므로 일단 320 x 240 사이즈의 테스트용 비트맵 파일을 가지고 실험을 해봤다.

테스트 사진 중에 가로로 긴 사진이 없어서 세로로 긴 사진을 가지고 테스트했다.

(그러나, 사진을 보면 잘했다는 생각이 들것임. *^^*) 일단, 완소 김태희 양의 사진을 가지고 분석했다.
 

1.jpg

 
완소 김태희 사진이 들어있는 BMP파일을 첨부파일을 울트라 에디터로 열어보면 바이너리 모드로 열린다. 그리고, 탐색기에서 해당 비트맵 파일의 등록정보를 열어서 파일 사이즈 및 비트맵 정보를 확인하자. 마우스로 오른쪽 클릭하고 등록정보를 확인하면 파일 크기가 230,454 바이트(=16진수로는 0x38436 바이트)이고, 비트 깊이는 24비트임을 알 수 있다. 물론 크기는 240 x 320 픽셀이다.

 자, 그 다음에 울트라 에디터로 살짝 파일을 열어보면 파일 앞 부분이 다음과 같이 되어 있음을 알 수 있다.

2.jpg

 위의 그림에서 빨간색 박스로 그려진 부분이 BMP파일의 헤더에 해당한다. 위의 헤더파일의 구조는 다음과 같다.

3.jpg

 생각보다 BMP 파일의 헤더 구조는 단순하다. 그리고, 저 헤더부분 뒤에 이미지의 RGB 데이터가 바로 이어지기 때문에 BMP파일에서 RGB 데이터를 추출하고자 할 때에는 헤더부분을 제외한 다음 부분부터 파싱을 하면 된다. 헤더 부분을 잠시 살펴보면 모든 BMP 파일은 첫 두바이트가 0x41, 0x4D로 시작한다. 이를 보통 매직코드라고 부른다. 대부분의 특정한 포맷을 가진 파일들은 위와 같은 매직코드를 가지고 있는 경우가 많다. 그 다음 필드는 BMP 파일의 사이즈인데, 아까 등록정보로 0x38436 바이트라고 했는데, 그 값이 뒤집혀서(즉, 리틀 엔디안 형식으로) 저장되어 있다. 그리고, 다섯번째 필드를 보면 실제 이미지 데이터가 시작하는 오프셋의 위치를 담고 있는데... 0x36번째 부터란다. 헤더 부분의 총크기를 계산하면 0 ~ 0x35이므로 총 0x36바이트를 가진다. 따라서, 오프셋의 위치와 정확히 일치함을 알 수 있다. 그리고, image width/height in pixels 필드는 그림 파일의 가로/세로 픽셀 수에 해당하는데 저 값을 10진수로 바꿔보면 240 x 320이다. 그 다음 가장 중요한 "size of image data in bytes"는 실제 이미지 데이터의 크기를 말한다(데이터 크기가 정말 저 값이 맞는지는 각자 확인하기 바람 *^^*). 또한, number of bits per pixel 은 한 픽셀당 비트수를 말하는데, 0x18이니까 십진수로 24비트이다. 아까 등록정보에서 확인한 값과 일치한다. 이런 식으로 헤더정보를 파싱하면 된다.


이제, 헤더의 다음부분 부터 이미지 데이터가 어떤 식으로 저장되어 있는지 살펴보면...

신기하게도... 좌측상단의 점부터 데이터를 저장하고 있는 것이 아니라, 맨 아래줄부터 데이터가 저장된다.

무슨 말인고 하면...

 

4.jpg

 

위의 그림처럼 BMP파일의 그림 데이터는 라인단위로 저장되는데... 이미지의 가장 아래 라인부터 역순으로 저장된다. 게다가, 저장되는 순서도 RGB(8비트, 8비트, 8비트) 순서로 저장되는 것이 아니라 BGR 순서로 저장된다. 정말???

5.jpg

 

위의 모서리 값은 실제 이미지 데이터의 RGB값을 그래픽 뷰어 프로그램으로 찾아 본 것인데, 헤더 부분의 그 다음 3바이트를 확인해보면...

 

EC, EF, E6

 

임을 알 수 있는데, 그 값은 정확히 위 의 그림에서 ③번 모서리의 RGB의 역순임을 알 수 있다. 이런 식으로 BMP파일에서 RGB 데이터를 추출하면 된다.


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];
  }

}

2008. 9. 29. 18:28

컬러 모델

색상을 표현하는 방법을 나타내며 다음과 같은 모델이 사용되어진다.

가. RGB 모델

 - 색상이 Red, Green, Blue의 3색 강도를 통해 표현된다.

 - 컬러 CRT의 R, G, B 전자총에 쉽게 매핑되므로 비디오 디스플레이 드라이버에 편리한 모델

 

나. CMY 모델

 - Cyan, Magenta, Yellow

 - subtractive 모델 [C, M, Y] = [1, 1, 1] - [R, G, B]

 - 컬러 프린터에서 흰색 바탕에 색을 뿌릴 때 사용됨.

 - C + M + Y로 best black을 만들지 못하므로 K(pure black)을 추가(CMYK)

 

다. CIE color space

 - Commission Internationale de I'Eclarage에 의한 규격

 - 휘도(luminance, Y)와 두 색상값(chrominance value; x, y)

 

라. YIQ(YUV, YCrCb) 모델

 - 휘도(luminance) - 색상(chrominance; color difference)

 - YIQ(NTSC), YUV(PAL, SECAM)

 - 흑백 TV와의 호환(Y 신호가 gray level 제공)

 - 변환계

 - Y(luminance: CIE의 Y) = 0.3R + 0.59G + 0.11B

   I(chrominance: orange - cyan hue) = 0.6R - 0.28G - 0.32B

   Q(chrominance: green - magenta hue) = 0.21R - 0.52G + 0.31B

   R = Y + 0.956I + 9.62Q

   G = Y - 0.272I - 0.647Q

   B = Y - 1.108I - 1.705Q

- Y = 0.3R + 0.59G + 0.11B

   U = (B-Y) * 0.493

   V = (R-Y) * 0.877   

   R = Y + 0.956U + 9.621V

   G = Y - 0.272U - 0.647V

   B = Y - 1.1061U - 1.703V

- YCrCb(MPEG)

  Y = 0.3R + 0.59G + 0.11B

  Cr = R - Y

  Cb = B - Y

 

마. HSI 모델

 - 색조(hue), 채도(saturation), 밝기(intensity) 값

 - 영상처리에 적합한 모델


2008. 9. 29. 18:27

YUV(YCbCr) 변환에 대하여


YUV와 YIQ는 TV에 사용되는 색 표현방식이다.
YUV방식은 사람의 눈이 색상보다는 밝기에 민감하다는 사실에 착안한
방식으로,
색을 밝기(Luminance)인 Y성분과 색상(Chrominance)인 U(Cb)V(Cr) 성분으로

구분한다.
Y성분은 오차에 민감하므로 색상성분인 U와 V보다 많은 비트를 코딩한다.

전형적인 Y:U:V의 비율은 4:2:2 이다.
YUV 방식은 CD-I와 DYI (Digital Video Interactive)에서도 사용된다.

(Y 성분은 이미지를 Gray로 만든 것과 같다)

 

왜 RGB 정보를 그냥 이용하지 않고 YUV로 포맷을 변화시키냐면,

단순히 RGB 정보로 그 값을 기록했을 경우보다 YUV를 통하면 더 작은 데이터를 

만들어내서, 특히 대역폭(bandwidth)이 낮은

경우에 적합하게 표현할 수 있기 때문이다

 

+주의점+

YUV 파일 형식은, U,V data의 배열에 따라 Direction이 존재하는데 보통

Direction Vertical의 YUV420을 주로 사용한다.

그래서 파일로 저장할때는, Direction을 염두에 두고, 파일에 저장해야 하는데-

Direction Vertical은 Y에 덧붙여 U를 쓰고, V를 모두 쓴다

Direction Horizontal은 Y에 덧붙여서 U를 width/2 화소만큼, 

V를 width/2 화소만큼 번갈아가며 저장하게 된다

저장방법이 복잡해서 Direction Vertical을 주로 쓴다

 

 

 

(1) 전형적 Y:U:V 비율은 4:2:2로,, Y 4픽셀당 U, V는 2픽셀씩이라는 의미임.

ex> YUYV,YUYV,YUYV..

 

변환식은-

______________________________________________

Y = 0.3*R + 0.59*G + 0.11*B
U = (B-Y) x 0.493
V = (R-Y) x 0.877

..or..

Y = 0.299f * R + 0.587f * G + 0.114f * B;
U = -0.1687f * R - 0.3313f * G + 0.5f * B + 128;
V = 0.5f*R - 0.4187f*G - 0.0813f*B + 128; 

 

 

R = Y + 0.956*U + 0.621*V
G = Y + 0.272*U + 0.647*V
B = Y + 1.1061*U + 1.703*V

 

R = 1.00000*Y + 1.40200*V;
G = 1.00000*Y - 0.34414*U - 0.71414*V;
B = 1.00000*Y + 1.77200*U;

R = 1.164*(Y - 16.0) + 1.596*(V - 128.0);
G = 1.164*(Y - 16.0) - 0.813*(V - 128.0) - 0.391*(U - 128.0);
B = 1.164*(Y - 16.0) + 2.018*(U - 128.0);
───────────────────────────────

...

 

 

(2) Y:U:V 비율이 4:2:0인 경우,, Y 4픽셀 당 U, V는 가로/세로 2픽셀당

1픽셀씩이라는 의미임.

ex> 4x4 이미지일 경우,,

 Y1 Y2 Y3 Y4 Y5 Y6 Y7 Y8 Y9 Y10 Y11 Y12 Y13 Y14 Y15 Y16  U1 U2 U3 U4 

  V1 V2 V3 V4..

 

 

(3) Y:U:V 4:1:1은 화상을 구성하는 화소를 4개씩 묶어서 각각의 휘도(Y)를 샘플링하고, 색차(U,V)는 4개의 화소의 평균값을 취한다.

그러므로 4개의 화소에 4개의 휘도( Y)와 각 1개씩의 색차(U,V)가 있으므로

전체 6개의 샘플이 된다

ex > U2 Y0 Y1 V2 Y2 Y3
 

 

(4) YUV444에서 YUV420으로 변환하는 경우,, UV의 4화소를 평균하여 1화소로

바꿔 줄여준다

bool Yuv4442Yuv420(BYTE*U444,BYTE*V444,BYTE *U,BYTE *V,int wid,int hei)
{
    int i, j;
    register int addr;
    register int pos0, pos1, pos2, pos3;
 
    addr = 0;
 
    for (i=0; i<hei; i+=2) for(j=0; j<wid; j+=2)
    {
        pos0 = i*wid + j;       pos1 = pos0 + 1;
        pos2 = pos0 + wid;      pos3 = pos2 + 1;

 

        //오른쪽 쉬프트 연산 2번

        U[addr] = (BYTE)MyClip(((int)

           U444[pos0]+(int)U444[pos1]+(int)U444[pos2]+(int)U444[pos3])>>2); 

        //즉 나누기 4 : 평균내기
        V[addr] = (BYTE)MyClip(((int)

           V444[pos0]+(int)V444[pos1]+(int)V444[pos2]+(int)V444[pos3])>>2);  
        addr++;
    }
    return  true;
}

 

 

2008. 9. 29. 18:26

YUV to RGB conversion


YUV to RGB 변환 코드를 작성하시오.

 

step1)

 

   #define CLIP(x) (((x) < 0) ? 0 : (((x) > 255) ? 255 : (x)))


   void Yuv2Rgb(int Y, int U, int V, int &R, int &G, int &B)
   {
      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) ;

 

      B = CLIP(B);
      G = CLIP(G);
      R = CLIP(R);
   }


 => 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)

 (YUV 포멧 및 RGB 변환에 관해서는 http://www.fourcc.org/ 를 참조하라.)


 위의 공식은 실제 몇몇 픽셀에서 overflow 가 발생할 것이다.

 0~255의 범위를 벗어나는 값이 나온다.  이를 방지하기 위해, 0~ 255 값으로 clipping 이 필요하다.

 


step2)

 

   #define CLIP(x) (((x) < 0) ? 0 : (((x) > 255) ? 255 : (x)))

 

   void Yuv2Rgb(int Y, int U, int V, int &R, int &G, int &B)
   {
      B = ( 76284*(Y-16)                 + 132252*(U-128) ) >> 16;
      G = ( 76284*(Y-16) - 53281*(V-128) - 25625*(U-128) ) >> 16 ;
      R = ( 76284*(Y-16) + 104595*(V-128) ) >> 16 ;

     

      B = CLIP(B);
      G = CLIP(G);
      R = CLIP(R);
   }


 => YUV, RGB 변환 공식을 약간 변형하면 아래와 같다.

 B = 65536*(1.164*(Y-16)                 + 2.018*(U-128) ) / 65536
 G = 65536*(1.164*(Y-16) - 0.813*(V-128) - 0.391*(U-128) ) / 65536
 R = 65536*(1.164*(Y-16) + 1.596*(V-128) ) / 65536

 

 B = ( 76284*(Y-16)                  + 132252*(U-128) ) >> 16
 G = ( 76284*(Y-16) -  53281*(V-128) -  25625*(U-128) ) >> 16
 R = ( 76284*(Y-16) + 104595*(V-128) ) >> 16
 
 변형된 공식을 사용함으로서, floating point 연산을 없엘 수 있다.