2 * Copyright (c) 2001-2003, David Janssens
3 * Copyright (c) 2002-2003, Yannick Verschueren
4 * Copyright (c) 2003-2005, Francois Devaux and Antonin Descampe
5 * Copyright (c) 2005, Herve Drolon, FreeImage Team
6 * Copyright (c) 2002-2005, Communications and remote sensing Laboratory, Universite catholique de Louvain, Belgium
7 * Copyright (c) 2006, Mónica Díez García, Image Processing Laboratory, University of Valladolid, Spain
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
36 #include "opj_config.h"
38 #include "opj_getopt.h"
44 #define stricmp strcasecmp
45 #define strnicmp strncasecmp
48 /* ----------------------------------------------------------------------- */
49 static double calc_PSNR(opj_volume_t *original, opj_volume_t *decoded)
51 int max, i, k, compno = 0, size;
52 double sum, total = 0;
55 max = (original->comps[compno].prec <= 8) ? 255 : (1 <<
56 original->comps[compno].prec) - 1;
58 size = (original->x1 - original->x0) * (original->y1 - original->y0) *
59 (original->z1 - original->z0);
61 for (compno = 0; compno < original->numcomps; compno++) {
62 for (sum = 0, i = 0; i < size; ++i) {
63 if ((decoded->comps[compno].data[i] < 0) ||
64 (decoded->comps[compno].data[i] > max)) {
65 fprintf(stdout, "[WARNING] Data out of range during PSNR computing...\n");
67 sum += (original->comps[compno].data[i] - decoded->comps[compno].data[i]) *
68 (original->comps[compno].data[i] - decoded->comps[compno].data[i]);
73 total = ((sum == 0.0) ? 0.0 : 10 * log10(max * max / sum));
75 size = (original->x1 - original->x0) * (original->y1 - original->y0);
77 for (k = 0; k < original->z1 - original->z0; k++) {
78 int offset = k * size;
79 for (sum = 0, compno = 0; compno < original->numcomps; compno++) {
80 for (i = 0; i < size; ++i) {
81 if ((decoded->comps[compno].data[i + offset] < 0) ||
82 (decoded->comps[compno].data[i + offset] > max)) {
83 fprintf(stdout, "[WARNING] Data out of range during PSNR computing...\n");
85 sum += (original->comps[compno].data[i + offset] - decoded->comps[compno].data[i
86 + offset]) * (original->comps[compno].data[i + offset] -
87 decoded->comps[compno].data[i + offset]);
92 total = total + ((sum == 0.0) ? 0.0 : 10 * log10(max * max / sum));
96 if (total == 0) { /* perfect reconstruction, PSNR should return infinity */
101 /*return 20 * log10((max - 1) / sqrt(sum));*/
104 static double calc_SSIM(opj_volume_t *original, opj_volume_t *decoded)
106 int max, i, compno = 0, size, sizeM;
108 double mux = 0.0, muy = 0.0, sigmax = 0.0, sigmay = 0.0,
109 sigmaxy = 0.0/*, structx = 0.0, structy = 0.0*/;
110 double lcomp, ccomp, scomp;
113 max = (original->comps[compno].prec <= 8) ? 255 : (1 <<
114 original->comps[compno].prec) - 1;
115 size = (original->x1 - original->x0) * (original->y1 - original->y0) *
116 (original->z1 - original->z0);
120 /* sizeM = size / (original->z1 - original->z0);*/
123 for (sum = 0, i = 0; i < sizeM; ++i) {
124 /* First, the luminance of each signal is compared.*/
125 mux += original->comps[compno].data[i];
126 muy += decoded->comps[compno].data[i];
131 /*We use the standard deviation (the square root of variance) as an estimate of the signal contrast.*/
132 for (sum = 0, i = 0; i < sizeM; ++i) {
133 /* First, the luminance of each signal is compared.*/
134 sigmax += (original->comps[compno].data[i] - mux) *
135 (original->comps[compno].data[i] - mux);
136 sigmay += (decoded->comps[compno].data[i] - muy) *
137 (decoded->comps[compno].data[i] - muy);
138 sigmaxy += (original->comps[compno].data[i] - mux) *
139 (decoded->comps[compno].data[i] - muy);
143 sigmaxy /= sizeM - 1;
145 sigmax = sqrt(sigmax);
146 sigmay = sqrt(sigmay);
147 sigmaxy = sqrt(sigmaxy);
149 /*Third, the signal is normalized (divided) by its own standard deviation, */
150 /*so that the two signals being compared have unit standard deviation.*/
152 /*Luminance comparison*/
153 C1 = (0.01 * max) * (0.01 * max);
154 lcomp = ((2 * mux * muy) + C1) / ((mux * mux) + (muy * mux) + C1);
155 /*Constrast comparison*/
156 C2 = (0.03 * max) * (0.03 * max);
157 ccomp = ((2 * sigmax * sigmay) + C2) / ((sigmax * sigmax) +
158 (sigmay * sigmay) + C2);
159 /*Structure comparison*/
161 scomp = (sigmaxy + C3) / (sigmax * sigmay + C3);
162 /*Similarity measure*/
164 sum = lcomp * ccomp * scomp;
168 void decode_help_display()
170 fprintf(stdout, "HELP\n----\n\n");
171 fprintf(stdout, "- the -h option displays this help information on screen\n\n");
173 fprintf(stdout, "List of parameters for the JPEG 2000 encoder:\n");
174 fprintf(stdout, "\n");
175 fprintf(stdout, " Required arguments \n");
176 fprintf(stdout, " ---------------------------- \n");
177 fprintf(stdout, " -i <compressed file> ( *.jp3d, *.j3d )\n");
179 " Currently accepts J3D-files. The file type is identified based on its suffix.\n");
180 fprintf(stdout, " -o <decompressed file> ( *.pgx, *.bin )\n");
182 " Currently accepts PGX-files and BIN-files. Binary data is written to the file (not ascii). \n");
184 " If a PGX filename is given, there will be as many output files as slices; \n");
186 " an indice starting from 0 will then be appended to the output filename,\n");
187 fprintf(stdout, " just before the \"pgx\" extension.\n");
188 fprintf(stdout, " -m <characteristics file> ( *.img ) \n");
190 " Required only for BIN-files. Ascii data of volume characteristics is written. \n");
191 fprintf(stdout, "\n");
192 fprintf(stdout, " Optional \n");
193 fprintf(stdout, " ---------------------------- \n");
194 fprintf(stdout, " -h \n ");
195 fprintf(stdout, " Display the help information\n");
196 fprintf(stdout, " -r <RFx,RFy,RFz>\n");
198 " Set the number of highest resolution levels to be discarded on each dimension. \n");
200 " The volume resolution is effectively divided by 2 to the power of the\n");
202 " number of discarded levels. The reduce factor is limited by the\n");
204 " smallest total number of decomposition levels among tiles.\n");
205 fprintf(stdout, " -l <number of quality layers to decode>\n");
207 " Set the maximum number of quality layers to decode. If there are\n");
209 " less quality layers than the specified number, all the quality layers\n");
210 fprintf(stdout, " are decoded. \n");
211 fprintf(stdout, " -O original-file \n");
213 " This option offers the possibility to compute some quality results \n");
215 " for the decompressed volume, like the PSNR value achieved or the global SSIM value. \n");
217 " Needs the original file in order to compare with the new one.\n");
219 " NOTE: Only valid when -r option is 0,0,0 (both original and decompressed volumes have same resolutions) \n");
221 " NOTE: If original file is .BIN file, the volume characteristics file shall be defined with the -m option. \n");
222 fprintf(stdout, " (i.e. -O original-BIN-file -m original-IMG-file) \n");
223 fprintf(stdout, " -BE \n");
225 " Define that the recovered volume data will be saved with big endian byte order.\n");
226 fprintf(stdout, " By default, little endian byte order is used.\n");
227 fprintf(stdout, "\n");
230 /* -------------------------------------------------------------------------- */
232 int get_file_format(char *filename)
235 static const char *extension[] = {"pgx", "bin", "j3d", "jp3d", "j2k", "img"};
236 static const int format[] = { PGX_DFMT, BIN_DFMT, J3D_CFMT, J3D_CFMT, J2K_CFMT, IMG_DFMT};
237 char * ext = strrchr(filename, '.');
240 for (i = 0; i < sizeof(format) / sizeof(format[0]); i++) {
241 if (strnicmp(ext, extension[i], 3) == 0) {
250 /* -------------------------------------------------------------------------- */
252 int parse_cmdline_decoder(int argc, char **argv, opj_dparameters_t *parameters)
254 /* parse the command line */
257 int c = opj_getopt(argc, argv, "i:o:O:r:l:B:m:h");
262 case 'i': { /* input file */
263 char *infile = opj_optarg;
264 parameters->decod_format = get_file_format(infile);
265 switch (parameters->decod_format) {
270 fprintf(stdout, "[ERROR] Unknown format for infile %s [only *.j3d]!! \n",
275 strncpy(parameters->infile, infile, MAX_PATH);
276 fprintf(stdout, "[INFO] Infile: %s \n", parameters->infile);
281 case 'm': { /* img file */
282 char *imgfile = opj_optarg;
283 int imgformat = get_file_format(imgfile);
289 "[ERROR] Unrecognized format for imgfile : %s [accept only *.img] !!\n\n",
294 strncpy(parameters->imgfile, imgfile, MAX_PATH);
295 fprintf(stdout, "[INFO] Imgfile: %s Format: %d\n", parameters->imgfile,
300 /* ----------------------------------------------------- */
302 case 'o': { /* output file */
303 char *outfile = opj_optarg;
304 parameters->cod_format = get_file_format(outfile);
305 switch (parameters->cod_format) {
311 "[ERROR] Unrecognized format for outfile : %s [accept only *.pgx or *.bin] !!\n\n",
316 strncpy(parameters->outfile, outfile, MAX_PATH);
317 fprintf(stdout, "[INFO] Outfile: %s \n", parameters->outfile);
322 /* ----------------------------------------------------- */
324 case 'O': { /* Original image for PSNR computing */
325 char *original = opj_optarg;
326 parameters->orig_format = get_file_format(original);
327 switch (parameters->orig_format) {
333 "[ERROR] Unrecognized format for original file : %s [accept only *.pgx or *.bin] !!\n\n",
338 strncpy(parameters->original, original, MAX_PATH);
339 fprintf(stdout, "[INFO] Original file: %s \n", parameters->original);
343 /* ----------------------------------------------------- */
345 case 'r': { /* reduce option */
346 /*sscanf(opj_optarg, "%d, %d, %d", ¶meters->cp_reduce[0], ¶meters->cp_reduce[1], ¶meters->cp_reduce[2]);*/
348 aux = sscanf(opj_optarg, "%d,%d,%d", ¶meters->cp_reduce[0],
349 ¶meters->cp_reduce[1], ¶meters->cp_reduce[2]);
351 parameters->cp_reduce[2] = 0;
352 } else if (aux == 1) {
353 parameters->cp_reduce[1] = parameters->cp_reduce[0];
354 parameters->cp_reduce[2] = 0;
355 } else if (aux == 0) {
356 parameters->cp_reduce[0] = 0;
357 parameters->cp_reduce[1] = 0;
358 parameters->cp_reduce[2] = 0;
363 /* ----------------------------------------------------- */
365 case 'l': { /* layering option */
366 sscanf(opj_optarg, "%d", ¶meters->cp_layer);
370 /* ----------------------------------------------------- */
372 case 'B': { /* BIGENDIAN vs. LITTLEENDIAN */
373 parameters->bigendian = 1;
377 /* ----------------------------------------------------- */
379 case 'L': { /* BIGENDIAN vs. LITTLEENDIAN */
380 parameters->decod_format = LSE_CFMT;
384 /* ----------------------------------------------------- */
386 case 'h': { /* display an help description */
387 decode_help_display();
392 /* ----------------------------------------------------- */
395 fprintf(stdout, "[WARNING] This option is not valid \"-%c %s\"\n", c,
401 /* check for possible errors */
403 if ((parameters->infile[0] == 0) || (parameters->outfile[0] == 0)) {
405 "[ERROR] At least one required argument is missing\n Check jp3d_to_volume -help for usage information\n");
412 /* -------------------------------------------------------------------------- */
415 sample error callback expecting a FILE* client object
417 void error_callback(const char *msg, void *client_data)
419 FILE *stream = (FILE*)client_data;
420 fprintf(stream, "[ERROR] %s", msg);
423 sample warning callback expecting a FILE* client object
425 void warning_callback(const char *msg, void *client_data)
427 FILE *stream = (FILE*)client_data;
428 fprintf(stream, "[WARNING] %s", msg);
431 sample debug callback expecting no client object
433 void info_callback(const char *msg, void *client_data)
435 fprintf(stdout, "[INFO] %s", msg);
438 /* -------------------------------------------------------------------------- */
440 int main(int argc, char **argv)
443 opj_dparameters_t parameters; /* decompression parameters */
444 opj_event_mgr_t event_mgr; /* event manager */
445 opj_volume_t *volume = NULL;
447 opj_volume_t *original = NULL;
448 opj_cparameters_t cparameters; /* original parameters */
451 unsigned char *src = NULL;
456 opj_dinfo_t* dinfo = NULL; /* handle to a decompressor */
457 opj_cio_t *cio = NULL;
459 /* configure the event callbacks (not required) */
460 memset(&event_mgr, 0, sizeof(opj_event_mgr_t));
461 event_mgr.error_handler = error_callback;
462 event_mgr.warning_handler = warning_callback;
463 event_mgr.info_handler = info_callback;
465 /* set decoding parameters to default values */
466 opj_set_default_decoder_parameters(¶meters);
468 /* parse input and get user decoding parameters */
469 strcpy(parameters.original, "NULL");
470 strcpy(parameters.imgfile, "NULL");
471 if (parse_cmdline_decoder(argc, argv, ¶meters) == 1) {
475 /* read the input file and put it in memory */
476 /* ---------------------------------------- */
477 fprintf(stdout, "[INFO] Loading %s file \n",
478 parameters.decod_format == J3D_CFMT ? ".jp3d" : ".j2k");
479 fsrc = fopen(parameters.infile, "rb");
481 fprintf(stdout, "[ERROR] Failed to open %s for reading\n", parameters.infile);
484 fseek(fsrc, 0, SEEK_END);
485 file_length = ftell(fsrc);
486 fseek(fsrc, 0, SEEK_SET);
487 src = (unsigned char *) malloc(file_length);
488 fread(src, 1, file_length, fsrc);
491 /* decode the code-stream */
492 /* ---------------------- */
493 if (parameters.decod_format == J3D_CFMT ||
494 parameters.decod_format == J2K_CFMT) {
495 /* get a JP3D or J2K decoder handle */
496 if (parameters.decod_format == J3D_CFMT) {
497 dinfo = opj_create_decompress(CODEC_J3D);
498 } else if (parameters.decod_format == J2K_CFMT) {
499 dinfo = opj_create_decompress(CODEC_J2K);
502 /* catch events using our callbacks and give a local context */
503 opj_set_event_mgr((opj_common_ptr)dinfo, &event_mgr, stderr);
505 /* setup the decoder decoding parameters using user parameters */
506 opj_setup_decoder(dinfo, ¶meters);
508 /* open a byte stream */
509 cio = opj_cio_open((opj_common_ptr)dinfo, src, file_length);
511 /* decode the stream and fill the volume structure */
512 volume = opj_decode(dinfo, cio);
514 fprintf(stdout, "[ERROR] jp3d_to_volume: failed to decode volume!\n");
515 opj_destroy_decompress(dinfo);
520 /* close the byte stream */
524 /* free the memory containing the code-stream */
528 /* create output volume */
529 /* ------------------- */
531 switch (parameters.cod_format) {
532 case PGX_DFMT: /* PGX */
533 decodeok = volumetopgx(volume, parameters.outfile);
535 fprintf(stdout, "[ERROR] Unable to write decoded volume into pgx files\n");
539 case BIN_DFMT: /* BMP */
540 decodeok = volumetobin(volume, parameters.outfile);
542 fprintf(stdout, "[ERROR] Unable to write decoded volume into pgx files\n");
546 switch (parameters.orig_format) {
547 case PGX_DFMT: /* PGX */
548 if (strcmp("NULL", parameters.original) != 0) {
549 fprintf(stdout, "Loading original file %s \n", parameters.original);
550 cparameters.subsampling_dx = 1;
551 cparameters.subsampling_dy = 1;
552 cparameters.subsampling_dz = 1;
553 cparameters.volume_offset_x0 = 0;
554 cparameters.volume_offset_y0 = 0;
555 cparameters.volume_offset_z0 = 0;
556 original = pgxtovolume(parameters.original, &cparameters);
560 case BIN_DFMT: /* BMP */
561 if (strcmp("NULL", parameters.original) != 0 &&
562 strcmp("NULL", parameters.imgfile) != 0) {
563 fprintf(stdout, "Loading original file %s %s\n", parameters.original,
565 cparameters.subsampling_dx = 1;
566 cparameters.subsampling_dy = 1;
567 cparameters.subsampling_dz = 1;
568 cparameters.volume_offset_x0 = 0;
569 cparameters.volume_offset_y0 = 0;
570 cparameters.volume_offset_z0 = 0;
571 original = bintovolume(parameters.original, parameters.imgfile, &cparameters);
576 fprintf(stdout, "[RESULT] Volume: %d x %d x %d (x %d bpv)\n ",
577 (volume->comps[0].w >> volume->comps[0].factor[0]),
578 (volume->comps[0].h >> volume->comps[0].factor[1]),
579 (volume->comps[0].l >> volume->comps[0].factor[2]),
580 volume->comps[0].prec);
583 psnr = calc_PSNR(original, volume);
584 ssim = calc_SSIM(original, volume);
586 fprintf(stdout, " PSNR: Inf , SSMI %f -- Perfect reconstruction!\n", ssim);
588 fprintf(stdout, " PSNR: %f , SSIM %f \n", psnr, ssim);
591 /* free remaining structures */
593 opj_destroy_decompress(dinfo);
596 /* free volume data structure */
597 opj_volume_destroy(volume);