2 * Copyright (c) 2001-2003, David Janssens
\r
3 * Copyright (c) 2002-2003, Yannick Verschueren
\r
4 * Copyright (c) 2003-2005, Francois Devaux and Antonin Descampe
\r
5 * Copyright (c) 2005, Herve Drolon, FreeImage Team
\r
6 * Copyright (c) 2002-2005, Communications and remote sensing Laboratory, Universite catholique de Louvain, Belgium
\r
7 * Copyright (c) 2006, M�nica D�ez Garc�a, Image Processing Laboratory, University of Valladolid, Spain
\r
8 * All rights reserved.
\r
10 * Redistribution and use in source and binary forms, with or without
\r
11 * modification, are permitted provided that the following conditions
\r
13 * 1. Redistributions of source code must retain the above copyright
\r
14 * notice, this list of conditions and the following disclaimer.
\r
15 * 2. Redistributions in binary form must reproduce the above copyright
\r
16 * notice, this list of conditions and the following disclaimer in the
\r
17 * documentation and/or other materials provided with the distribution.
\r
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
\r
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
\r
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
\r
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
\r
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
\r
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
\r
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
\r
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
\r
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
\r
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
\r
29 * POSSIBILITY OF SUCH DAMAGE.
\r
36 #include "opj_config.h"
\r
37 #include "openjp3d.h"
\r
38 #include "opj_getopt.h"
\r
39 #include "convert.h"
\r
42 #include <windows.h>
\r
44 #define stricmp strcasecmp
\r
45 #define strnicmp strncasecmp
\r
48 /* ----------------------------------------------------------------------- */
\r
49 static double calc_PSNR(opj_volume_t *original, opj_volume_t *decoded)
\r
51 int max, i, k, compno = 0, size;
\r
52 double sum, total = 0;
\r
55 max = (original->comps[compno].prec <= 8) ? 255 : (1 << original->comps[compno].prec) - 1;
\r
57 size = (original->x1 - original->x0) * (original->y1 - original->y0) * (original->z1 - original->z0);
\r
59 for (compno = 0; compno < original->numcomps; compno++) {
\r
60 for(sum = 0, i = 0; i < size; ++i) {
\r
61 if ((decoded->comps[compno].data[i] < 0) || (decoded->comps[compno].data[i] > max))
\r
62 fprintf(stdout,"[WARNING] Data out of range during PSNR computing...\n");
\r
64 sum += (original->comps[compno].data[i] - decoded->comps[compno].data[i]) * (original->comps[compno].data[i] - decoded->comps[compno].data[i]);
\r
68 total = ((sum==0.0) ? 0.0 : 10 * log10(max * max / sum));
\r
70 size = (original->x1 - original->x0) * (original->y1 - original->y0);
\r
72 for (k = 0; k < original->z1 - original->z0; k++) {
\r
73 int offset = k * size;
\r
74 for (sum = 0, compno = 0; compno < original->numcomps; compno++) {
\r
75 for(i = 0; i < size; ++i) {
\r
76 if ((decoded->comps[compno].data[i + offset] < 0) || (decoded->comps[compno].data[i + offset] > max))
\r
77 fprintf(stdout,"[WARNING] Data out of range during PSNR computing...\n");
\r
79 sum += (original->comps[compno].data[i + offset] - decoded->comps[compno].data[i + offset]) * (original->comps[compno].data[i + offset] - decoded->comps[compno].data[i + offset]);
\r
83 total = total + ((sum==0.0) ? 0.0 : 10 * log10(max * max / sum));
\r
87 if(total == 0) /* perfect reconstruction, PSNR should return infinity */
\r
91 /*return 20 * log10((max - 1) / sqrt(sum));*/
\r
94 static double calc_SSIM(opj_volume_t *original, opj_volume_t *decoded)
\r
96 int max, i, compno = 0, size, sizeM;
\r
98 double mux = 0.0, muy = 0.0, sigmax = 0.0, sigmay = 0.0,
\r
99 sigmaxy = 0.0/*, structx = 0.0, structy = 0.0*/;
\r
100 double lcomp,ccomp,scomp;
\r
103 max = (original->comps[compno].prec <= 8) ? 255 : (1 << original->comps[compno].prec) - 1;
\r
104 size = (original->x1 - original->x0) * (original->y1 - original->y0) * (original->z1 - original->z0);
\r
108 /* sizeM = size / (original->z1 - original->z0);*/
\r
111 for(sum = 0, i = 0; i < sizeM; ++i) {
\r
112 /* First, the luminance of each signal is compared.*/
\r
113 mux += original->comps[compno].data[i];
\r
114 muy += decoded->comps[compno].data[i];
\r
119 /*We use the standard deviation (the square root of variance) as an estimate of the signal contrast.*/
\r
120 for(sum = 0, i = 0; i < sizeM; ++i) {
\r
121 /* First, the luminance of each signal is compared.*/
\r
122 sigmax += (original->comps[compno].data[i] - mux) * (original->comps[compno].data[i] - mux);
\r
123 sigmay += (decoded->comps[compno].data[i] - muy) * (decoded->comps[compno].data[i] - muy);
\r
124 sigmaxy += (original->comps[compno].data[i] - mux) * (decoded->comps[compno].data[i] - muy);
\r
126 sigmax /= sizeM - 1;
\r
127 sigmay /= sizeM - 1;
\r
128 sigmaxy /= sizeM - 1;
\r
130 sigmax = sqrt(sigmax);
\r
131 sigmay = sqrt(sigmay);
\r
132 sigmaxy = sqrt(sigmaxy);
\r
134 /*Third, the signal is normalized (divided) by its own standard deviation, */
\r
135 /*so that the two signals being compared have unit standard deviation.*/
\r
137 /*Luminance comparison*/
\r
138 C1 = (0.01 * max) * (0.01 * max);
\r
139 lcomp = ((2 * mux * muy) + C1)/((mux*mux) + (muy*mux) + C1);
\r
140 /*Constrast comparison*/
\r
141 C2 = (0.03 * max) * (0.03 * max);
\r
142 ccomp = ((2 * sigmax * sigmay) + C2)/((sigmax*sigmax) + (sigmay*sigmay) + C2);
\r
143 /*Structure comparison*/
\r
145 scomp = (sigmaxy + C3) / (sigmax * sigmay + C3);
\r
146 /*Similarity measure*/
\r
148 sum = lcomp * ccomp * scomp;
\r
152 void decode_help_display() {
\r
153 fprintf(stdout,"HELP\n----\n\n");
\r
154 fprintf(stdout,"- the -h option displays this help information on screen\n\n");
\r
156 fprintf(stdout,"List of parameters for the JPEG 2000 encoder:\n");
\r
157 fprintf(stdout,"\n");
\r
158 fprintf(stdout," Required arguments \n");
\r
159 fprintf(stdout," ---------------------------- \n");
\r
160 fprintf(stdout," -i <compressed file> ( *.jp3d, *.j3d )\n");
\r
161 fprintf(stdout," Currently accepts J3D-files. The file type is identified based on its suffix.\n");
\r
162 fprintf(stdout," -o <decompressed file> ( *.pgx, *.bin )\n");
\r
163 fprintf(stdout," Currently accepts PGX-files and BIN-files. Binary data is written to the file (not ascii). \n");
\r
164 fprintf(stdout," If a PGX filename is given, there will be as many output files as slices; \n");
\r
165 fprintf(stdout," an indice starting from 0 will then be appended to the output filename,\n");
\r
166 fprintf(stdout," just before the \"pgx\" extension.\n");
\r
167 fprintf(stdout," -m <characteristics file> ( *.img ) \n");
\r
168 fprintf(stdout," Required only for BIN-files. Ascii data of volume characteristics is written. \n");
\r
169 fprintf(stdout,"\n");
\r
170 fprintf(stdout," Optional \n");
\r
171 fprintf(stdout," ---------------------------- \n");
\r
172 fprintf(stdout," -h \n ");
\r
173 fprintf(stdout," Display the help information\n");
\r
174 fprintf(stdout," -r <RFx,RFy,RFz>\n");
\r
175 fprintf(stdout," Set the number of highest resolution levels to be discarded on each dimension. \n");
\r
176 fprintf(stdout," The volume resolution is effectively divided by 2 to the power of the\n");
\r
177 fprintf(stdout," number of discarded levels. The reduce factor is limited by the\n");
\r
178 fprintf(stdout," smallest total number of decomposition levels among tiles.\n");
\r
179 fprintf(stdout," -l <number of quality layers to decode>\n");
\r
180 fprintf(stdout," Set the maximum number of quality layers to decode. If there are\n");
\r
181 fprintf(stdout," less quality layers than the specified number, all the quality layers\n");
\r
182 fprintf(stdout," are decoded. \n");
\r
183 fprintf(stdout," -O original-file \n");
\r
184 fprintf(stdout," This option offers the possibility to compute some quality results \n");
\r
185 fprintf(stdout," for the decompressed volume, like the PSNR value achieved or the global SSIM value. \n");
\r
186 fprintf(stdout," Needs the original file in order to compare with the new one.\n");
\r
187 fprintf(stdout," NOTE: Only valid when -r option is 0,0,0 (both original and decompressed volumes have same resolutions) \n");
\r
188 fprintf(stdout," NOTE: If original file is .BIN file, the volume characteristics file shall be defined with the -m option. \n");
\r
189 fprintf(stdout," (i.e. -O original-BIN-file -m original-IMG-file) \n");
\r
190 fprintf(stdout," -BE \n");
\r
191 fprintf(stdout," Define that the recovered volume data will be saved with big endian byte order.\n");
\r
192 fprintf(stdout," By default, little endian byte order is used.\n");
\r
193 fprintf(stdout,"\n");
\r
196 /* -------------------------------------------------------------------------- */
\r
198 int get_file_format(char *filename) {
\r
200 static const char *extension[] = {"pgx", "bin", "j3d", "jp3d", "j2k", "img"};
\r
201 static const int format[] = { PGX_DFMT, BIN_DFMT, J3D_CFMT, J3D_CFMT, J2K_CFMT, IMG_DFMT};
\r
202 char * ext = strrchr(filename, '.');
\r
205 for(i = 0; i < sizeof(format) / sizeof(format[0]); i++) {
\r
206 if(strnicmp(ext, extension[i], 3) == 0) {
\r
215 /* -------------------------------------------------------------------------- */
\r
217 int parse_cmdline_decoder(int argc, char **argv, opj_dparameters_t *parameters) {
\r
218 /* parse the command line */
\r
221 int c = opj_getopt(argc, argv, "i:o:O:r:l:B:m:h");
\r
225 case 'i': /* input file */
\r
227 char *infile = opj_optarg;
\r
228 parameters->decod_format = get_file_format(infile);
\r
229 switch(parameters->decod_format) {
\r
234 fprintf(stdout, "[ERROR] Unknown format for infile %s [only *.j3d]!! \n", infile);
\r
238 strncpy(parameters->infile, infile, MAX_PATH);
\r
239 fprintf(stdout, "[INFO] Infile: %s \n", parameters->infile);
\r
244 case 'm': /* img file */
\r
246 char *imgfile = opj_optarg;
\r
247 int imgformat = get_file_format(imgfile);
\r
248 switch(imgformat) {
\r
252 fprintf(stdout, "[ERROR] Unrecognized format for imgfile : %s [accept only *.img] !!\n\n", imgfile);
\r
256 strncpy(parameters->imgfile, imgfile, MAX_PATH);
\r
257 fprintf(stdout, "[INFO] Imgfile: %s Format: %d\n", parameters->imgfile, imgformat);
\r
261 /* ----------------------------------------------------- */
\r
263 case 'o': /* output file */
\r
265 char *outfile = opj_optarg;
\r
266 parameters->cod_format = get_file_format(outfile);
\r
267 switch(parameters->cod_format) {
\r
272 fprintf(stdout, "[ERROR] Unrecognized format for outfile : %s [accept only *.pgx or *.bin] !!\n\n", outfile);
\r
276 strncpy(parameters->outfile, outfile, MAX_PATH);
\r
277 fprintf(stdout, "[INFO] Outfile: %s \n", parameters->outfile);
\r
282 /* ----------------------------------------------------- */
\r
284 case 'O': /* Original image for PSNR computing */
\r
286 char *original = opj_optarg;
\r
287 parameters->orig_format = get_file_format(original);
\r
288 switch(parameters->orig_format) {
\r
293 fprintf(stdout, "[ERROR] Unrecognized format for original file : %s [accept only *.pgx or *.bin] !!\n\n", original);
\r
297 strncpy(parameters->original, original, MAX_PATH);
\r
298 fprintf(stdout, "[INFO] Original file: %s \n", parameters->original);
\r
302 /* ----------------------------------------------------- */
\r
304 case 'r': /* reduce option */
\r
306 /*sscanf(opj_optarg, "%d, %d, %d", ¶meters->cp_reduce[0], ¶meters->cp_reduce[1], ¶meters->cp_reduce[2]);*/
\r
308 aux = sscanf(opj_optarg, "%d,%d,%d", ¶meters->cp_reduce[0], ¶meters->cp_reduce[1], ¶meters->cp_reduce[2]);
\r
310 parameters->cp_reduce[2] = 0;
\r
311 else if (aux == 1) {
\r
312 parameters->cp_reduce[1] = parameters->cp_reduce[0];
\r
313 parameters->cp_reduce[2] = 0;
\r
314 }else if (aux == 0){
\r
315 parameters->cp_reduce[0] = 0;
\r
316 parameters->cp_reduce[1] = 0;
\r
317 parameters->cp_reduce[2] = 0;
\r
322 /* ----------------------------------------------------- */
\r
324 case 'l': /* layering option */
\r
326 sscanf(opj_optarg, "%d", ¶meters->cp_layer);
\r
330 /* ----------------------------------------------------- */
\r
332 case 'B': /* BIGENDIAN vs. LITTLEENDIAN */
\r
334 parameters->bigendian = 1;
\r
338 /* ----------------------------------------------------- */
\r
340 case 'L': /* BIGENDIAN vs. LITTLEENDIAN */
\r
342 parameters->decod_format = LSE_CFMT;
\r
346 /* ----------------------------------------------------- */
\r
348 case 'h': /* display an help description */
\r
350 decode_help_display();
\r
355 /* ----------------------------------------------------- */
\r
358 fprintf(stdout,"[WARNING] This option is not valid \"-%c %s\"\n",c, opj_optarg);
\r
363 /* check for possible errors */
\r
365 if((parameters->infile[0] == 0) || (parameters->outfile[0] == 0)) {
\r
366 fprintf(stdout,"[ERROR] At least one required argument is missing\n Check jp3d_to_volume -help for usage information\n");
\r
373 /* -------------------------------------------------------------------------- */
\r
376 sample error callback expecting a FILE* client object
\r
378 void error_callback(const char *msg, void *client_data) {
\r
379 FILE *stream = (FILE*)client_data;
\r
380 fprintf(stream, "[ERROR] %s", msg);
\r
383 sample warning callback expecting a FILE* client object
\r
385 void warning_callback(const char *msg, void *client_data) {
\r
386 FILE *stream = (FILE*)client_data;
\r
387 fprintf(stream, "[WARNING] %s", msg);
\r
390 sample debug callback expecting no client object
\r
392 void info_callback(const char *msg, void *client_data) {
\r
393 fprintf(stdout, "[INFO] %s", msg);
\r
396 /* -------------------------------------------------------------------------- */
\r
398 int main(int argc, char **argv) {
\r
400 opj_dparameters_t parameters; /* decompression parameters */
\r
401 opj_event_mgr_t event_mgr; /* event manager */
\r
402 opj_volume_t *volume = NULL;
\r
404 opj_volume_t *original = NULL;
\r
405 opj_cparameters_t cparameters; /* original parameters */
\r
408 unsigned char *src = NULL;
\r
413 opj_dinfo_t* dinfo = NULL; /* handle to a decompressor */
\r
414 opj_cio_t *cio = NULL;
\r
416 /* configure the event callbacks (not required) */
\r
417 memset(&event_mgr, 0, sizeof(opj_event_mgr_t));
\r
418 event_mgr.error_handler = error_callback;
\r
419 event_mgr.warning_handler = warning_callback;
\r
420 event_mgr.info_handler = info_callback;
\r
422 /* set decoding parameters to default values */
\r
423 opj_set_default_decoder_parameters(¶meters);
\r
425 /* parse input and get user decoding parameters */
\r
426 strcpy(parameters.original,"NULL");
\r
427 strcpy(parameters.imgfile,"NULL");
\r
428 if(parse_cmdline_decoder(argc, argv, ¶meters) == 1) {
\r
432 /* read the input file and put it in memory */
\r
433 /* ---------------------------------------- */
\r
434 fprintf(stdout, "[INFO] Loading %s file \n",parameters.decod_format==J3D_CFMT ? ".jp3d" : ".j2k");
\r
435 fsrc = fopen(parameters.infile, "rb");
\r
437 fprintf(stdout, "[ERROR] Failed to open %s for reading\n", parameters.infile);
\r
440 fseek(fsrc, 0, SEEK_END);
\r
441 file_length = ftell(fsrc);
\r
442 fseek(fsrc, 0, SEEK_SET);
\r
443 src = (unsigned char *) malloc(file_length);
\r
444 fread(src, 1, file_length, fsrc);
\r
447 /* decode the code-stream */
\r
448 /* ---------------------- */
\r
449 if (parameters.decod_format == J3D_CFMT || parameters.decod_format == J2K_CFMT) {
\r
450 /* get a JP3D or J2K decoder handle */
\r
451 if (parameters.decod_format == J3D_CFMT)
\r
452 dinfo = opj_create_decompress(CODEC_J3D);
\r
453 else if (parameters.decod_format == J2K_CFMT)
\r
454 dinfo = opj_create_decompress(CODEC_J2K);
\r
456 /* catch events using our callbacks and give a local context */
\r
457 opj_set_event_mgr((opj_common_ptr)dinfo, &event_mgr, stderr);
\r
459 /* setup the decoder decoding parameters using user parameters */
\r
460 opj_setup_decoder(dinfo, ¶meters);
\r
462 /* open a byte stream */
\r
463 cio = opj_cio_open((opj_common_ptr)dinfo, src, file_length);
\r
465 /* decode the stream and fill the volume structure */
\r
466 volume = opj_decode(dinfo, cio);
\r
468 fprintf(stdout, "[ERROR] jp3d_to_volume: failed to decode volume!\n");
\r
469 opj_destroy_decompress(dinfo);
\r
470 opj_cio_close(cio);
\r
474 /* close the byte stream */
\r
475 opj_cio_close(cio);
\r
478 /* free the memory containing the code-stream */
\r
482 /* create output volume */
\r
483 /* ------------------- */
\r
485 switch (parameters.cod_format) {
\r
486 case PGX_DFMT: /* PGX */
\r
487 decodeok = volumetopgx(volume, parameters.outfile);
\r
489 fprintf(stdout,"[ERROR] Unable to write decoded volume into pgx files\n");
\r
492 case BIN_DFMT: /* BMP */
\r
493 decodeok = volumetobin(volume, parameters.outfile);
\r
495 fprintf(stdout,"[ERROR] Unable to write decoded volume into pgx files\n");
\r
498 switch (parameters.orig_format) {
\r
499 case PGX_DFMT: /* PGX */
\r
500 if (strcmp("NULL",parameters.original) != 0){
\r
501 fprintf(stdout,"Loading original file %s \n",parameters.original);
\r
502 cparameters.subsampling_dx = 1; cparameters.subsampling_dy = 1; cparameters.subsampling_dz = 1;
\r
503 cparameters.volume_offset_x0 = 0;cparameters.volume_offset_y0 = 0;cparameters.volume_offset_z0 = 0;
\r
504 original = pgxtovolume(parameters.original,&cparameters);
\r
508 case BIN_DFMT: /* BMP */
\r
509 if (strcmp("NULL",parameters.original) != 0 && strcmp("NULL",parameters.imgfile) != 0){
\r
510 fprintf(stdout,"Loading original file %s %s\n",parameters.original,parameters.imgfile);
\r
511 cparameters.subsampling_dx = 1; cparameters.subsampling_dy = 1; cparameters.subsampling_dz = 1;
\r
512 cparameters.volume_offset_x0 = 0;cparameters.volume_offset_y0 = 0;cparameters.volume_offset_z0 = 0;
\r
513 original = bintovolume(parameters.original,parameters.imgfile,&cparameters);
\r
518 fprintf(stdout, "[RESULT] Volume: %d x %d x %d (x %d bpv)\n ",
\r
519 (volume->comps[0].w >> volume->comps[0].factor[0]),
\r
520 (volume->comps[0].h >> volume->comps[0].factor[1]),
\r
521 (volume->comps[0].l >> volume->comps[0].factor[2]),
\r
522 volume->comps[0].prec);
\r
525 psnr = calc_PSNR(original,volume);
\r
526 ssim = calc_SSIM(original,volume);
\r
528 fprintf(stdout, " PSNR: Inf , SSMI %f -- Perfect reconstruction!\n",ssim);
\r
530 fprintf(stdout, " PSNR: %f , SSIM %f \n",psnr,ssim);
\r
532 /* free remaining structures */
\r
534 opj_destroy_decompress(dinfo);
\r
537 /* free volume data structure */
\r
538 opj_volume_destroy(volume);
\r