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)
;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 -1) do 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