暴力法调整 PNG 图像尺寸
原文链接: https://www.nv5geospatialsoftware.com/Learn/Blogs/Blog-Details/brute-force-png-image-sizing
11403 给这篇文章评分:
3.3
暴力法调整 PNG 图像尺寸
匿名作者 2016年6月30日,星期四
最近,我参与了一个项目,该项目的输出 .png 文件有非常严格的最大尺寸限制。起初这似乎不是什么大问题,直到我意识到在创建图像之前,要计算 .png 文件的最终大小极其困难。长话短说,这促使我创建了一种暴力方法,用于寻找输出图像的最佳尺寸。
顺便提一句,我知道你们在想什么,“既然这么困难,为什么不选择几个尺寸尝试一下,然后建立一个线性模型,这样以后就不用再处理这个问题了?”嗯,我试过了,但行不通。从图1和图2可以看出,这既不是简单的线性关系,也不是容易预测的关系。图1和图2中的蓝线代表了文件大小与图像像素数量的关系;而红线则是简单的线性回归。可以看出,数据点与趋势线存在明显的偏差。
![]()
图 1

图 2
回到手头的任务:暴力法。这个方法相对简单:
- 以图像的原始尺寸保存(如果它小于允许的最大尺寸,那么任务就完成了)。
- 将原始图像尺寸减小一半,并检查文件大小。
- 如果文件大于允许的大小,则取原始图像尺寸的四分之一,换句话说,就是取上一个计算出的过大值(即初始图像尺寸的50%)与上一个计算出的最小尺寸值(因为这是第一次迭代,最小的图像尺寸尚未计算,所以将其设置为零)之间距离的一半。
- 如果文件小于允许的大小,则取原始文件尺寸的四分之三,或者取上一个计算出的最大图像尺寸(在本例中是原始图像尺寸)与上一个计算出的最小图像尺寸(即原始尺寸的一半)之间距离的一半。
- 继续这种模式,每次取一个要么大于、要么小于刚刚计算的中间点的值。每次都要确保使用整数或长整型值来计算行数和样本数。
- 每次迭代后,检查是否已经测试过相同的图像尺寸,如果是,那么这就是最终的图像尺寸,并且已经找到了最优解。
下面的示例应该能更清楚地说明这个执行过程。
;启动应用程序
e = ENVI()
;打开输入文件
File = Filepath('qb_boulder_msi', Subdir=['data'], $
Root_Dir=e.Root_Dir)
Raster = e.OpenRaster(File)
;从 ENVI 任务目录中获取任务
Task = ENVITask('ISODATAClassification')
;定义输入
Task.Input_Raster = Raster
;运行任务
Task.Execute
;提取新的 ISODATA 栅格
ISORaster = Task.OUTPUT_RASTER
;获取元数据
metadata = ISORaster.Metadata
;获取新文件的信息
nb = ISORaster.nb
Raster_ns = ISORaster.ns
Raster_nl = ISORaster.nl
;设置输出文件的文件名
output_file = 'C:\Output\PNG_Test.png'
;设置期望的输出大小(字节)
output_size = 150000
;删除任何同名的旧文件
FILE_DELETE, output_file,/ALLOW_NONEXISTENT
;将图像保存为我们的初始测试
;从 ENVI 任务目录中获取任务
ERTP_Task = ENVITask('ExportRasterToPNG')
;定义输入
ERTP_Task.INPUT_RASTER = ISORaster
;定义输出
ERTP_Task.OUTPUT_URI = output_file
;运行任务
ERTP_Task.Execute
;关闭 PNG 栅格
ERTP_Task.Output_Raster.close
;获取新文件的大小
file_size = ((file_info(output_file)).size) * 1.
;为最大样本数和行数创建一个容器
max_ns = Raster_ns
max_nl = Raster_nl
;为当前样本数和行数创建一个容器
nl = Raster_nl
ns = Raster_ns
;为最小样本数和行数创建一个容器
min_ns = 0
min_nl = 0
;创建一个容器,用于存储图像右下角相对于最大图像尺寸的一维索引值
loc_1d = []
;检查以确保当前图像过大
if file_size gt output_size then begin
;检查相同的图像尺寸是否多次出现。如果是,则停止迭代
while ((where(loc_1d eq (nl * Raster_ns +ns)))[0] eq -1) do begin
;以 1D 形式记录图像的宽度和高度
loc_1d = [loc_1d, nl * Raster_ns + ns]
;决定哪一侧的值应该被分成两半
if (file_size gt output_size) then side = 'right'
if (file_size lt output_size) then side = 'left'
case side of
;图像过大,必须减小尺寸
'right' : begin
; 确定图像的新尺寸(即上限值减去当前值的一半)
ns = max_ns - ((max_ns - min_ns)* .5)
nl = max_nl - ((max_nl - min_nl)* .5)
; 删除旧图像
FILE_DELETE, output_file,/ALLOW_NONEXISTENT
; 缩小原始图像
; 从 ENVI 任务目录中获取任务
DRR_Task=ENVITask('DimensionsResampleRaster')
; 定义输入
DRR_Task.INPUT_RASTER =ISORaster
DRR_Task.DIMENSIONS=[ns,nl]
; 运行任务
DRR_Task.Execute
ns = DRR_Task.Output_Raster.ns
nl = DRR_Task.Output_Raster.nl
; 从 ENVI 任务目录中获取任务
ERTP_Task = ENVITask('ExportRasterToPNG')
; 定义输入
ERTP_Task.INPUT_RASTER =DRR_Task.Output_Raster
; 定义输出
ERTP_Task.OUTPUT_URI =output_file
; 运行任务
ERTP_Task.Execute
; 关闭重采样后的栅格
DRR_Task.Output_Raster.close
; 关闭 PNG 栅格
ERTP_Task.OUTPUT_Raster.close
; 获取新文件的大小
file_size = ((file_info(output_file)).size)* 1.
; 将新尺寸记录为上限
max_ns = ns
max_nl = nl
end
'left' : begin ; 太小
; 确定图像的新尺寸(即上限值减去当前值的1.5倍)
ns = ((max_ns - min_ns)* 1.5) + max_ns
nl = ((max_nl - min_nl)* 1.5 )+ max_nl
; 删除旧图像
FILE_DELETE, output_file,/ALLOW_NONEXISTENT
; 缩小原始图像
; 从 ENVI 任务目录中获取任务
DRR_Task=ENVITask('DimensionsResampleRaster')
; 定义输入
DRR_Task.INPUT_RASTER =ISORaster
DRR_Task.DIMENSIONS=[ns,nl]
; 运行任务
DRR_Task.Execute
; 记录新图像的尺寸
dims = size(test_img,/DIMENSIONS)
ns = DRR_Task.OUTPUT_RASTER.ns
nl = DRR_Task.OUTPUT_RASTER.nl
; 保存新图像并测试大小
; 从 ENVI 任务目录中获取任务
ERTP_Task = ENVITask('ExportRasterToPNG')
; 定义输入
ERTP_Task.INPUT_RASTER =DRR_Task.Output_Raster
; 定义输出
ERTP_Task.OUTPUT_URI =output_file
; 运行任务
ERTP_Task.Execute
; 关闭重采样后的栅格
DRR_Task.Output_Raster.close
; 关闭 PNG 栅格
ERTP_Task.OUTPUT_Raster.close
; 记录新文件的大小
file_size = ((file_info(output_file)).size)* 1.
; 将旧尺寸记录为下限
min_ns = max_ns
min_nl = max_nl
; 将新尺寸记录为上限
max_ns = ns
max_nl = nl
end
endcase
endwhile
endif
;关闭初始栅格
ISORaster.close