5.0

# Brute Force PNG Image Sizing

Austin Coates

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 .

a.       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).

b.      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)

;Define inputs

;Pull out the new ISOData raster

;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

;Define inputs

;Define outputs

;Close the PNG Raster

;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

; Define inputs

; Define inputs

; Define outputs

; Close the Resampled Raster

; Close the PNG raster

; 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

; Define inputs

; Record the dimensions of the new image

dims = size(test_img,/DIMENSIONS)

; Save the new image and test the size

; Define inputs

; Define outputs

; Close the Resampled Raster

; Close the PNG raster

; 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