jpeg to bmp with 300x300 resolution outputs distorted grayscale image [duplicate] - c

This question already has answers here:
not able to save an image file using c?
(2 answers)
Closed 7 years ago.
I am using libjpeg on a Ubuntu 14.04 environment to decompress my JPEG image and write it to a BMP file. However, if I have a colored JPEG image with 300x300 resolution(2550x4206 pixels), the output BMP file is in grayscale and image seems dull. Other JPEG images with resolution 200x200, 400x400 and 600x600 will output the correct BMP image. Need your advice on this. Please help. Thank you.
link to JPEG image: https://drive.google.com/file/d/0B3ob0t07z5xEdmtVVWRicUQ5SGs/view?usp=sharing
Preview for BMP is not available. Download and view the image in Ubuntu or a Linux environment system. I have no idea why it doesn't display the image in Google or even in Windows.
link to output BMP image: https://drive.google.com/file/d/0B3ob0t07z5xEZTMycVRVX3Vscnc/view?usp=sharing
Code snippet writing decompressed JPEG image to BMP file:
struct jpeg_decompress_struct cinfo;
unsigned int bytesPerRow = cinfo.output_width * cinfo.num_components;
unsigned int colColor;
FILE *bmpFile = NULL;
while (cinfo.output_scanline < cinfo.image_height) {
JSAMPROW row_pointer[1];
row_pointer[0] = raw_image
+ cinfo.output_scanline * bytesPerRow;
jpeg_read_scanlines(&cinfo, row_pointer, 1);
for (colColor = 0; colColor < cinfo.image_width; colColor++) {
/* BMP scanlines should be aligned at 4-byte boundary */
}
/* write each row to bmp file */
fwrite(row_pointer[0], 1, bytesPerRow, bmpFile);
}
BMP FILE: (bit fields are set byte by byte)
typedef struct {
unsigned int img_bits_per_pixel;
unsigned int img_scansize;
unsigned int img_width;
unsigned int img_height;
} Image_Information;
Image_Information *image_info;
image_info = (Image_Information *) malloc(sizeof(Image_Information));
image_info->img_height = cinfo.image_height;
image_info->img_width = cinfo.image_width;
image_info->img_scansize = ((image_info->img_width * 24 + 31) & ~31) / 8;
BITMAPFILEHEADER:
bfType = "BM";
bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + image_info->img_scansize * image_info->img_height;
bfReserved1 = 0;
bfReserved2 = 0;
bfOffBits = 54;
BITMAPINFOHEADER:
biSize = sizeof(BITMAPINFOHEADER);
biWidth = image_info->img_width;
biHeight = image_info->img_heigh;
biPlanes = 1;
biBitCount = 24;
biCompression = 0;
biSizeImage = 0;
biXPelsPerMeter = 0;
biYPelsPerMeter = 0;
biClrUsed = 0;
biClrImportant = 0;

I actually got the answer from #user3629249. Thank you.
I just added 2 bytes of padding at the end of each row only if resolution is 300x300
padding = 0;
fwrite(&padding, 2, 1, bmpFile);

Related

libjpeg reads a scanline 1/8th the original width

I am trying to just read every pixel in a jpeg image. When I read a scanline, it appears that the image has been squashed to one eighth the size of the original image. The scanline is the correct width but the remaining 7/8'ths of the scanline are not filled (0, 0, 0).
I cant simply use each pixel 8 times since a large amount of information was lost.
I don't use any decompression parameters and am perfectly happy with the defaults. The Libjpeg version I am using is "libjpeg/9d" from the https://conan.io package center.
How can I get scanlines in the correct aspect ratio?
FILE* file_p = openfile(fileaddress);
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr err; //the error handler
cinfo.err = jpeg_std_error( &err );
jpeg_create_decompress(&cinfo);
jpeg_stdio_src(&cinfo, file_p);
int result = jpeg_read_header(&cinfo, TRUE);
bool startedfine = jpeg_start_decompress(&cinfo);
int row_stride = cinfo.output_width * cinfo.output_components;
image output = create_image(cinfo.output_width, cinfo.output_height);
JSAMPARRAY buffer = calloc(1, sizeof(JSAMPROW*));
buffer[0] = calloc(1, sizeof(JSAMPROW) * row_stride);
while (cinfo.output_scanline < cinfo.output_height) {
int current_y = cinfo.output_scanline;
jpeg_read_scanlines(&cinfo, buffer, 1);
JSAMPROW row = buffer[0];
for(int row_i = 0; row_i < row_stride; row_i += 3) {
int r = (int)row[row_i];
int g = (int)row[row_i + 1];
int b = (int)row[row_i + 2];
int actual_x = row_i / 3;
output.pixels_array_2d[actual_x][current_y].r = r;
output.pixels_array_2d[actual_x][current_y].g = b;
output.pixels_array_2d[actual_x][current_y].b = g;
}
}
free(buffer[0]);
free(buffer);
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
fclose(file_p);
As a side note, you may notice I assign the blue value to the green value when copying the pixels. This is because without it, my test image is the wrong colour and I don't know why.

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.

how to align scanline at 4 byte boundary

I am using libjpeg to decompress my JPEG image and write it to a BMP file. Assuming the image width is being set to 2550 pixels at 24 bits (3 bytes) per pixel, the resulting row width will not be a multiple of 4. How do I align each row at 4 byte boundary?
struct jpeg_decompress_struct cinfo;
unsigned int bytesPerRow = cinfo.output_width * cinfo.num_components;
unsigned int colColor;
FILE *bmpFile = NULL;
while (cinfo.output_scanline < cinfo.image_height) {
JSAMPROW row_pointer[1];
row_pointer[0] = raw_image
+ cinfo.output_scanline * bytesPerRow;
jpeg_read_scanlines(&cinfo, row_pointer, 1);
for (colColor = 0; colColor < cinfo.image_width; colColor++) {
/* BMP scanlines should be aligned at 4-byte boundary */
}
/* write each row to bmp file */
fwrite(row_pointer[0], 1, bytesPerRow, bmpFile);
}
bytesPerRow = 4 * ((cinfo.output_width * cinfo.num_components + 3) /
4); – Hans Passant
The formula is correct as far as rounding up to a multiple of 4 is concerned, but incorrect in using the multiplicand cinfo.num_components; according to USING THE IJG JPEG LIBRARY:
You will need output_width * output_components JSAMPLEs per scanline
in your output buffer…
The output arrays are required to be output_width * output_components
JSAMPLEs wide.
So, it's
unsigned int bytesPerRow = (cinfo.output_width * cinfo.output_components + 3) / 4 * 4;

Create Bitmap in C

I'm trying to create a Bitmap that shows the flightpath of a bullet.
int drawBitmap(int height, int width, Point* curve, char* bitmap_name)
{
int image_size = width * height * 3;
int padding = width - (width % 4);
struct _BitmapFileheader_ BMFH;
struct _BitmapInfoHeader_ BMIH;
BMFH.type_[1] = 'B';
BMFH.type_[2] = 'M';
BMFH.file_size_ = 54 + height * padding;
BMFH.reserved_1_ = 0;
BMFH.reserved_2_ = 0;
BMFH.offset_ = 54;
BMIH.header_size_ = 40;
BMIH.width_ = width;
BMIH.height_ = height;
BMIH.colour_planes_ = 1;
BMIH.bit_per_pixel_ = 24;
BMIH.compression_ = 0;
BMIH.image_size_ = image_size + height * padding;
BMIH.x_pixels_per_meter_ = 2835;
BMIH.y_pixels_per_meter_ = 2835;
BMIH.colours_used_ = 0;
BMIH.important_colours_ = 0;
writeBitmap(BMFH, BMIH, curve, bitmap_name);
}
void* writeBitmap(struct _BitmapFileheader_ file_header,
struct _BitmapInfoHeader_ file_infoheader, void* pixel_data, char* file_name)
{
FILE* image = fopen(file_name, "w");
fwrite((void*)&file_header, 1, sizeof(file_header), image);
fwrite((void*)&file_infoheader, 1, sizeof(file_infoheader), image);
fwrite((void*)pixel_data, 1, sizeof(pixel_data), image);
fclose(image);
return 0;
}
Curve is the return value from the function which calculates the path. It points at an array of Points, which is a struct of x and y coordinates.
I don't really know how to "put" the data into the Bitmap correctly.
I just started programming C recently and I'm quite lost at the moment.
You already know about taking up any slack space in each pixel row, but I see a problem in your calculation. Each pixel row must have length % 4 == 0. So with 3 bytes per pixel (24-bit)
length = ((3 * width) + 3) & -4; // -4 as I don't know the int size, say 0xFFFFFFFC
Look up the structure of a bitmap - perhaps you already have. Declare (or allocate) an image byte array size height * length and fill it with zeros. Parse the bullet trajectory and find the range of x and y coordinates. Scale these to the bitmap size width and height. Now parse the bullet trajectory again, scaling the coordinates to xx and yy, and write three 0xFF bytes (you specified 24-bit colour) into the correct place in the array for each bullet position.
if (xx >= 0 && xx < width && yy >= 0 && yy < height) {
index = yy * length + xx * 3;
bitmap [index] = 0xFF;
bitmap [index + 1] = 0xFF;
bitmap [index + 2] = 0xFF;
}
Finally save the bitmap info, header and image data to file. When that works, you can refine your use of colour.

What is the simplest RGB image format?

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".

Resources