X
9371 Rate this article:
3.3

Brute Force PNG Image Sizing

Anonym

Recently, I was working on a project that had some pretty strict requirements for the max size of the output .png file. This did not seem like a big deal initially, until I realized that it is extremely difficult to calculate the final size of a .png file prior to creating the image. Long story short, this led me to creating a brute force method for finding the optimal size of an output image.

As a side note, I know what you are thinking, “if it is so difficult, why don’t you just choose a couple of sizes, try those out and then create a linear model so that in the future you no longer have to deal with this?”  Well I tried it and it was a no-go. As you can see from figures 1 and 2 it is not a simple linear relationship, nor is it an easily predictable relationship. The blue lines in figures 1 and 2 represent the file’s size as compared to the number of pixels in the image; whereas the red line is a simple linear regression. It is possible to see a pretty distinct deviation from the trend line.

Figure 1

Figure 2

Back to the task at hand: the brute force method. The method is relatively simple:

  1. Save the image at its native size (if it is smaller than the max allowable size, you are done).
  2. Reduce the original image size by one half and check the file size.
    • If it is larger than the allowable size, take one quarter of the original image dimensions, or in other words, one half of the distance between the last computed value that was too large (i.e. 50% of the initial image size) and the last smallest computed value (the smallest image size has not been computed yet, since this is the first iteration so just set it to zero).
    • If it is smaller than the allowable size, take three quarters of the original file size, or one half the distance between the last largest image dimensions computed (in this case the original image dimensions) and the last smallest image dimensions computed (i.e. one half the original).
  3. Continue the pattern of taking one of the values either greater than or less than the halfway point that was just computed. Each time, make sure that the number of lines and samples is being computed using either integer or long values.
  4. After each iteration, check to see if the same image dimensions have already been tested and if so, that is your final image size and you have found the optimal solution.

The example below should shed a little bit more light on how this is being performed.

 ;Start the application

 e = ENVI()

 

 ;Open an input file

 File = Filepath('qb_boulder_msi', Subdir=['data'], $

   Root_Dir=e.Root_Dir)

 Raster = e.OpenRaster(File)

 

 ;Get the  task from the catalog ofENVITasks

 Task = ENVITask('ISODATAClassification')

 

 ;Define inputs

 Task.Input_Raster = Raster

 

 ;Run the task

 Task.Execute

 

 ;Pull out the new ISOData raster

 ISORaster = Task.OUTPUT_RASTER

 

 ;get the metadata

 metadata = ISORaster.Metadata

 

 ;get the information about the new file

 nb = ISORaster.nb

 Raster_ns = ISORaster.ns

 Raster_nl = ISORaster.nl

 

 ;set the filename for the output file

 output_file = 'C:\Output\PNG_Test.png'

 

 ;Set the desired output size in bytes

 output_size = 150000

 

 ;delete any old files with the same name

 FILE_DELETE, output_file,/ALLOW_NONEXISTENT

 

 ;Save the image out as our initial test

 

 ;Get the task from the catalog of ENVITasks

 ERTP_Task = ENVITask('ExportRasterToPNG')

 

 ;Define inputs

 ERTP_Task.INPUT_RASTER = ISORaster

 

 ;Define outputs

 ERTP_Task.OUTPUT_URI = output_file

 

 ;Run the task

 ERTP_Task.Execute

 

 ;Close the PNG Raster

 ERTP_Task.Output_Raster.close

 

 ;Get the new File Size

 file_size = ((file_info(output_file)).size) * 1.

 

 ;create a container for the maximum number of samples and lines

 max_ns = Raster_ns

 max_nl = Raster_nl

 

 ;create a container for the current number of samples and lines

 nl = Raster_nl

 ns = Raster_ns

 

 ;create a container for the minimum number of samples and lines

 min_ns = 0

 min_nl = 0

 

 ;Create a container for the 1D index value of the lower right corner

 ;of the image with regards to the maximum image size

 loc_1d = []

 

 ;check to make sure the image is currently too large

 if file_size gt output_size then begin

 

   ;check to see if the same image size has come up more than once.  if so

   ;then stop iterating

   while ((where(loc_1d eq (nl * Raster_ns +ns)))[0] eq -1do begin

 

     ;record the image width and height in 1D

     loc_1d = [loc_1d, nl * Raster_ns + ns]

 

     ;decide which side of the value should be divided in half

     if (file_size gt output_size) then side = 'right'

     if (file_size lt output_size) then side = 'left'

 

     case side of

 

       ;the image is too large and must be reduced in size

       'right' : begin

 

          ; determine the new size of the image (i.e.1/2 of the upper bound minus the current size)

          ns = max_ns - ((max_ns - min_ns)* .5)

          nl = max_nl - ((max_nl - min_nl)* .5)

 

          ; Delete the old image

          FILE_DELETE, output_file,/ALLOW_NONEXISTENT

 

          ; Shrink the original image

 

          ; Get the task from the catalog ofENVITasks

          DRR_Task=ENVITask('DimensionsResampleRaster')

 

          ; Define inputs

          DRR_Task.INPUT_RASTER =ISORaster

          DRR_Task.DIMENSIONS=[ns,nl]

 

          ; Run the task

          DRR_Task.Execute

 

          ns = DRR_Task.Output_Raster.ns

          nl = DRR_Task.Output_Raster.nl

 

          ; Get the task from the catalog ofENVITasks

          ERTP_Task = ENVITask('ExportRasterToPNG')

 

          ; Define inputs

          ERTP_Task.INPUT_RASTER =DRR_Task.Output_Raster

 

          ; Define outputs

          ERTP_Task.OUTPUT_URI =output_file

 

          ; Run the task

          ERTP_Task.Execute

 

          ; Close the Resampled Raster

          DRR_Task.Output_Raster.close

 

          ; Close the PNG raster

          ERTP_Task.OUTPUT_Raster.close

 

          ; Get the new File Size

          file_size = ((file_info(output_file)).size)* 1.

 

          ; Record the new dimensions as the upperbound

          max_ns = ns

          max_nl = nl

 

       end

 

       'left' : begin ; too small

 

          ; determine the new size of the image (i.e.1.5 of the upper bound minus the current size)

          ns = ((max_ns - min_ns)* 1.5) + max_ns

          nl = ((max_nl - min_nl)* 1.5 )+ max_nl

 

          ; Delete the old image

          FILE_DELETE, output_file,/ALLOW_NONEXISTENT

 

          ; Shrink the original image

 

          ; Get the task from the catalog ofENVITasks

          DRR_Task=ENVITask('DimensionsResampleRaster')

 

          ; Define inputs

          DRR_Task.INPUT_RASTER =ISORaster

          DRR_Task.DIMENSIONS=[ns,nl]

 

          ; Run the task

          DRR_Task.Execute

 

          ; Record the dimensions of the new image

          dims = size(test_img,/DIMENSIONS)

          ns = DRR_Task.OUTPUT_RASTER.ns

          nl = DRR_Task.OUTPUT_RASTER.nl

          ; Save the new image and test the size

 

          ; Get the task from the catalog ofENVITasks

          ERTP_Task = ENVITask('ExportRasterToPNG')

 

          ; Define inputs

          ERTP_Task.INPUT_RASTER =DRR_Task.Output_Raster

 

          ; Define outputs

          ERTP_Task.OUTPUT_URI =output_file

 

          ; Run the task

          ERTP_Task.Execute

 

          ; Close the Resampled Raster

          DRR_Task.Output_Raster.close

 

          ; Close the PNG raster

          ERTP_Task.OUTPUT_Raster.close

 

          ; Record the new file size

          file_size = ((file_info(output_file)).size)* 1.

 

          ; Record the old dimension as the lowerbound

          min_ns = max_ns

          min_nl = max_nl

 

          ; Record the new dimension as the upperbound

          max_ns = ns

          max_nl = nl

 

       end

     endcase

   endwhile

 endif

 

 ;Close the intial raster

  ISORaster.close