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  }