Sunday, June 5, 2016

Converting JPG image limited by desired final output file size.

Many of us need to convert images where we keep a maximum limit of file size. Suppose an online form requires jpg/jpeg file of less than 100KB. How can we achieve that? There are at-least two good solutions.


1. ImageMagick 6.9+

This is free, OpenSource and multiplatform application which is available on almost all latest popular Linux distros and can be downloaded on Windows, Mac. Download it and convert images like this command:

convert -define jpeg:extent=100KB input.png output.jpg


There can be some problem in older versions and jpg to jpg might not work. At-least this is what i encountered while converting from jpg to jpg using extent. ImageMagick 6.8.



2. IrfanView + RIOT plugin
Download IrfanView windows app and using jpg saver plugin we can choose target maximum file size.



Back to ImageMagick with developer's mind

Being a programmer i wanted to know how ImageMagick achieves target size based conversion i.e. "jpeg:extent=". Here is what i found in the code below from file ImageMagick-6.9.3-2/coders/jpeg.c

 option=GetImageOption(image_info,"jpeg:extent");  
  if (option != (const char *) NULL)  
   {  
    Image  
     *jpeg_image;  
    ImageInfo  
     *jpeg_info;  
    jpeg_info=CloneImageInfo(image_info);  
    jpeg_info->blob=NULL;  
    jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);  
    if (jpeg_image != (Image *) NULL)  
     {  
      MagickSizeType  
       extent;  
      size_t  
       maximum,  
       minimum;  
      /*  
       Search for compression quality that does not exceed image extent.  
      */  
      jpeg_image->quality=0;  
      extent=(MagickSizeType) SiPrefixToDoubleInterval(option,100.0);  
      (void) DeleteImageOption(jpeg_info,"jpeg:extent");  
      (void) DeleteImageArtifact(jpeg_image,"jpeg:extent");  
      maximum=image_info->quality;  
      if (maximum < 2)  
       maximum=101;  
      for (minimum=2; minimum < maximum; )  
      {  
       (void) AcquireUniqueFilename(jpeg_image->filename);  
       jpeg_image->quality=minimum+(maximum-minimum+1)/2;  
       (void) WriteJPEGImage(jpeg_info,jpeg_image);  
       if (GetBlobSize(jpeg_image) <= extent)  
        minimum=jpeg_image->quality+1;  
       else  
        maximum=jpeg_image->quality-1;  
       (void) RelinquishUniqueFileResource(jpeg_image->filename);  
      }  
      quality=(int) minimum-1;  
      jpeg_image=DestroyImage(jpeg_image);  
     }  
    jpeg_info=DestroyImageInfo(jpeg_info);  
   }  
  jpeg_set_quality(&jpeg_info,quality,TRUE); 


In this code a comment itself says "Search for quality which does not exceeds jpeg extent". I tried it on Linux to convert jpg-to-jpg but could not see it working. Months ago this year 2016, i published my Android image converter app powered by ImageMagick called KisImageConverter. However extent feature was causing crash in app. For while that feature has been disabled but even if i enable it will be very slow. Because the method is like hit-n-trial. 1 conversion is slow, doing multiple times will be very very slow on mobile CPUs.