质量监控-图片减包

文章链接

经过多个版本迭代,项目在release配置下的打包体积依旧轻松破百,应用体积过大导致的问题包括:

  • 更长的构建时间,换个词就是加班
  • TEXT段体积过大会导致审核失败
  • 用户不愿意下载应用

通常来说,资源文件能在应用体积包中占据1/3或者更多的体积,相比起代码(5kb/千行)的平均占用来说,对图片进行减包是最直接高效的手段,对图片资源的处理方式包括四种:

  1. 通过请求下载大图
  2. 使用工具压缩图片
  3. 查找删除重复图片
  4. 查找复用相似图片

考虑到由于项目开发分工的问题,方式1需要推动落地,所以本文不讨论这种处理方式。其他三种都能通过编写脚本实现自动化处理

图片压缩

图片压缩分为有损压缩无损压缩两类,有损压缩放弃了一部分图片的质量换取更高的压缩比。网上主流的压缩工具有tinypngpngquantImageAlphaImageOptim等,分别采用了一种或者多种压缩技术完成图片压缩

为什么png能够无损压缩

由于png格式的灵活性,同一张图片可以使用多种方式进行表示,不同方式占用的大小不一样。一般的软件会采用效率更高的方式来表示图片,所以这种情况下png图片存在巨大的优化空间。通常来说,从png文件中能去除的数据包括:

  • iTXttEXtzTXt这些可以存储任意文本的数据区段
  • iCCP数据区段存储的profile等等
  • photoshop导出的png图片存在大量的额外信息

png图片有两种类型的数据块,一种是必不可缺的数据块称为关键数据块。另一种叫做辅助数据块png文件格式规范指定的辅助数据块包括:

  • 背景颜色数据块bKGD
  • 基色和白色数据块cHRM
  • 图像γ数据块gAMA
  • 图像直方图数据块hIST
  • 物理像素尺寸数据块pHYs
  • 样本有效位数据块sBIT
  • 文本信息数据块tEXt
  • 图像最后修改时间数据块tIME
  • 图像透明数据块tRNS
  • 压缩文本数据块zTXt

其中tEXtzTXt数据段中存在的数据包括:

关键字
Title 图像名称
Author 图像作者
Description 图像说明
Copyright 版权声明
CreationTime 原图创作时间
Software 创作图像使用的软件
Disclaimer 弃权
Warning 图像内容警告
Source 创作图像使用的设备
Comment 注释信息

由上可见,辅助数据块在png文件中可能占据了极大的篇幅,正是这些数据块构成了png的无损压缩条件

tinypng

tinypng采用了一种称作Quantization的压缩技术,通过合并图片中相似的颜色,将24bit的图片文件压缩成8bit图片,同时去除图片中不必要的元数据,图片最高能达到70%以上的压缩率。截止文章完成之前,tinypng仅提供了线上压缩功能,暂未提供工具下载

pngquant

根据官方介绍,pngquant24bit以上的图片转换成8bit的保留透明度通道的压缩图片,压缩算法的压缩比非常显著,通常都能减少70%的大小。pngquant提供了命令行工具来完成解压任务:

pngquant --quality=0-100 imagepath

命令行更多调试参数可以在官网参阅

ImageAlpha

ImageAlpha是一个macOS系统下的有损图片压缩工具,内置了pngquantpngnq-s9等多个压缩工具,多数情况下通过将图片降至8bit来获取高压缩比。由于ImageAlpha的可视化界面无法批量处理图片,直接使用提供的命令工具可以实现批量压缩图片:

for file in $(ls $1); do
    imagepath=$1"/"$file
    if [ -d imagepath ]
    then
        /// 路径为文件夹
    else
        if [[ $file == *.png ]]
        then
            beforeSize=`ls -l $imagepath | awk '{print $5}'`
            /Applications/ImageAlpha.app/Contents/MacOS/pngquant $imagepath
            afterSize=`ls -l ${imagepath/.png/-fs8.png} | awk '{print $5}'`
            
            if [[ $afterSize -lt $beforeSize]]
            then
                mv ${imagepath/.png/-fs8.png} $imagepath
            fi
        fi
    fi
done

使用ImageAlpha需要注意两点:

  1. 压缩后的图片命名会自动添加-fs8后缀,需要使用mv命令实现替换
  2. 有损压缩会修改关键数据块,可能导致压缩图片尺寸增大,需要过滤

在使用有损压缩时需要注意单张png图片是可以被多次压缩的,但这会导致图片的清晰度和色彩都受到影响,不建议对图片超过一次以上的有损压缩

ImageOptim

ImageOptim是介绍的四种工具中唯一的无损压缩,它采用了包括去除exif信息重新排列像素存储方式等手段实现图片的压缩。无损代表着一张图片被ImageOptim压缩后,后续无法再次进行压缩,同时它的压缩比往往比不上其他的有损压缩方案,但最大程度上保证了图片的原始清晰度和色彩

for file in $(ls $1); do
    imagepath=$1"/"$file
    if [ -d imagepath ]
    then
        /// 路径为文件夹
    else
        if [[ $file == *.png ]]
        then
            /Applications/ImageOptim.app/Contents/MacOS/ImageOptim $imagepath
        fi
    fi
done

ImageOptim同样存在可视化的工具并且支持批量压缩图片

多方案对比

考虑到ImageAlpha几乎都是使用pngquant作为压缩工具,因此只列出三种压缩工具的对比:

原始尺寸 压缩工具 压缩后尺寸 压缩比
319.5KB tinypng 120.5KB 62%
319.5KB ImageAlpha-pngquant 395KB -24%
319.5KB ImageOptim 252KB 21%

测试图片采用qq聊天截图生成的pngtinypng压缩率非常高,而pngquant的表现不尽人意

删除重复图片

通常来说,出现重复图片的原因包括模块间需求开发没有打通或是缺少统一的图片命名规范。通过图片MD5摘要是识别重复图片的最快方法,以python为例,匹配重复图片的代码如下:

md5list = {}
for file in files:
    if os.path.isdir(file.path):
        continue
        
    md5obj = hashlib.md5()
    fd = open(file.path, 'rb')
    while True:
        buff = fd.read(2048)
        if not buff:
            break
        md5obj.update(buff)
    fd.close()
    
    filemd5 = str(md5obj.hexdigest()).lower()
    if filemd5 in md5list:
        md5list[filemd5].add(file.path)
    else:
        md5list[filemd5] = set([file.path])
        
for key in md5list:
    list = md5list[key]
    if len(list) > 1:
        print (list)

在遍历中以文件MD5字符串作为key,维护具备相同MD5的图片路径,最后遍历这个map查找存在一个以上路径的数组并且输出

寻找相似图片

相似图片在图片内容、色彩上都十分的接近,多数时间可以考虑复用这些图片,但相似图片的问题在于无法通过MD5直接匹配。为了确认两个图片是否相似,要使用简单的一个数学公式来帮忙查找:

方差。在概率论和统计学中,一个随机变量的方差描述的是它的离散程度,也就是该变量离其期望值的距离

举个例子,甲同学五次成绩分别是65, 69, 81, 89, 96,乙同学五次成绩是82, 80, 77, 81, 80,两个人平均成绩都是80,但是引入方差公式计算:

甲: ((65-80)^2 + (69-80)^2 + (81-80)^2 + (89-80)^2 + (96-80)^2) / 5 = 136.8
乙: ((82-80)^2 + (80-80)^2 + (77-80)^2 + (81-80)^2 + (80-80)^2) / 5 = 2.8

平均值相同的情况下,方差越大,说明数据偏离期望值的情况越严重。方差越接近的两个随机变量,他们的变化就越加趋同,获取方差代码如下:

def getVariance(nums):
    variance = 0
    average = sum(nums) / len(nums)
    for num in nums:
        variance += (num - average) * (num - average) / len(nums)
    return variance

因此将图片划分成连串的一维数据,以此计算出图片的方差,通过方差匹配可以实现一个简单的图片相似度判断工具,实现前还要注意两点:

  1. 图片RGB色彩值会导致方差的计算变得复杂,所以转成灰度图可以降低难度
  2. 不同尺寸需要缩放到相同尺寸进行计算

最终将图片转换成一维数据列表的代码如下:

def getAverageList(img):
    commonlength = 30
    img = cv2.resize(img, (commonlength, commonlength), interpolation=cv2.INTER_CUBIC)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    
    res = []
    for idx in range(commonlength):
        average = sum(gray[idx]) / len(gray[idx])
        res.append(average)

将图片转成灰度图后,仍然可能存在RGB色值不同但灰度值相同的情况导致判断失准,可以考虑两种方案提高算法的检测准确率:

  1. 在不修改以灰度值计算方差的方案下,构建以列平均像素值为单位的一维列表计算另一个方差,两个方差值一并做判断
  2. 摒弃灰度值方差方案,每一行分别生成RGB三种色彩平均值的一维列表,计算出三个方差进行匹配检测

效果

经过两轮图片减包处理后,整个项目资源产生的减包量约有20M,其中通过文中的三种手段产生的减包量在6.5M左右,整体上来看产出还是比较可观的

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349

推荐阅读更多精彩内容