Converting RGB image to YUV using C programming - c

I have images as bitmap and JPEG. I will have to retrieve the pixels from the image there by RGB values of all pixels are obtained. Please suggest a method where RGB values are retrieved from an image file. I would appreciated if there are any functions available in C.

You can parse and get bitmap from JPEG using libJPEG - it is pretty simple
Suppose you have and RGB bimap in 'rgb'. Result will be placed in 'yuv420p' vector.
void rgb2yuv420p(std::vector<BYTE>& rgb, std::vector<BYTE>& yuv420p)
{
unsigned int i = 0;
unsigned int numpixels = width * height;
unsigned int ui = numpixels;
unsigned int vi = numpixels + numpixels / 4;
unsigned int s = 0;
#define sR (BYTE)(rgb[s+2])
#define sG (BYTE)(rgb[s+1])
#define sB (BYTE)(rgb[s+0])
yuv420p.resize(numpixels * 3 / 2);
for (int j = 0; j < height; j++)
for (int k = 0; k < width; k++)
{
yuv420p[i] = (BYTE)( (66*sR + 129*sG + 25*sB + 128) >> 8) + 16;
if (0 == j%2 && 0 == k%2)
{
yuv420p[ui++] = (BYTE)( (-38*sR - 74*sG + 112*sB + 128) >> 8) + 128;
yuv420p[vi++] = (BYTE)( (112*sR - 94*sG - 18*sB + 128) >> 8) + 128;
}
i++;
s += colors;
}
}

If you want to do this yourself, here's teh Wikipedia article that I worked from when I did this at work, about a year back:
http://en.wikipedia.org/wiki/YUV
This is pretty good too:
http://www.fourcc.org/fccyvrgb.php
But MUCH easier is jpeglib - that wasn't an option in my case, because the data wasn't jpeg in the first place.

Related

How to left pad a 2 dimensional byte array with leading zeros in C#

I have a 2 dimensional byte array of a bitmap pixel data that I need to send to a thermal printer using ESC/POS via comport. I can do this successfully. However, I need to shift the printed image to the right. Center justify, Right justify, HT, and all other ESC/POS commands have no effect due to the command used to print the bitmap (DC2 * r n [d1.....dn]).
I wish to left pad the array of bytes containing the bitmap in order to shift the printed image to the right. Below are my code lines to print bitmap
private void Print_Bipmap()
{
int x;
int y;
int i;
int RowBytes;
byte n;
Color Pixels;
byte[,] ImageArray = new byte[bitmap.Width, bitmap.Height];
// Calculate output size
RowBytes = (bitmap.Width + 7) / 8;
// Generate body of array
for (y = 0; y < bitmap.Height; y++)
{ // Each row...
for (x = 0; x < (bitmap.Width / 8); x++)
{ // Each 8-pixel block within row...
ImageArray[x, y] = 0;
for (n = 0; n < 8; n++)
{ // Each pixel within block...
Pixels = bitmap.GetPixel(x * 8 + n, y);
if (Pixels.GetBrightness() < 0.5)
{
ImageArray[x, y] += (byte)(1 << (7 - n));
}
}
}
}
comport_writeByte(18); //DC2
comport_writeByte(42); //*
comport_writeByte((byte)bitmap.Height); //r
comport_writeByte((byte)RowBytes); //n
for (y = 0; y < bitmap.Height; y++)
{
for (x = 0; x < RowBytes; x++)
{
comport_writeByte(ImageArray[x, y]); //[d1 ..... dn]
}
}
}
How do I left pad the 2 dimensional array (ImageArray[x, y])?
Many thanks in advance.

Wrong result for non square image

I am trying to implement dark (not exactly)emboss filter, my problem is when I use it on SQUARED Lena image 512x512 result is good.
But when I use it on image which has rectangular shape e.g. 1280x720 result is all messed up, why is it so? Format of images is RGB.
GOOD result with Lena 512x512 (original):
WRONG result with 1280x720 image (original not same size just for comparison):
For a 24bit image, if the width of the image is 682 then it needs padding. Because 682*3 is not a multiple of 4. Try changing the image width to 680 and try again.
To pad the image rows, use the following formula:
int pad = WIDTH % 4;
if(pad == 4) pad = 0;
WIDTH += pad;
Change the condition to fb_j < HEIGHT - 1 - FILTER_HEIGHT and fb_i < WIDTH - 1 - FILTER_WIDTH to avoid buffer overflow.
The bitmap is scanned from top to bottom. It works fine when I switched the dimension as follows (but I loaded the bitmap differently)
//Pixel frame_buffer[WIDTH][HEIGHT];
//Pixel temp_buffer[WIDTH][HEIGHT];
Pixel frame_buffer[HEIGHT][WIDTH];
Pixel temp_buffer[HEIGHT][WIDTH];
...
for(int fb_j = 1; fb_j < HEIGHT - 1 - FILTER_HEIGHT; fb_j++) {
for(int fb_i = 1; fb_i < WIDTH - 1 - FILTER_WIDTH; fb_i++) {
float r = 0, g = 0, b = 0;
for(int ker_i = 0; ker_i < FILTER_WIDTH; ker_i++) {
for(int ker_j = 0; ker_j < FILTER_HEIGHT; ker_j++) {
r += ((float)(frame_buffer[fb_j + ker_j][fb_i + ker_i].r / 255.0) * emboss_kernel[ker_j][ker_i]);
g += ((float)(frame_buffer[fb_j + ker_j][fb_i + ker_i].g / 255.0) * emboss_kernel[ker_j][ker_i]);
b += ((float)(frame_buffer[fb_j + ker_j][fb_i + ker_i].b / 255.0) * emboss_kernel[ker_j][ker_i]);
}
}
if(r > 1.0) r = 1.0;
else if(r < 0) r = 0;
if(g > 1.0) g = 1.0;
else if(g < 0) g = 0;
if(b > 1.0) b = 1.0;
else if(b < 0) b = 0;
// Output buffer which will be rendered after convolution
temp_buffer[fb_j][fb_i].r = (GLubyte)(r*255.0);
temp_buffer[fb_j][fb_i].g = (GLubyte)(g*255.0);
temp_buffer[fb_j][fb_i].b = (GLubyte)(b*255.0);
}
}
Also try running a direct copy for testing. Example:
temp_buffer[fb_j][fb_i].r = frame_buffer[fb_j][fb_i].r;
temp_buffer[fb_j][fb_i].g = frame_buffer[fb_j][fb_i].g;
temp_buffer[fb_j][fb_i].b = frame_buffer[fb_j][fb_i].b;

24bpp to 8bpp conversion C with raw image data

I am currently trying to convert raw binary image data (512 x 512 24bpp) to a 512 x 512 8bpp image by using 3bits for the R channel, 3 for the G channel, and 2 for the B channel. However when using my code my picture comes out grey scale? Can anyone tell me what I'm doing wrong?
/*24 bit per pixel - 8 bit per pixel transformation*/
unsigned char buf[512][512][3];
unsigned char in[512][512][3];
unsigned char out[512][512][3];
unsigned char pix[512][512];
int main(){
FILE *fp, *output;
int i, j;
/*open file*/
if((fp = fopen("LennaRGB512.data", "rb")) == NULL){
printf("error opening file\n");
}
/*read file into buffer*/
for (i = 0; i < 512; i++) {
for (j = 0; j < 512; j++) {
buf[i][j][0] = fgetc(fp); /*r*/
buf[i][j][1] = fgetc(fp); /*g*/
buf[i][j][2] = fgetc(fp); /*b*/
in[i][j][0] = buf[i][j][0];
in[i][j][1] = buf[i][j][1];
in[i][j][2] = buf[i][j][2];
}
}
fclose(fp);
output = fopen("lenna_8bpp.data", "wb");
for(i = 0; i < 512; i++){
char pix[512][512];
for(j = 0; j < 512; j++){
out[i][j][0] = (in[i][j][0] * 8) / 256;
out[i][j][1] = (in[i][j][1] * 8) / 256;
out[i][j][2] = (in[i][j][2] * 4) / 256;
pix[i][j] = (out[i][j][0] << 5) | (out[i][j][1] << 2) | out[i][j][2];
fputc(pix[i][j], output);
}
}
fclose(output);
return 0;
}
There are tons of questions on doing this with .bmp files and others but I can't find any help with manipulating the raw image data pixel by pixel.
I agree with the commenters. I think the grayscale is very likely an artifact of your viewer rather than your conversion. However, your conversion can also be improved. Try the following output loop:
unsigned char pix; /* don't need 512*512 of them. */
unsigned char r, g, b;
for(row = 0; row < 512; row++){
for(col = 0; col < 512; col++){
r = in[row][col][0] >> 5; /* keep 3 bits */
g = in[row][col][1] >> 5;
b = in[row][col][2] >> 6; /* keep 2 bits */
pix = (r << 5) | (g << 2) | b;
fputc(pix, output);
}
}
You are only processing one pixel at a time, so you only need one pix value.
For each of the r, g, and b, color components (remember to specify unsigned char throughout), use >> (right shift) to drop all the bits except the most significant. This is simpler and more clear than the *8/256 sequence. Also, I believe *8/256 only works because arithmetic is promoted to int — if it were done in chars, the *8 could cause overflow and lose data.
Edit The problem is indeed in the display. I have posted a palette and instructions on my blog since the full contents are too long for the space here. Yes, I know link-only answers are bad :( . I just saved it into the Archive in case of link rot.
You do need to open the image as Indexed, and then assign the colormap of the image.

Decode from VP8 video frame to RGB

In my application, we need to display the video frame on the screen. I use libvpx to decode a video from WebM, but frame is decoded to YUV format (VPX_IMG_FMT_I420 according to the documentation). I need to output format is RGB and the documentation says a image supported a RGB format (VPX_IMG_FMT_RGB24). I have a formula for translating YUV->RGB:
R = Y + 1.13983 * (V - 128);
G = Y - 0.39465 * (U - 128) - 0.58060 * (V - 128);
B = Y + 2.03211 * (U - 128);
But I think is too many conversions VP8->YUV->RGB. Is there a method for set a output frame format for conversion function?
If you can afford using Intel's IPP library, here is some CPU friendly piece of code that you can try and apply in your project:
unsigned char* mpRGBBuffer;
void VPXImageToRGB24(vpx_image_t* pImage, bool isUsingBGR)
{
const unsigned int rgbBufferSize = pImage->d_w * pImage->d_h * 3;
mpRGBBuffer - allocate your raw RGB buffer...
const IppiSize sz = { pImage->d_w, pImage->d_h };
const Ipp8u* src[3] = { pImage->planes[PLANE_Y], pImage->planes[PLANE_U], pImage->planes[PLANE_V] };
int srcStep[3] = { pImage->stride[VPX_PLANE_Y], pImage->stride[VPX_PLANE_U], pImage->stride[VPX_PLANE_V] };
if (isUsingBGR) ippiYCbCr420ToBGR_8u_P3C3R(src, srcStep, pDest, pImage->d_w * 3, sz);
else ippiYCbCr420ToRGB_8u_P3C3R(src, srcStep, pDest, pImage->d_w * 3, sz);
}
If you dont want to use IPP, here is a link to some working peace of core that could really be usefull. Tested this, works for 100% but not sure about the CPU cost.
Here is the code from the link above (in case link fails...)
inline int clamp8(int v)
{
return std::min(std::max(v, 0), 255);
}
Image VP8Decoder::convertYV12toRGB(const vpx_image_t* img)
{
Image rgbImg(img->d_w, img->d_h);
std::vector<uint8_t>& data = rgbImg.data;
uint8_t *yPlane = img->planes[VPX_PLANE_Y];
uint8_t *uPlane = img->planes[VPX_PLANE_U];
uint8_t *vPlane = img->planes[VPX_PLANE_V];
int i = 0;
for (unsigned int imgY = 0; imgY < img->d_h; imgY++) {
for (unsigned int imgX = 0; imgX < img->d_w; imgX++) {
int y = yPlane[imgY * img->stride[VPX_PLANE_Y] + imgX];
int u = uPlane[(imgY / 2) * img->stride[VPX_PLANE_U] + (imgX / 2)];
int v = vPlane[(imgY / 2) * img->stride[VPX_PLANE_V] + (imgX / 2)];
int c = y - 16;
int d = (u - 128);
int e = (v - 128);
// TODO: adjust colors ?
int r = clamp8((298 * c + 409 * e + 128) >> 8);
int g = clamp8((298 * c - 100 * d - 208 * e + 128) >> 8);
int b = clamp8((298 * c + 516 * d + 128) >> 8);
// TODO: cast instead of clamp8
data[i + 0] = static_cast<uint8_t>(r);
data[i + 1] = static_cast<uint8_t>(g);
data[i + 2] = static_cast<uint8_t>(b);
i += 3;
}
}
return rgbImg;
}

need to create a webm video from RGB frames

I have an app that generates a bunch of jpgs that I need to turn into a webm video. I'm trying to get my rgb data from the jpegs into the vpxenc sample. I can see the basic shapes from the original jpgs in the output video, but everything is tinted green (even pixels that should be black are about halfway green) and every other scanline has some garbage in it.
I'm trying to feed it VPX_IMG_FMT_YV12 data, which I'm assuming is structured like so:
for each frame
8-bit Y data
8-bit averages of each 2x2 V block
8-bit averages of each 2x2 U block
Here is a source image and a screenshot of the video that is coming out:
Images
It's entirely possible that I'm doing the RGB->YV12 conversion incorrectly, but even if I only encode the 8-bit Y data and set the U and V blocks to 0, the video looks about the same. I'm basically running my RGB data through this equation:
// (R, G, and B are 0-255)
float y = 0.299f*R + 0.587f*G + 0.114f*B;
float v = (R-y)*0.713f;
float u = (B-v)*0.565f;
.. and then to produce the 2x2 filtered values for U and V that I write into vpxenc, I just do (a + b + c + d) / 4, where a,b,c,d are the U or V values of each 2x2 pixel block.
So I'm wondering:
Is there an easier way (in code) to take RGB data and feed it to vpx_codec_encode to get a nice webm video?
Is my RGB->YV12 conversion wrong somewhere?
Any help would be greatly appreciated.
freefallr: Sure. Here is the code. Note that it's converting the RGB->YUV in place as well as putting the YV12 output into pFullYPlane/pDownsampledUPlane/pDownsampledVPlane. This code produced nice looking WebM videos when I modified their vpxenc sample to use this data.
void RGB_To_YV12( unsigned char *pRGBData, int nFrameWidth, int nFrameHeight, void *pFullYPlane, void *pDownsampledUPlane, void *pDownsampledVPlane )
{
int nRGBBytes = nFrameWidth * nFrameHeight * 3;
// Convert RGB -> YV12. We do this in-place to avoid allocating any more memory.
unsigned char *pYPlaneOut = (unsigned char*)pFullYPlane;
int nYPlaneOut = 0;
for ( int i=0; i < nRGBBytes; i += 3 )
{
unsigned char B = pRGBData[i+0];
unsigned char G = pRGBData[i+1];
unsigned char R = pRGBData[i+2];
float y = (float)( R*66 + G*129 + B*25 + 128 ) / 256 + 16;
float u = (float)( R*-38 + G*-74 + B*112 + 128 ) / 256 + 128;
float v = (float)( R*112 + G*-94 + B*-18 + 128 ) / 256 + 128;
// NOTE: We're converting pRGBData to YUV in-place here as well as writing out YUV to pFullYPlane/pDownsampledUPlane/pDownsampledVPlane.
pRGBData[i+0] = (unsigned char)y;
pRGBData[i+1] = (unsigned char)u;
pRGBData[i+2] = (unsigned char)v;
// Write out the Y plane directly here rather than in another loop.
pYPlaneOut[nYPlaneOut++] = pRGBData[i+0];
}
// Downsample to U and V.
int halfHeight = nFrameHeight >> 1;
int halfWidth = nFrameWidth >> 1;
unsigned char *pVPlaneOut = (unsigned char*)pDownsampledVPlane;
unsigned char *pUPlaneOut = (unsigned char*)pDownsampledUPlane;
for ( int yPixel=0; yPixel < halfHeight; yPixel++ )
{
int iBaseSrc = ( (yPixel*2) * nFrameWidth * 3 );
for ( int xPixel=0; xPixel < halfWidth; xPixel++ )
{
pVPlaneOut[yPixel * halfWidth + xPixel] = pRGBData[iBaseSrc + 2];
pUPlaneOut[yPixel * halfWidth + xPixel] = pRGBData[iBaseSrc + 1];
iBaseSrc += 6;
}
}
}
Never mind. The scheme I was using was correct but I had a bug in the U/V downsampling code.

Resources