What is the simplest RGB image format? - c

I am working in C on a physics experiment, Young's interference experiment and I made a program who prints to file a huge bunch of pixels:
for (i=0; i < width*width; i++)
{
fwrite(hue(raster_matrix[i]), 1, 3, file);
}
Where hue, when given a value [0..255], gives back a char * with 3 bytes, R,G,B.
I would like to put a minimal header in my image file in order to make this raw file a valid image file.
More concise, switching from:
offset
0000 : height * width : data } my data, 24bit RGB pixels
to:
offset
0000 : dword : magic \
: /* ?? */ \
0012 : dword : height } Header <--> common image file
0016 : dword : width /
: /* ?? */ /
0040 : height * width : data } my data, 24bit RGB pixels

You probably want to use the PPM format which is what you're looking for: a minimal header followed by raw RGB.

TARGA (file name extension .tga) may be the simplest widely supported binary image file format if you don't use compression and don't use any of its extensions. It's even simpler than Windows .bmp files and is supported by ImageMagick and many paint programs. It has been my go-to format when I just need to output some pixels from a throwaway program.
Here's a minimal C program to generate an image to standard output:
#include <stdio.h>
#include <string.h>
enum { width = 550, height = 400 };
int main(void) {
static unsigned char pixels[width * height * 3];
static unsigned char tga[18];
unsigned char *p;
size_t x, y;
p = pixels;
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
*p++ = 255 * ((float)y / height);
*p++ = 255 * ((float)x / width);
*p++ = 255 * ((float)y / height);
}
}
tga[2] = 2;
tga[12] = 255 & width;
tga[13] = 255 & (width >> 8);
tga[14] = 255 & height;
tga[15] = 255 & (height >> 8);
tga[16] = 24;
tga[17] = 32;
return !((1 == fwrite(tga, sizeof(tga), 1, stdout)) &&
(1 == fwrite(pixels, sizeof(pixels), 1, stdout)));
}

The recently created farbfeld format is quite minimal, though there is not much software supporting it (at least so far).
Bytes │ Description
8 │ "farbfeld" magic value
4 │ 32-Bit BE unsigned integer (width)
4 │ 32-Bit BE unsigned integer (height)
(2+2+2+2)*width*height │ 4*16-Bit BE unsigned integers [RGBA] / pixel, row-major

Here's a minimal example that writes your image file with a minimal PPM header. Happily, I was able to get it to work with the exact for loop you've provided:
#include <math.h> // compile with gcc young.c -lm
#include <stdio.h>
#include <stdlib.h>
#define width 256
int main(){
int x, y, i; unsigned char raster_matrix[width*width], h[256][3];
#define WAVE(x,y) sin(sqrt( (x)*(x)+(y)*(y) ) * 30.0 / width)
#define hue(i) h[i]
/* Setup nice hue palette */
for (i = 0; i <= 85; i++){
h[i][0] = h[i+85][1] = h[i+170][2] = (i <= 42)? 255: 40+(85-i)*5;
h[i][1] = h[i+85][2] = h[i+170][0] = (i <= 42)? 40+i*5: 255;
h[i][2] = h[i+85][0] = h[i+170][1] = 40;
}
/* Setup Young's Interference image */
for (i = y = 0; y < width; y++) for (x = 0; x < width; x++)
raster_matrix[i++] = 128 + 64*(WAVE(x,y) + WAVE(x,width-y));
/* Open PPM File */
FILE *file = fopen("young.ppm", "wb"); if (!file) return -1;
/* Write PPM Header */
fprintf(file, "P6 %d %d %d\n", width, width, 255); /* width, height, maxval */
/* Write Image Data */
for (i=0; i < width*width; i++)
fwrite(hue(raster_matrix[i]), 1, 3, file);
/* Close PPM File */
fclose(file);
/* All done */
return 0;
}
The header code is based on the specs at http://netpbm.sourceforge.net/doc/ppm.html. For this image, the header is just a string of fifteen bytes: "P6 256 256 255\n".

Related

RGB to YUV conversion with libav (ffmpeg) triplicates image

I'm building a small program to capture the screen (using X11 MIT-SHM extension) on video. It works well if I create individual PNG files of the captured frames, but now I'm trying to integrate libav (ffmpeg) to create the video and I'm getting... funny results.
The furthest I've been able to reach is this. The expected result (which is a PNG created directly from the RGB data of the XImage file) is this:
However, the result I'm getting is this:
As you can see the colors are funky and the image appears cropped three times. I have a loop where I capture the screen, and first I generate the individual PNG files (currently commented in the code below) and then I try to use libswscale to convert from RGB24 to YUV420:
while (gRunning) {
printf("Processing frame framecnt=%i \n", framecnt);
if (!XShmGetImage(display, RootWindow(display, DefaultScreen(display)), img, 0, 0, AllPlanes)) {
printf("\n Ooops.. Something is wrong.");
break;
}
// PNG generation
// snprintf(imageName, sizeof(imageName), "salida_%i.png", framecnt);
// writePngForImage(img, width, height, imageName);
unsigned long red_mask = img->red_mask;
unsigned long green_mask = img->green_mask;
unsigned long blue_mask = img->blue_mask;
// Write image data
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
unsigned long pixel = XGetPixel(img, x, y);
unsigned char blue = pixel & blue_mask;
unsigned char green = (pixel & green_mask) >> 8;
unsigned char red = (pixel & red_mask) >> 16;
pixel_rgb_data[y * width + x * 3] = red;
pixel_rgb_data[y * width + x * 3 + 1] = green;
pixel_rgb_data[y * width + x * 3 + 2] = blue;
}
}
uint8_t* inData[1] = { pixel_rgb_data };
int inLinesize[1] = { in_w };
printf("Scaling frame... \n");
int sliceHeight = sws_scale(sws_context, inData, inLinesize, 0, height, pFrame->data, pFrame->linesize);
printf("Obtained slice height: %i \n", sliceHeight);
pFrame->pts = framecnt * (pVideoStream->time_base.den) / ((pVideoStream->time_base.num) * 25);
printf("Frame pts: %li \n", pFrame->pts);
int got_picture = 0;
printf("Encoding frame... \n");
int ret = avcodec_encode_video2(pCodecCtx, &pkt, pFrame, &got_picture);
// int ret = avcodec_send_frame(pCodecCtx, pFrame);
if (ret != 0) {
printf("Failed to encode! Error: %i\n", ret);
return -1;
}
printf("Succeed to encode frame: %5d - size: %5d\n", framecnt, pkt.size);
framecnt++;
pkt.stream_index = pVideoStream->index;
ret = av_write_frame(pFormatCtx, &pkt);
if (ret != 0) {
printf("Error writing frame! Error: %framecnt \n", ret);
return -1;
}
av_packet_unref(&pkt);
}
I've placed the entire code at this gist. This question right here looks pretty similar to mine, but not quite, and the solution did not work for me, although I think this has something to do with the way the line stride is calculated.
Don't use av_image_alloc use av_frame_get_buffer.
(unrelated to your question, But using avcodec_encode_video2 is considered bad practice now and should be replaced with avcodec_send_frame and avcodec_receive_packet)
In the end, the error was not in the usage of libav but on the code that fills the pixel data from XImage to the rgb vector. Instead of using:
pixel_rgb_data[y * width + x * 3 ] = red;
pixel_rgb_data[y * width + x * 3 + 1] = green;
pixel_rgb_data[y * width + x * 3 + 2] = blue;
I should have used this:
pixel_rgb_data[3 * (y * width + x) ] = red;
pixel_rgb_data[3 * (y * width + x) + 1] = green;
pixel_rgb_data[3 * (y * width + x) + 2] = blue;
Somehow I was multiplying only the the horizontal displacement within the matrix, not the vertical displacement. The moment I changed it, it worked perfectly.

how to create bitmap in C and compile with gcc

i decided to learn C, and i try to follow this tutorial http://ricardolovelace.com/creating-bitmap-images-with-c-on-windows.html
but when i try to compile my code with gcc as this >gcc -Wall testc o app
he doesn't know type_rgb, can i define this type and how? and where in my code ?
#include <stdio.h>
struct rgb_data {
float r, g, b;
};
void save_bitmap( const char *file_name, int width, int height, int dpi, type_rgb *pixel_data);
/*
next steps of the tutorial
*/
rgb_data *pixels = new rgb_data[width * height];
for( int x = 0; x < width; x++)
{
for(int y = 0; y < height; y++)
int a = y * width +x;
{
if ((x > 50 && x < 350) && (y > y && y < 350))
{
pixels[a].r = 255;
pixels[a].g = 255;
pixels[a].b = 0;
}else{
pixels[a].r = 55;
pixels[a].g = 55;
pixels[a].b = 55;
}
}
}
save_bitmap("black_border.bmp", width, height, dpi, pixels);
Bitmap file format is rather complicated. This is not the best way to learn C. It's better to start with something much simpler.
Having said that, the bitmap format starts with a bitmap header BITMAPFILEHEADER structure which is 14 bytes long, followed by BITMAPINFOHEADER structure 40 bytes long. These structures are defined in "Windows.h"
You have to write in various information in these structures and write them to file before writing the actual pixels.
You can have 1, 4, 8, 16, 24, and 32-bit bitmap. This is an example to read a 32-bit bitmap. This code assumes sizeof(short) is 2, sizeof(int) is 4.
int main()
{
int row, column;
int width = 100;
int height = 100;
int size = width * height * 4; //for 32-bit bitmap only
char header[54] = { 0 };
strcpy(header, "BM");
memset(&header[2], (int)(54 + size), 1);
memset(&header[10], (int)54, 1);//always 54
memset(&header[14], (int)40, 1);//always 40
memset(&header[18], (int)width, 1);
memset(&header[22], (int)height, 1);
memset(&header[26], (short)1, 1);
memset(&header[28], (short)32, 1);//32bit
memset(&header[34], (int)size, 1);//pixel size
unsigned char *pixels = malloc(size);
for(row = height - 1; row >= 0; row--) {
for(column = 0; column < width; column++) {
int p = (row * width + column) * 4;
pixels[p + 0] = 64; //blue
pixels[p + 1] = 128;//green
pixels[p + 2] = 192;//red
}
}
FILE *fout = fopen("32bit.bmp", "wb");
fwrite(header, 1, 54, fout);
fwrite(pixels, 1, size, fout);
free(pixels);
fclose(fout);
return 0;
}
Note the first pixel is blue, followed by green and read. The last pixel is not used in 32-bit bitmap. Also the height goes from bottom to top. This is another odd feature of bitmap. 24-bit bitmaps are more complicated because they need padding. 8-bit and lower will need an additional palette.
struct rgb_data {
float r, g, b;
};
float is not the right type for pixels. Each color goes from 0 to 255. This fits in unsigned char. You need instead
struct rgb_data {
unsigned r, g, b, alpha;
};
The alpha is the extra byte for 32-bit bitmap (which we won't use). Notice the size of this structure is 4. You can allocate this as
struct rgb_data *rgb = malloc(size);
Now you can access the pixels as follows:
int p = (row * width + column);
rgb[p].r = 255;
rgb[p].g = 0;
rgb[p].b = 0;
...
fwrite(rgb, 4, width * height, fout);

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.

BMP color replacer: Algorithm for padding doesn't work

I am building a function that replaces a color in a BMP image with another target color.
It works as long as I don't attempt to replace a color in image that requires padding.
However, I am almost certain that the way I account padding is correct.. So for me this is a complete mystery.
330 x 250 (248 054 bytes) Hi-color 24bit bitmap
This is the function:
union
{
unsigned long ulColor;
unsigned char byteColor[4];
} oldColor;
union
{
unsigned long ulColor;
unsigned char byteColor[4];
} newColor;
typedef unsigned char BYTE;
typedef unsigned short int WORD;
typedef unsigned long int DWORD;
typedef unsigned long long int DDWORD;
DDWORD
bitfox_color_replace_data
(BYTE *buff, BYTE old_r, BYTE old_g, BYTE old_b, BYTE new_r, BYTE new_g, BYTE new_b)
{
#define OFFSET_OF_SIZE 0x2
#define OFFSET_OF_PIXELS 0xA
#define OFFSET_OF_WIDTH 0x12
#define OFFSET_OF_HEIGHT 0x16
DWORD* buffSize = (DWORD*)&buff[OFFSET_OF_SIZE];
DWORD* buffPixels = (DWORD*)&buff[OFFSET_OF_PIXELS];
DWORD* buffWidth = (DWORD*)&buff[OFFSET_OF_WIDTH];
DWORD buffHeight = 0;
BYTE pad = 0;
DDWORD pixels_replaced = 0;
DDWORD i;
oldColor.byteColor[0] = old_b; newColor.byteColor[0] = new_b;
oldColor.byteColor[1] = old_g; newColor.byteColor[1] = new_g;
oldColor.byteColor[2] = old_r; newColor.byteColor[2] = new_r;
for(i = (*buffPixels); i < (*buffSize); i += 3)
{
if( i == ((*buffPixels) + (((*buffWidth) * 3) + pad) * (buffHeight + 1)) )
{
pad = ((*buffWidth) % 4);
buffHeight++;
i += pad;
}
if(!memcmp(buff + i, oldColor.byteColor, 3))
{
memcpy(buff + i, newColor.byteColor, 3);
pixels_replaced++;
}
}
return pixels_replaced;
}
What am I possibly doing wrong ?
When dealing with bitmaps, there are three parameters that you need to know: height, width, and stride. Width and height are obvious. The stride is the number of bytes per line including padding.
Here's one way to compute the stride. Note that the stride must be a multiple of 4.
int stride = ((width * 3) + 3) >> 2;
stride *= 4;
The first line computes the minimum number of 4-byte values that can hold a line. The second line converts the stride to a byte count.
The code at the end of this post demonstrates how to use the stride. The code assumes that the input file contains a 24bpp RGB image. The file headers have already been read, leaving just the pixel data, which is read into a buffer. The code writes to an output file, assuming the output image is the same size as the input image and any headers have already been written.
The important lines are
size = height * stride; // total number of bytes in the image, including padding
offset = (y * stride) + (x * 3); // 'y * stride' is the offset to the beginning of a line
// 'x * 3' computes the byte offset of a particular pixel
for ( x=width*3; x<stride; x++ ) // outputs the padding bytes, if needed,
fputc( 0, fpout );
unsigned char *buffer = NULL;
int height = bmpinfo.biHeight;
int width = bmpinfo.biWidth;
// stride = (width * 3), rounded up to a multiple of 4
int stride = ((width * 3) + 3) >> 2;
stride *= 4;
// size of the pixel data, including padding
size_t size = height * stride;
// allocate memory for the pixel data
if ( (buffer = malloc( size )) == NULL )
error( "Insufficient memory" );
// read the pixel data from the file
if ( fread( buffer, 1, size, fpin ) != size )
error( "Unable to read image data" );
// process pixels by row and column
for ( y = 0; y < height; y++ )
{
for ( x = 0; x < width; x++ )
{
// get the RGB values from the buffer
offset = (y * stride) + (x * 3);
blue = buffer[offset];
green = buffer[offset+1];
red = buffer[offset+2];
// mess around with the RGB value here
// write the new RGB values to the file
fputc( (int)blue , fpout );
fputc( (int)green, fpout );
fputc( (int)red , fpout );
}
// write the padding bytes to the file
for ( x = width*3; x < stride; x++ )
fputc( 0, fpout );
}

Pixman compositing vs. alpha blending

Below is a small test program (works on little endian machines).
As is, the result is already strange to me :
in: r=20 g=20 b=80 a=FF (#202080FF, ok!)
out: r=90 g=90 b=C0 a=FF (#9090C0FF, strange...)
Where as I expected the fill color #FFFFFFFF x the mask 0x80 = #FFFFFF80 and so an output of #9090FFFF...
Now, if I set the fill color to #FFFFFF80 by changing "cfill.alpha = uint16_t(0x80) << 8;" , the result seems really wrong :
in: r=20 g=20 b=80 a=FF
out: r=98 g=98 b=E0 a=FF
I would expect fill x mask => #FFFFFF40 and thus an output of: #606060C0FF.
I especially do not understand how a lower alpha input color can end up in a lighter output on the target image.
What I am doing wrong here ?
Is there another PIXMAP_OP_xxx that would work as I expect ?
Thanks.
#include <stdlib.h>
#include <stdio.h>
#include "pixman.h"
union C {
uint32_t value;
struct RGBA8888 {
uint8_t a;
uint8_t b;
uint8_t g;
uint8_t r;
} rgba;
};
int main()
{
// create target image full with r=0x20 g=0x20 b=0x80 a=0xFF
size_t w = 100; // multiple of 4 for alignment
size_t h = 100;
C *target = (C*)malloc(w * h * sizeof(C));
for(size_t i = 0; i < w * h; ++i)
target[i].value = 0x202080FF;
printf("in: r=%02X g=%02X b=%02X a=%02X\n",
target[0].rgba.r, target[0].rgba.g, target[0].rgba.b, target[0].rgba.a);
// connect target to pixman image
pixman_image_t *ptarget = pixman_image_create_bits(PIXMAN_r8g8b8a8, w, h, (uint32_t*)target, w * sizeof(uint32_t));
// create fill
pixman_color_t cfill;
cfill.red = uint16_t(0xFF) << 8;
cfill.green = uint16_t(0xFF) << 8;
cfill.blue = uint16_t(0xFF) << 8;
cfill.alpha = uint16_t(0xFF) << 8;
pixman_image_t *pfill = pixman_image_create_solid_fill(&cfill);
// create mask with a=0x80
uint8_t *mask = (uint8_t*)malloc(w * h);
for(size_t i = 0; i < w * h; ++i)
mask[i] = 0x80;
pixman_image_t *pmask = pixman_image_create_bits(PIXMAN_a8, w, h, (uint32_t*)mask, w);
// do compositing
pixman_image_composite(
PIXMAN_OP_OVER,
pfill, pmask, ptarget,
// src_x, src_y
0, 0,
// mask_x, mask_y
0, 0,
// dest_x, dest_y, width, height
0, 0, w, h);
// display one pixel of target
printf("out: r=%02X g=%02X b=%02X a=%02X\n",
target[0].rgba.r, target[0].rgba.g, target[0].rgba.b, target[0].rgba.a);
}
I turns out that Pixman works with premultiplied alpha !
So the white with alpha should be #80808080 and subsequently #40404040 and not #FFFFFF80 and #FFFFFF40.
Hope it helps somebody else ;)

Resources