D. Biegler D. Biegler - 3 months ago 19
C++ Question

Read in JPEG, resize it and save it to disk

What I want:



Resize an existing jpeg into a new, smaller one.

Example: before_3000x2000.jpeg →→→ after_1024x768.jpeg

I think I'll need pseudo-functions like these:


foo( before_file_path, after_file_path, new_width, new_height )


which makes use of


decompress_jpeg( before_file_path )

resize( decompressed_data, new_width, new_height )

compress_jpeg( resized_data )

save_jpeg( compressed_data, after_file_path )


Please correct my approach if I misunderstood something.

What I assume:




  • Since the resolutions will vary, first you have to decompress the existing jpeg so you can properly work with the pixel data.

  • Second, you resize the pixeldata to a smaller buffer with some function foo().

  • Third, you compress said buffer and save it to disk.



What I tried (and failed):



For starters, I tried playing around with the


write_JPEG_file (char * filename, int quality)


function provided in example.c of jpeglib-9b. I just want to write out a test-picture in whatever resolution I specify with a single colour. But I run into a problem. I think I'm writing out random memory and I dont know why. (here is a picture: img_100x100) Notice how at the top there is white colour up to a certain point. This part changes accordingly to what I assign in the first loop. But after a certain amount it changes to random noise. Why?

What I changed:



I defined
image_width, image_height, image_buffer


I replaced all
fopen(...)
with
fopen_s(...)
.

I hardcoded the quality to 100 in
jpeg_set_quality(...)


Code:



#include <iostream>
#include "jpeg-9b\jpeglib.h"
#include "jpeg-9b\jerror.h"

int TEST_WRITE_JPEG(char* file_name)
{
// DEFINE A PIC HERE
const int image_width = 100; /* Number of columns in image */
const int image_height = 100; /* Number of rows in image */
JSAMPLE* image_buffer = new JSAMPLE[image_width * image_height](); /* Points to large array of R,G,B-order data */
for (int i = 0; i < image_width*image_height; i++)
{
image_buffer[i] = 255;
}

/* Step 1: allocate and initialize JPEG compression object */
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
FILE * outfile;
JSAMPROW row_pointer[1];
int row_stride;

cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);

/* Step 2: specify data destination (eg, a file) */
if (fopen_s(&outfile, file_name, "wb") != NULL)
{
fprintf(stderr, "can't open %s\n", file_name);
exit(1);
}
jpeg_stdio_dest(&cinfo, outfile);

/* Step 3: set parameters for compression */
cinfo.image_width = image_width; /* image width and height, in pixels */
cinfo.image_height = image_height;
cinfo.input_components = 3; /* # of color components per pixel */
cinfo.in_color_space = JCS_RGB; /* colorspace of input image */

jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, 100, TRUE);

/* Step 4: Start compressor */
jpeg_start_compress(&cinfo, TRUE);

/* Step 5: while (scan lines remain to be written) */
row_stride = image_width * 3;

while (cinfo.next_scanline < cinfo.image_height)
{
row_pointer[0] = (JSAMPROW)&image_buffer[cinfo.next_scanline * row_stride];
(void)jpeg_write_scanlines(&cinfo, row_pointer, 1);
}

/* Step 6: Finish compression */
jpeg_finish_compress(&cinfo);
/* After finish_compress, we can close the output file. */
fclose(outfile);

/* Step 7: release JPEG compression object */
/* This is an important step since it will release a good deal of memory. */
jpeg_destroy_compress(&cinfo);

return 0;
}


Clear questions:



Why do I write out this picture and not a plain white one?

Why does the program crash if
image_buffer
contains more than 128x128 unsigned chars?

Answer

3 bytes are used for each pixels, so you have to allocate 3 * image_width * image_height elements while you allocated only image_width * image_height elements. Allocate and initialize sufficient elements.