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, Herv� 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 "openjpeg.h"
\r
38 #include "convert.h"
\r
41 #define stricmp strcasecmp
\r
42 #define strnicmp strncasecmp
\r
45 /* ----------------------------------------------------------------------- */
\r
46 static double calc_PSNR(opj_volume_t *original, opj_volume_t *decoded)
\r
48 int max, i, k, compno = 0, size;
\r
49 double sum, total = 0;
\r
52 max = (original->comps[compno].prec <= 8) ? 255 : (1 << original->comps[compno].prec) - 1;
\r
54 size = (original->x1 - original->x0) * (original->y1 - original->y0) * (original->z1 - original->z0);
\r
56 for (compno = 0; compno < original->numcomps; compno++) {
\r
57 for(sum = 0, i = 0; i < size; ++i) {
\r
58 if ((decoded->comps[compno].data[i] < 0) || (decoded->comps[compno].data[i] > max))
\r
59 fprintf(stdout,"[WARNING] Data out of range during PSNR computing...\n");
\r
61 sum += (original->comps[compno].data[i] - decoded->comps[compno].data[i]) * (original->comps[compno].data[i] - decoded->comps[compno].data[i]);
\r
65 total = ((sum==0.0) ? 0.0 : 10 * log10(max * max / sum));
\r
67 size = (original->x1 - original->x0) * (original->y1 - original->y0);
\r
69 for (k = 0; k < original->z1 - original->z0; k++) {
\r
70 int offset = k * size;
\r
71 for (sum = 0, compno = 0; compno < original->numcomps; compno++) {
\r
72 for(i = 0; i < size; ++i) {
\r
73 if ((decoded->comps[compno].data[i + offset] < 0) || (decoded->comps[compno].data[i + offset] > max))
\r
74 fprintf(stdout,"[WARNING] Data out of range during PSNR computing...\n");
\r
76 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
80 total = total + ((sum==0.0) ? 0.0 : 10 * log10(max * max / sum));
\r
84 if(total == 0) /* perfect reconstruction, PSNR should return infinity */
\r
88 //return 20 * log10((max - 1) / sqrt(sum));
\r
91 static double calc_SSIM(opj_volume_t *original, opj_volume_t *decoded)
\r
93 int max, i, compno = 0, size, sizeM;
\r
95 double mux = 0.0, muy = 0.0, sigmax = 0.0, sigmay = 0.0,
\r
96 sigmaxy = 0.0, structx = 0.0, structy = 0.0;
\r
97 double lcomp,ccomp,scomp;
\r
100 max = (original->comps[compno].prec <= 8) ? 255 : (1 << original->comps[compno].prec) - 1;
\r
101 size = (original->x1 - original->x0) * (original->y1 - original->y0) * (original->z1 - original->z0);
\r
105 // sizeM = size / (original->z1 - original->z0);
\r
108 for(sum = 0, i = 0; i < sizeM; ++i) {
\r
109 // First, the luminance of each signal is compared.
\r
110 mux += original->comps[compno].data[i];
\r
111 muy += decoded->comps[compno].data[i];
\r
116 //We use the standard deviation (the square root of variance) as an estimate of the signal contrast.
\r
117 for(sum = 0, i = 0; i < sizeM; ++i) {
\r
118 // First, the luminance of each signal is compared.
\r
119 sigmax += (original->comps[compno].data[i] - mux) * (original->comps[compno].data[i] - mux);
\r
120 sigmay += (decoded->comps[compno].data[i] - muy) * (decoded->comps[compno].data[i] - muy);
\r
121 sigmaxy += (original->comps[compno].data[i] - mux) * (decoded->comps[compno].data[i] - muy);
\r
123 sigmax /= sizeM - 1;
\r
124 sigmay /= sizeM - 1;
\r
125 sigmaxy /= sizeM - 1;
\r
127 sigmax = sqrt(sigmax);
\r
128 sigmay = sqrt(sigmay);
\r
129 sigmaxy = sqrt(sigmaxy);
\r
131 //Third, the signal is normalized (divided) by its own standard deviation,
\r
132 //so that the two signals being compared have unit standard deviation.
\r
134 //Luminance comparison
\r
135 C1 = (0.01 * max) * (0.01 * max);
\r
136 lcomp = ((2 * mux * muy) + C1)/((mux*mux) + (muy*mux) + C1);
\r
137 //Constrast comparison
\r
138 C2 = (0.03 * max) * (0.03 * max);
\r
139 ccomp = ((2 * sigmax * sigmay) + C2)/((sigmax*sigmax) + (sigmay*sigmay) + C2);
\r
140 //Structure comparison
\r
142 scomp = (sigmaxy + C3) / (sigmax * sigmay + C3);
\r
143 //Similarity measure
\r
145 sum = lcomp * ccomp * scomp;
\r
149 void decode_help_display() {
\r
150 fprintf(stdout,"HELP\n----\n\n");
\r
151 fprintf(stdout,"- the -h option displays this help information on screen\n\n");
\r
153 fprintf(stdout,"List of parameters for the JPEG 2000 encoder:\n");
\r
154 fprintf(stdout,"\n");
\r
155 fprintf(stdout," Required arguments \n");
\r
156 fprintf(stdout," ---------------------------- \n");
\r
157 fprintf(stdout," -i <compressed file> ( *.jp3d, *.j3d )\n");
\r
158 fprintf(stdout," Currently accepts J3D-files. The file type is identified based on its suffix.\n");
\r
159 fprintf(stdout," -o <decompressed file> ( *.pgx, *.bin )\n");
\r
160 fprintf(stdout," Currently accepts PGX-files and BIN-files. Binary data is written to the file (not ascii). \n");
\r
161 fprintf(stdout," If a PGX filename is given, there will be as many output files as slices; \n");
\r
162 fprintf(stdout," an indice starting from 0 will then be appended to the output filename,\n");
\r
163 fprintf(stdout," just before the \"pgx\" extension.\n");
\r
164 fprintf(stdout," -m <characteristics file> ( *.img ) \n");
\r
165 fprintf(stdout," Required only for BIN-files. Ascii data of volume characteristics is written. \n");
\r
166 fprintf(stdout,"\n");
\r
167 fprintf(stdout," Optional \n");
\r
168 fprintf(stdout," ---------------------------- \n");
\r
169 fprintf(stdout," -h \n ");
\r
170 fprintf(stdout," Display the help information\n");
\r
171 fprintf(stdout," -r <RFx,RFy,RFz>\n");
\r
172 fprintf(stdout," Set the number of highest resolution levels to be discarded on each dimension. \n");
\r
173 fprintf(stdout," The volume resolution is effectively divided by 2 to the power of the\n");
\r
174 fprintf(stdout," number of discarded levels. The reduce factor is limited by the\n");
\r
175 fprintf(stdout," smallest total number of decomposition levels among tiles.\n");
\r
176 fprintf(stdout," -l <number of quality layers to decode>\n");
\r
177 fprintf(stdout," Set the maximum number of quality layers to decode. If there are\n");
\r
178 fprintf(stdout," less quality layers than the specified number, all the quality layers\n");
\r
179 fprintf(stdout," are decoded. \n");
\r
180 fprintf(stdout," -O original-file \n");
\r
181 fprintf(stdout," This option offers the possibility to compute some quality results \n");
\r
182 fprintf(stdout," for the decompressed volume, like the PSNR value achieved or the global SSIM value. \n");
\r
183 fprintf(stdout," Needs the original file in order to compare with the new one.\n");
\r
184 fprintf(stdout," NOTE: Only valid when -r option is 0,0,0 (both original and decompressed volumes have same resolutions) \n");
\r
185 fprintf(stdout," NOTE: If original file is .BIN file, the volume characteristics file shall be defined with the -m option. \n");
\r
186 fprintf(stdout," (i.e. -O original-BIN-file -m original-IMG-file) \n");
\r
187 fprintf(stdout," -BE \n");
\r
188 fprintf(stdout," Define that the recovered volume data will be saved with big endian byte order.\n");
\r
189 fprintf(stdout," By default, little endian byte order is used.\n");
\r
190 fprintf(stdout,"\n");
\r
193 /* -------------------------------------------------------------------------- */
\r
195 int get_file_format(char *filename) {
\r
197 static const char *extension[] = {"pgx", "bin", "j3d", "jp3d", "j2k", "img"};
\r
198 static const int format[] = { PGX_DFMT, BIN_DFMT, J3D_CFMT, J3D_CFMT, J2K_CFMT, IMG_DFMT};
\r
199 char * ext = strrchr(filename, '.') + 1;
\r
200 for(i = 0; i < sizeof(format) / sizeof(format[0]); i++) {
\r
201 if(strnicmp(ext, extension[i], 3) == 0) {
\r
209 /* -------------------------------------------------------------------------- */
\r
211 int parse_cmdline_decoder(int argc, char **argv, opj_dparameters_t *parameters) {
\r
212 /* parse the command line */
\r
215 int c = getopt(argc, argv, "i:o:O:r:l:B:m:h");
\r
219 case 'i': /* input file */
\r
221 char *infile = optarg;
\r
222 parameters->decod_format = get_file_format(infile);
\r
223 switch(parameters->decod_format) {
\r
228 fprintf(stdout, "[ERROR] Unknown format for infile %s [only *.j3d]!! \n", infile);
\r
232 strncpy(parameters->infile, infile, MAX_PATH);
\r
233 fprintf(stdout, "[INFO] Infile: %s \n", parameters->infile);
\r
238 case 'm': /* img file */
\r
240 char *imgfile = optarg;
\r
241 int imgformat = get_file_format(imgfile);
\r
242 switch(imgformat) {
\r
246 fprintf(stdout, "[ERROR] Unrecognized format for imgfile : %s [accept only *.img] !!\n\n", imgfile);
\r
250 strncpy(parameters->imgfile, imgfile, MAX_PATH);
\r
251 fprintf(stdout, "[INFO] Imgfile: %s Format: %d\n", parameters->imgfile, imgformat);
\r
255 /* ----------------------------------------------------- */
\r
257 case 'o': /* output file */
\r
259 char *outfile = optarg;
\r
260 parameters->cod_format = get_file_format(outfile);
\r
261 switch(parameters->cod_format) {
\r
266 fprintf(stdout, "[ERROR] Unrecognized format for outfile : %s [accept only *.pgx or *.bin] !!\n\n", outfile);
\r
270 strncpy(parameters->outfile, outfile, MAX_PATH);
\r
271 fprintf(stdout, "[INFO] Outfile: %s \n", parameters->outfile);
\r
276 /* ----------------------------------------------------- */
\r
278 case 'O': /* Original image for PSNR computing */
\r
280 char *original = optarg;
\r
281 parameters->orig_format = get_file_format(original);
\r
282 switch(parameters->orig_format) {
\r
287 fprintf(stdout, "[ERROR] Unrecognized format for original file : %s [accept only *.pgx or *.bin] !!\n\n", original);
\r
291 strncpy(parameters->original, original, MAX_PATH);
\r
292 fprintf(stdout, "[INFO] Original file: %s \n", parameters->original);
\r
296 /* ----------------------------------------------------- */
\r
298 case 'r': /* reduce option */
\r
300 //sscanf(optarg, "%d, %d, %d", ¶meters->cp_reduce[0], ¶meters->cp_reduce[1], ¶meters->cp_reduce[2]);
\r
302 aux = sscanf(optarg, "%d,%d,%d", ¶meters->cp_reduce[0], ¶meters->cp_reduce[1], ¶meters->cp_reduce[2]);
\r
304 parameters->cp_reduce[2] = 0;
\r
305 else if (aux == 1) {
\r
306 parameters->cp_reduce[1] = parameters->cp_reduce[0];
\r
307 parameters->cp_reduce[2] = 0;
\r
308 }else if (aux == 0){
\r
309 parameters->cp_reduce[0] = 0;
\r
310 parameters->cp_reduce[1] = 0;
\r
311 parameters->cp_reduce[2] = 0;
\r
316 /* ----------------------------------------------------- */
\r
318 case 'l': /* layering option */
\r
320 sscanf(optarg, "%d", ¶meters->cp_layer);
\r
324 /* ----------------------------------------------------- */
\r
326 case 'B': /* BIGENDIAN vs. LITTLEENDIAN */
\r
328 parameters->bigendian = 1;
\r
332 /* ----------------------------------------------------- */
\r
334 case 'L': /* BIGENDIAN vs. LITTLEENDIAN */
\r
336 parameters->decod_format = LSE_CFMT;
\r
340 /* ----------------------------------------------------- */
\r
342 case 'h': /* display an help description */
\r
344 decode_help_display();
\r
349 /* ----------------------------------------------------- */
\r
352 fprintf(stdout,"[WARNING] This option is not valid \"-%c %s\"\n",c, optarg);
\r
357 /* check for possible errors */
\r
359 if((parameters->infile[0] == 0) || (parameters->outfile[0] == 0)) {
\r
360 fprintf(stdout,"[ERROR] At least one required argument is missing\n Check jp3d_to_volume -help for usage information\n");
\r
367 /* -------------------------------------------------------------------------- */
\r
370 sample error callback expecting a FILE* client object
\r
372 void error_callback(const char *msg, void *client_data) {
\r
373 FILE *stream = (FILE*)client_data;
\r
374 fprintf(stream, "[ERROR] %s", msg);
\r
377 sample warning callback expecting a FILE* client object
\r
379 void warning_callback(const char *msg, void *client_data) {
\r
380 FILE *stream = (FILE*)client_data;
\r
381 fprintf(stream, "[WARNING] %s", msg);
\r
384 sample debug callback expecting no client object
\r
386 void info_callback(const char *msg, void *client_data) {
\r
387 fprintf(stdout, "[INFO] %s", msg);
\r
390 /* -------------------------------------------------------------------------- */
\r
392 int main(int argc, char **argv) {
\r
394 opj_dparameters_t parameters; /* decompression parameters */
\r
395 opj_event_mgr_t event_mgr; /* event manager */
\r
396 opj_volume_t *volume = NULL;
\r
398 opj_volume_t *original = NULL;
\r
399 opj_cparameters_t cparameters; /* original parameters */
\r
402 unsigned char *src = NULL;
\r
407 opj_dinfo_t* dinfo = NULL; /* handle to a decompressor */
\r
408 opj_cio_t *cio = NULL;
\r
410 /* configure the event callbacks (not required) */
\r
411 memset(&event_mgr, 0, sizeof(opj_event_mgr_t));
\r
412 event_mgr.error_handler = error_callback;
\r
413 event_mgr.warning_handler = warning_callback;
\r
414 event_mgr.info_handler = info_callback;
\r
416 /* set decoding parameters to default values */
\r
417 opj_set_default_decoder_parameters(¶meters);
\r
419 /* parse input and get user decoding parameters */
\r
420 strcpy(parameters.original,"NULL");
\r
421 strcpy(parameters.imgfile,"NULL");
\r
422 if(parse_cmdline_decoder(argc, argv, ¶meters) == 1) {
\r
426 /* read the input file and put it in memory */
\r
427 /* ---------------------------------------- */
\r
428 fprintf(stdout, "[INFO] Loading %s file \n",parameters.decod_format==J3D_CFMT ? ".jp3d" : ".j2k");
\r
429 fsrc = fopen(parameters.infile, "rb");
\r
431 fprintf(stdout, "[ERROR] Failed to open %s for reading\n", parameters.infile);
\r
434 fseek(fsrc, 0, SEEK_END);
\r
435 file_length = ftell(fsrc);
\r
436 fseek(fsrc, 0, SEEK_SET);
\r
437 src = (unsigned char *) malloc(file_length);
\r
438 fread(src, 1, file_length, fsrc);
\r
441 /* decode the code-stream */
\r
442 /* ---------------------- */
\r
443 if (parameters.decod_format == J3D_CFMT || parameters.decod_format == J2K_CFMT) {
\r
444 /* get a JP3D or J2K decoder handle */
\r
445 if (parameters.decod_format == J3D_CFMT)
\r
446 dinfo = opj_create_decompress(CODEC_J3D);
\r
447 else if (parameters.decod_format == J2K_CFMT)
\r
448 dinfo = opj_create_decompress(CODEC_J2K);
\r
450 /* catch events using our callbacks and give a local context */
\r
451 opj_set_event_mgr((opj_common_ptr)dinfo, &event_mgr, stderr);
\r
453 /* setup the decoder decoding parameters using user parameters */
\r
454 opj_setup_decoder(dinfo, ¶meters);
\r
456 /* open a byte stream */
\r
457 cio = opj_cio_open((opj_common_ptr)dinfo, src, file_length);
\r
459 /* decode the stream and fill the volume structure */
\r
460 volume = opj_decode(dinfo, cio);
\r
462 fprintf(stdout, "[ERROR] jp3d_to_volume: failed to decode volume!\n");
\r
463 opj_destroy_decompress(dinfo);
\r
464 opj_cio_close(cio);
\r
468 /* close the byte stream */
\r
469 opj_cio_close(cio);
\r
472 /* free the memory containing the code-stream */
\r
476 /* create output volume */
\r
477 /* ------------------- */
\r
479 switch (parameters.cod_format) {
\r
480 case PGX_DFMT: /* PGX */
\r
481 decodeok = volumetopgx(volume, parameters.outfile);
\r
483 fprintf(stdout,"[ERROR] Unable to write decoded volume into pgx files\n");
\r
486 case BIN_DFMT: /* BMP */
\r
487 decodeok = volumetobin(volume, parameters.outfile);
\r
489 fprintf(stdout,"[ERROR] Unable to write decoded volume into pgx files\n");
\r
492 switch (parameters.orig_format) {
\r
493 case PGX_DFMT: /* PGX */
\r
494 if (strcmp("NULL",parameters.original) != 0){
\r
495 fprintf(stdout,"Loading original file %s \n",parameters.original);
\r
496 cparameters.subsampling_dx = 1; cparameters.subsampling_dy = 1; cparameters.subsampling_dz = 1;
\r
497 cparameters.volume_offset_x0 = 0;cparameters.volume_offset_y0 = 0;cparameters.volume_offset_z0 = 0;
\r
498 original = pgxtovolume(parameters.original,&cparameters);
\r
502 case BIN_DFMT: /* BMP */
\r
503 if (strcmp("NULL",parameters.original) != 0 && strcmp("NULL",parameters.imgfile) != 0){
\r
504 fprintf(stdout,"Loading original file %s %s\n",parameters.original,parameters.imgfile);
\r
505 cparameters.subsampling_dx = 1; cparameters.subsampling_dy = 1; cparameters.subsampling_dz = 1;
\r
506 cparameters.volume_offset_x0 = 0;cparameters.volume_offset_y0 = 0;cparameters.volume_offset_z0 = 0;
\r
507 original = bintovolume(parameters.original,parameters.imgfile,&cparameters);
\r
512 fprintf(stdout, "[RESULT] Volume: %d x %d x %d (x %d bpv)\n ",
\r
513 (volume->comps[0].w >> volume->comps[0].factor[0]),
\r
514 (volume->comps[0].h >> volume->comps[0].factor[1]),
\r
515 (volume->comps[0].l >> volume->comps[0].factor[2]),volume->comps[0].prec);
\r
518 psnr = calc_PSNR(original,volume);
\r
519 ssim = calc_SSIM(original,volume);
\r
521 fprintf(stdout, " PSNR: Inf , SSMI %f -- Perfect reconstruction!\n",ssim);
\r
523 fprintf(stdout, " PSNR: %f , SSIM %f \n",psnr,ssim);
\r
525 /* free remaining structures */
\r
527 opj_destroy_decompress(dinfo);
\r
530 /* free volume data structure */
\r
531 opj_volume_destroy(volume);
\r