github.com/cellofellow/gopkg@v0.0.0-20140722061823-eec0544a62ad/image/jxr/jxrlib/test/jpeg/tga2jpg.cpp (about) 1 // tga2jpg.cpp - jpge/jpgd example command line app. 2 // Public domain, Rich Geldreich <richgel99@gmail.com> 3 // Last updated May. 19, 2012 4 5 // Note: jpge.cpp/h and jpgd.cpp/h are completely standalone, i.e. they do not have any dependencies to each other. 6 #include "jpge.h" 7 #include "jpgd.h" 8 #include "stb_image.c" 9 #include "timer.h" 10 #include <ctype.h> 11 12 #if defined(_MSC_VER) 13 #define strcasecmp _stricmp 14 #else 15 #define strcpy_s(d, c, s) strcpy(d, s) 16 #endif 17 18 static int print_usage() 19 { 20 printf("Usage: jpge [options] <source_file> <dest_file> <quality_factor>\n"); 21 printf("\nRequired parameters (must follow options):\n"); 22 printf("source_file: Source image file, in any format stb_image.c supports.\n"); 23 printf("dest_file: Destination JPEG file.\n"); 24 printf("quality_factor: 1-100, higher=better (only needed in compression mode)\n"); 25 printf("\nDefault mode compresses source_file to dest_file. Alternate modes:\n"); 26 printf("-x: Exhaustive compression test (only needs source_file)\n"); 27 printf("-d: Test jpgd.h. source_file must be JPEG, and dest_file must be .TGA\n"); 28 printf("\nOptions supported in all modes:\n"); 29 printf("-glogfilename.txt: Append output to log file\n"); 30 printf("\nOptions supported in compression mode (the default):\n"); 31 printf("-o: Enable optimized Huffman tables (slower, but smaller files)\n"); 32 printf("-luma: Output Y-only image\n"); 33 printf("-h1v1, -h2v1, -h2v2: Chroma subsampling (default is either Y-only or H2V2)\n"); 34 printf("-m: Test mem to mem compression (instead of mem to file)\n"); 35 printf("-wfilename.tga: Write decompressed image to filename.tga\n"); 36 printf("-s: Use stb_image.c to decompress JPEG image, instead of jpgd.cpp\n"); 37 printf("\nExample usages:\n"); 38 printf("Test compression: jpge orig.png comp.jpg 90\n"); 39 printf("Test decompression: jpge -d comp.jpg uncomp.tga\n"); 40 printf("Exhaustively test compressor: jpge -x orig.png\n"); 41 42 return EXIT_FAILURE; 43 } 44 45 static char s_log_filename[256]; 46 47 static void log_printf(const char *pMsg, ...) 48 { 49 va_list args; 50 51 va_start(args, pMsg); 52 char buf[2048]; 53 vsnprintf(buf, sizeof(buf) - 1, pMsg, args); 54 buf[sizeof(buf) - 1] = '\0'; 55 va_end(args); 56 57 printf("%s", buf); 58 59 if (s_log_filename[0]) 60 { 61 FILE *pFile = fopen(s_log_filename, "a+"); 62 if (pFile) 63 { 64 fprintf(pFile, "%s", buf); 65 fclose(pFile); 66 } 67 } 68 } 69 70 static uint get_file_size(const char *pFilename) 71 { 72 FILE *pFile = fopen(pFilename, "rb"); 73 if (!pFile) return 0; 74 fseek(pFile, 0, SEEK_END); 75 uint file_size = ftell(pFile); 76 fclose(pFile); 77 return file_size; 78 } 79 80 struct image_compare_results 81 { 82 image_compare_results() { memset(this, 0, sizeof(*this)); } 83 84 double max_err; 85 double mean; 86 double mean_squared; 87 double root_mean_squared; 88 double peak_snr; 89 }; 90 91 static void get_pixel(int* pDst, const uint8 *pSrc, bool luma_only, int num_comps) 92 { 93 int r, g, b; 94 if (num_comps == 1) 95 { 96 r = g = b = pSrc[0]; 97 } 98 else if (luma_only) 99 { 100 const int YR = 19595, YG = 38470, YB = 7471; 101 r = g = b = (pSrc[0] * YR + pSrc[1] * YG + pSrc[2] * YB + 32768) / 65536; 102 } 103 else 104 { 105 r = pSrc[0]; g = pSrc[1]; b = pSrc[2]; 106 } 107 pDst[0] = r; pDst[1] = g; pDst[2] = b; 108 } 109 110 // Compute image error metrics. 111 static void image_compare(image_compare_results &results, int width, int height, const uint8 *pComp_image, int comp_image_comps, const uint8 *pUncomp_image_data, int uncomp_comps, bool luma_only) 112 { 113 double hist[256]; 114 memset(hist, 0, sizeof(hist)); 115 116 const uint first_channel = 0, num_channels = 3; 117 for (int y = 0; y < height; y++) 118 { 119 for (int x = 0; x < width; x++) 120 { 121 int a[3]; get_pixel(a, pComp_image + (y * width + x) * comp_image_comps, luma_only, comp_image_comps); 122 int b[3]; get_pixel(b, pUncomp_image_data + (y * width + x) * uncomp_comps, luma_only, uncomp_comps); 123 for (uint c = 0; c < num_channels; c++) 124 hist[labs(a[first_channel + c] - b[first_channel + c])]++; 125 } 126 } 127 128 results.max_err = 0; 129 double sum = 0.0f, sum2 = 0.0f; 130 for (uint i = 0; i < 256; i++) 131 { 132 if (!hist[i]) 133 continue; 134 if (i > results.max_err) 135 results.max_err = i; 136 double x = i * hist[i]; 137 sum += x; 138 sum2 += i * x; 139 } 140 141 // See http://bmrc.berkeley.edu/courseware/cs294/fall97/assignment/psnr.html 142 double total_values = width * height; 143 144 results.mean = sum / total_values; 145 results.mean_squared = sum2 / total_values; 146 147 results.root_mean_squared = sqrt(results.mean_squared); 148 149 if (!results.root_mean_squared) 150 results.peak_snr = 1e+10f; 151 else 152 results.peak_snr = log10(255.0f / results.root_mean_squared) * 20.0f; 153 } 154 155 // Simple exhaustive test. Tries compressing/decompressing image using all supported quality, subsampling, and Huffman optimization settings. 156 static int exhausive_compression_test(const char *pSrc_filename, bool use_jpgd) 157 { 158 int status = EXIT_SUCCESS; 159 160 // Load the source image. 161 const int req_comps = 3; // request RGB image 162 int width = 0, height = 0, actual_comps = 0; 163 uint8 *pImage_data = stbi_load(pSrc_filename, &width, &height, &actual_comps, req_comps); 164 if (!pImage_data) 165 { 166 log_printf("Failed loading file \"%s\"!\n", pSrc_filename); 167 return EXIT_FAILURE; 168 } 169 170 log_printf("Source file: \"%s\" Image resolution: %ix%i Actual comps: %i\n", pSrc_filename, width, height, actual_comps); 171 172 int orig_buf_size = width * height * 3; // allocate a buffer that's hopefully big enough (this is way overkill for jpeg) 173 if (orig_buf_size < 1024) orig_buf_size = 1024; 174 void *pBuf = malloc(orig_buf_size); 175 176 uint8 *pUncomp_image_data = NULL; 177 178 double max_err = 0; 179 double lowest_psnr = 9e+9; 180 double threshold_psnr = 9e+9; 181 double threshold_max_err = 0.0f; 182 183 image_compare_results prev_results; 184 185 for (uint quality_factor = 1; quality_factor <= 100; quality_factor++) 186 { 187 for (uint subsampling = 0; subsampling <= jpge::H2V2; subsampling++) 188 { 189 for (uint optimize_huffman_tables = 0; optimize_huffman_tables <= 1; optimize_huffman_tables++) 190 { 191 // Fill in the compression parameter structure. 192 jpge::params params; 193 params.m_quality = quality_factor; 194 params.m_subsampling = static_cast<jpge::subsampling_t>(subsampling); 195 params.m_two_pass_flag = (optimize_huffman_tables != 0); 196 197 int comp_size = orig_buf_size; 198 if (!jpge::compress_image_to_jpeg_file_in_memory(pBuf, comp_size, width, height, req_comps, pImage_data, params)) 199 { 200 status = EXIT_FAILURE; 201 goto failure; 202 } 203 204 int uncomp_width = 0, uncomp_height = 0, uncomp_actual_comps = 0, uncomp_req_comps = 3; 205 free(pUncomp_image_data); 206 if (use_jpgd) 207 pUncomp_image_data = jpgd::decompress_jpeg_image_from_memory((const stbi_uc*)pBuf, comp_size, &uncomp_width, &uncomp_height, &uncomp_actual_comps, uncomp_req_comps); 208 else 209 pUncomp_image_data = stbi_load_from_memory((const stbi_uc*)pBuf, comp_size, &uncomp_width, &uncomp_height, &uncomp_actual_comps, uncomp_req_comps); 210 if (!pUncomp_image_data) 211 { 212 status = EXIT_FAILURE; 213 goto failure; 214 } 215 216 if ((uncomp_width != width) || (uncomp_height != height)) 217 { 218 status = EXIT_FAILURE; 219 goto failure; 220 } 221 222 image_compare_results results; 223 image_compare(results, width, height, pImage_data, req_comps, pUncomp_image_data, uncomp_req_comps, (params.m_subsampling == jpge::Y_ONLY) || (actual_comps == 1) || (uncomp_actual_comps == 1)); 224 //log_printf("Q: %3u, S: %u, O: %u, CompSize: %7u, Error Max: %3.3f, Mean: %3.3f, Mean^2: %5.3f, RMSE: %3.3f, PSNR: %3.3f\n", quality_factor, subsampling, optimize_huffman_tables, comp_size, results.max_err, results.mean, results.mean_squared, results.root_mean_squared, results.peak_snr); 225 log_printf("%3u, %u, %u, %7u, %3.3f, %3.3f, %5.3f, %3.3f, %3.3f\n", quality_factor, subsampling, optimize_huffman_tables, comp_size, results.max_err, results.mean, results.mean_squared, results.root_mean_squared, results.peak_snr); 226 if (results.max_err > max_err) max_err = results.max_err; 227 if (results.peak_snr < lowest_psnr) lowest_psnr = results.peak_snr; 228 229 if (quality_factor == 1) 230 { 231 if (results.peak_snr < threshold_psnr) 232 threshold_psnr = results.peak_snr; 233 if (results.max_err > threshold_max_err) 234 threshold_max_err = results.max_err; 235 } 236 else 237 { 238 // Couple empirically determined tests - worked OK on my test data set. 239 if ((results.peak_snr < (threshold_psnr - 3.0f)) || (results.peak_snr < 6.0f)) 240 { 241 status = EXIT_FAILURE; 242 goto failure; 243 } 244 if (optimize_huffman_tables) 245 { 246 if ((prev_results.max_err != results.max_err) || (prev_results.peak_snr != results.peak_snr)) 247 { 248 status = EXIT_FAILURE; 249 goto failure; 250 } 251 } 252 } 253 254 prev_results = results; 255 } 256 } 257 } 258 259 log_printf("Max error: %f Lowest PSNR: %f\n", max_err, lowest_psnr); 260 261 failure: 262 free(pImage_data); 263 free(pBuf); 264 free(pUncomp_image_data); 265 266 log_printf((status == EXIT_SUCCESS) ? "Success.\n" : "Exhaustive test failed!\n"); 267 return status; 268 } 269 270 // Test JPEG file decompression using jpgd.h 271 static int test_jpgd(const char *pSrc_filename, const char *pDst_filename) 272 { 273 // Load the source JPEG image. 274 const int req_comps = 3; // request RGB image 275 int width = 0, height = 0, actual_comps = 0; 276 277 timer tm; 278 tm.start(); 279 280 uint8 *pImage_data = jpgd::decompress_jpeg_image_from_file(pSrc_filename, &width, &height, &actual_comps, req_comps); 281 282 tm.stop(); 283 284 if (!pImage_data) 285 { 286 log_printf("Failed loading JPEG file \"%s\"!\n", pSrc_filename); 287 return EXIT_FAILURE; 288 } 289 290 log_printf("Source JPEG file: \"%s\", image resolution: %ix%i, actual comps: %i\n", pSrc_filename, width, height, actual_comps); 291 292 log_printf("Decompression time: %3.3fms\n", tm.get_elapsed_ms()); 293 294 if (!stbi_write_tga(pDst_filename, width, height, req_comps, pImage_data)) 295 { 296 log_printf("Failed writing image to file \"%s\"!\n", pDst_filename); 297 free(pImage_data); 298 return EXIT_FAILURE; 299 } 300 log_printf("Wrote decompressed image to TGA file \"%s\"\n", pDst_filename); 301 302 log_printf("Success.\n"); 303 304 free(pImage_data); 305 return EXIT_SUCCESS; 306 } 307 308 int main(int arg_c, char* ppArgs[]) 309 { 310 printf("jpge/jpgd example app\n"); 311 312 // Parse command line. 313 bool run_exhausive_test = false; 314 bool test_memory_compression = false; 315 bool optimize_huffman_tables = false; 316 int subsampling = -1; 317 char output_filename[256] = ""; 318 bool use_jpgd = true; 319 bool test_jpgd_decompression = false; 320 321 int arg_index = 1; 322 while ((arg_index < arg_c) && (ppArgs[arg_index][0] == '-')) 323 { 324 switch (tolower(ppArgs[arg_index][1])) 325 { 326 case 'd': 327 test_jpgd_decompression = true; 328 break; 329 case 'g': 330 strcpy_s(s_log_filename, sizeof(s_log_filename), &ppArgs[arg_index][2]); 331 break; 332 case 'x': 333 run_exhausive_test = true; 334 break; 335 case 'm': 336 test_memory_compression = true; 337 break; 338 case 'o': 339 optimize_huffman_tables = true; 340 break; 341 case 'l': 342 if (strcasecmp(&ppArgs[arg_index][1], "luma") == 0) 343 subsampling = jpge::Y_ONLY; 344 else 345 { 346 log_printf("Unrecognized option: %s\n", ppArgs[arg_index]); 347 return EXIT_FAILURE; 348 } 349 break; 350 case 'h': 351 if (strcasecmp(&ppArgs[arg_index][1], "h1v1") == 0) 352 subsampling = jpge::H1V1; 353 else if (strcasecmp(&ppArgs[arg_index][1], "h2v1") == 0) 354 subsampling = jpge::H2V1; 355 else if (strcasecmp(&ppArgs[arg_index][1], "h2v2") == 0) 356 subsampling = jpge::H2V2; 357 else 358 { 359 log_printf("Unrecognized subsampling: %s\n", ppArgs[arg_index]); 360 return EXIT_FAILURE; 361 } 362 break; 363 case 'w': 364 { 365 strcpy_s(output_filename, sizeof(output_filename), &ppArgs[arg_index][2]); 366 break; 367 } 368 case 's': 369 { 370 use_jpgd = false; 371 break; 372 } 373 default: 374 log_printf("Unrecognized option: %s\n", ppArgs[arg_index]); 375 return EXIT_FAILURE; 376 } 377 arg_index++; 378 } 379 380 if (run_exhausive_test) 381 { 382 if ((arg_c - arg_index) < 1) 383 { 384 log_printf("Not enough parameters (expected source file)\n"); 385 return print_usage(); 386 } 387 388 const char* pSrc_filename = ppArgs[arg_index++]; 389 return exhausive_compression_test(pSrc_filename, use_jpgd); 390 } 391 else if (test_jpgd_decompression) 392 { 393 if ((arg_c - arg_index) < 2) 394 { 395 log_printf("Not enough parameters (expected source and destination files)\n"); 396 return print_usage(); 397 } 398 399 const char* pSrc_filename = ppArgs[arg_index++]; 400 const char* pDst_filename = ppArgs[arg_index++]; 401 return test_jpgd(pSrc_filename, pDst_filename); 402 } 403 404 // Test jpge 405 if ((arg_c - arg_index) < 3) 406 { 407 log_printf("Not enough parameters (expected source file, dest file, quality factor to follow options)\n"); 408 return print_usage(); 409 } 410 411 const char* pSrc_filename = ppArgs[arg_index++]; 412 const char* pDst_filename = ppArgs[arg_index++]; 413 414 int quality_factor = atoi(ppArgs[arg_index++]); 415 if ((quality_factor < 1) || (quality_factor > 100)) 416 { 417 log_printf("Quality factor must range from 1-100!\n"); 418 return EXIT_FAILURE; 419 } 420 421 // Load the source image. 422 const int req_comps = 3; // request RGB image 423 int width = 0, height = 0, actual_comps = 0; 424 uint8 *pImage_data = stbi_load(pSrc_filename, &width, &height, &actual_comps, req_comps); 425 if (!pImage_data) 426 { 427 log_printf("Failed loading file \"%s\"!\n", pSrc_filename); 428 return EXIT_FAILURE; 429 } 430 431 log_printf("Source file: \"%s\", image resolution: %ix%i, actual comps: %i\n", pSrc_filename, width, height, actual_comps); 432 433 // Fill in the compression parameter structure. 434 jpge::params params; 435 params.m_quality = quality_factor; 436 params.m_subsampling = (subsampling < 0) ? ((actual_comps == 1) ? jpge::Y_ONLY : jpge::H2V2) : static_cast<jpge::subsampling_t>(subsampling); 437 params.m_two_pass_flag = optimize_huffman_tables; 438 439 log_printf("Writing JPEG image to file: %s\n", pDst_filename); 440 441 timer tm; 442 443 // Now create the JPEG file. 444 if (test_memory_compression) 445 { 446 int buf_size = width * height * 3; // allocate a buffer that's hopefully big enough (this is way overkill for jpeg) 447 if (buf_size < 1024) buf_size = 1024; 448 void *pBuf = malloc(buf_size); 449 450 tm.start(); 451 if (!jpge::compress_image_to_jpeg_file_in_memory(pBuf, buf_size, width, height, req_comps, pImage_data, params)) 452 { 453 log_printf("Failed creating JPEG data!\n"); 454 return EXIT_FAILURE; 455 } 456 tm.stop(); 457 458 FILE *pFile = fopen(pDst_filename, "wb"); 459 if (!pFile) 460 { 461 log_printf("Failed creating file \"%s\"!\n", pDst_filename); 462 return EXIT_FAILURE; 463 } 464 465 if (fwrite(pBuf, buf_size, 1, pFile) != 1) 466 { 467 log_printf("Failed writing to output file!\n"); 468 return EXIT_FAILURE; 469 } 470 471 if (fclose(pFile) == EOF) 472 { 473 log_printf("Failed writing to output file!\n"); 474 return EXIT_FAILURE; 475 } 476 } 477 else 478 { 479 tm.start(); 480 481 if (!jpge::compress_image_to_jpeg_file(pDst_filename, width, height, req_comps, pImage_data, params)) 482 { 483 log_printf("Failed writing to output file!\n"); 484 return EXIT_FAILURE; 485 } 486 tm.stop(); 487 } 488 489 double total_comp_time = tm.get_elapsed_ms(); 490 491 const uint comp_file_size = get_file_size(pDst_filename); 492 const uint total_pixels = width * height; 493 log_printf("Compressed file size: %u, bits/pixel: %3.3f\n", comp_file_size, (comp_file_size * 8.0f) / total_pixels); 494 495 // Now try loading the JPEG file using jpgd or stbi_image's JPEG decompressor. 496 int uncomp_width = 0, uncomp_height = 0, uncomp_actual_comps = 0, uncomp_req_comps = 3; 497 498 tm.start(); 499 uint8 *pUncomp_image_data; 500 if (use_jpgd) 501 pUncomp_image_data = jpgd::decompress_jpeg_image_from_file(pDst_filename, &uncomp_width, &uncomp_height, &uncomp_actual_comps, uncomp_req_comps); 502 else 503 pUncomp_image_data = stbi_load(pDst_filename, &uncomp_width, &uncomp_height, &uncomp_actual_comps, uncomp_req_comps); 504 505 double total_uncomp_time = tm.get_elapsed_ms(); 506 507 if (!pUncomp_image_data) 508 { 509 log_printf("Failed loading compressed image file \"%s\"!\n", pDst_filename); 510 return EXIT_FAILURE; 511 } 512 513 log_printf("Compression time: %3.3fms, Decompression time: %3.3fms\n", total_comp_time, total_uncomp_time); 514 515 // Write uncompressed image. 516 if (output_filename[0]) 517 stbi_write_tga(output_filename, uncomp_width, uncomp_height, uncomp_req_comps, pUncomp_image_data); 518 519 if ((uncomp_width != width) || (uncomp_height != height)) 520 { 521 log_printf("Loaded JPEG file has a different resolution than the original file!\n"); 522 return EXIT_FAILURE; 523 } 524 525 // Diff the original and compressed images. 526 image_compare_results results; 527 image_compare(results, width, height, pImage_data, req_comps, pUncomp_image_data, uncomp_req_comps, (params.m_subsampling == jpge::Y_ONLY) || (actual_comps == 1) || (uncomp_actual_comps == 1)); 528 log_printf("Error Max: %f, Mean: %f, Mean^2: %f, RMSE: %f, PSNR: %f\n", results.max_err, results.mean, results.mean_squared, results.root_mean_squared, results.peak_snr); 529 530 log_printf("Success.\n"); 531 532 return EXIT_SUCCESS; 533 }