Sample program for converting RGB to YCbCr to DCT coefficients and vice versa.
/*
rgb_ybr.cpp
Containing functions for converting RGB to YCbCr and vice versa.
*/
#include <stdio.h>
#include <stdlib.h>
#include "common.h"
#include "rgb_ybr.h"
/*
Convert from RGB to YCbCr using ITU-R recommendation BT.601.
Y = 0.299R + 0.587G + 0.114B
Cb = 0.564(B-Y)
Cr = 0.713(R-Y)
Integer arithmetic is used to speed up calculations:
0.299 ~ 19595 / 2^16,
0.587 ~ 38470 / 2^16
.....
Input: an RGB pixel
Output: a YCbCr "pixel".
*/
void rgb2ycbcr( RGB &rgb, YCbCr &ycb )
{
//coefs summed to 65536 ( 1 << 16 ), so Y is always within [0, 255]
ycb.Y = (short)((19595 * rgb.R + 38470 * rgb.G + 7471 * rgb.B ) >> 16);
ycb.Cb = (short)( ( 36962 * ( rgb.B - ycb.Y ) ) >> 16);
ycb.Cr = (short)(( 46727 * ( rgb.R - ycb.Y ) ) >> 16);
}
//just convert an RGB pixel to Y component
void rgb2y( RGB &rgb, short &y )
{
y = (short)((19595 * rgb.R + 38470 * rgb.G + 7471 * rgb.B ) >> 16);
}
//taking care of round off errors; ensure RGB values lie within [0, 255]
int chop_rgb ( int x )
{
if ( x < 0 ) {
x = 0;
} else if ( x > 255 ) {
x = 255;
}
return x;
}
/*
Convert from YCbCr to RGB domain. Using ITU-R standard:
R = Y + 1.402Cr
G = Y - 0.344Cb - 0.714Cr
R = B = Y + 1.772Cb
Integer arithmetic is used to speed up calculations.
*/
void ycbcr2rgb( YCbCr &ycb, RGB &rgb )
{
int Y = ( int ) ycb.Y << 16; //same as multiply 65536
rgb.R = chop_rgb ( ( Y + (int)91881 * ycb.Cr ) >> 16 );
rgb.G = chop_rgb ( ( Y - (int)22544 * ycb.Cb - (int)46793 * ycb.Cr ) >> 16 );
rgb.B = chop_rgb ( ( Y + (int) 116129 * ycb.Cb ) >> 16 );
}
/*
Convert an RGB macro block ( 16x16 ) to 4:2:0 YCbCr sample blocks ( six 8x8 blocks ).
*/
void macroblock2ycbcr ( RGB *macro16x16, YCbCr_MACRO *ycbcr_macro )
{
int i, j, k, r;
short y;
YCbCr ycb;
r = k = 0;
for ( i = 0; i < 16; ++i ) {
for ( j = 0; j < 16; ++j ) {
if ( !( i & 1 ) && !( j & 1 ) ) { //need one Cb, Cr for every 4 pixels
rgb2ycbcr ( macro16x16[r], ycb ); //convert to Y and Cb, Cr values
ycbcr_macro->Y[r] = ycb.Y;
ycbcr_macro->Cb[k] = ycb.Cb;
ycbcr_macro->Cr[k] = ycb.Cr;
k++;
} else { //only need the Y component for other 3 pixels
rgb2y ( macro16x16[r], ycbcr_macro->Y[r] );
}
r++; //convert every pixel for Y
}
}
}
/*
Convert the 6 8x8 YCbCr sample blocks to RGB macroblock ( 16x16 ).
*/
void ycbcr2macroblock( YCbCr_MACRO *ycbcr_macro, RGB *macro16x16 )
{
int i, j, k, r;
short y;
YCbCr ycb;
r = k = 0;
for ( i = 0; i < 16; ++i ) {
for ( j = 0; j < 16; ++j ) {
if ( !( i & 1 ) && !( j & 1 ) ) { //one Cb, Cr has been saved for every 4 pixels
ycb.Y = ycbcr_macro->Y[r];
ycb.Cb = ycbcr_macro->Cb[k];
ycb.Cr = ycbcr_macro->Cr[k];
ycbcr2rgb ( ycb, macro16x16[r]);
k++;
} else {
ycb.Y = ycbcr_macro->Y[r];
ycbcr2rgb( ycb, macro16x16[r] );
}
r++;
}
}
}
void print_ymacro ( YCbCr_MACRO ycbcr_macro )
{
int i, j, k;
k = 0;
for ( i = 0; i < 16; ++i ) {
printf("\n");
for ( j = 0; j < 16; ++j ) {
printf("%4d", ycbcr_macro.Y[k++] );
if ( j % 4 == 3 ) printf ( " | " );
}
}
printf("\n---------------------------------------\n");
k = 0;
for ( i = 0; i < 8; ++i ) {
printf("\n");
for ( j = 0; j < 8; ++j ){
printf("%4d", ycbcr_macro.Cb[k++] );
}
}
printf("\n---------------------------------------\n");
k = 0;
for ( i = 0; i < 8; ++i ) {
printf("\n");
for ( j = 0; j < 8; ++j ){
printf("%4d", ycbcr_macro.Cr[k++] );
}
}
}