wds0117吧 关注:4贴子:507
  • 29回复贴,共1

【转】为什么安卓手机图片保存次数多了会变绿?

只看楼主收藏回复

1楼发个图
下面开始发转载内容


IP属地:河南来自Android客户端1楼2016-10-15 09:46回复
    作者: Lion Yang
    业余版概要:安卓的一个核心的部分的代码,为了优化执行速度进行了魔改,结果写错了代码。结果导致 JPG 图片压缩发绿、崩坏。与安卓上的应用无关,它们是受害者(
    专业版概要:问题出在 Android 提供的压缩图片接口上,准确的说是一个 Android 里一个叫做 Skia 的库上。而这个 bug 在 2016 年 4 月中旬被修复了,如果按照 Android 的发行来看,那就是从 Android 7 (Nougat) 开始才消除这个问题。
    (不是百度的阴谋。(认真)
    前言:刚才在社区里和 @StarBrilliant 等人一起研究,现在应该可以下一个精确的定论了。如他的答案所说,问题出在 RGB 色彩空间转换到 YUV 的时候。但问题不仅仅是精度下降,最大的问题是,错误的舍入(向下取整)。另外,JDCT_IFAST 方法会导致图片严重劣化:“格子状崩坏”、灰块、黑白块、画面粗糙,但是题目问的仅仅是变绿,就不在这上面浪费篇幅了。
    网页模拟 by @StarBrilliant :JPEGreen Simulator
    历史性的修复:Use libjpeg-turbo for YUV->RGB conversion in jpeg encoder · google/skia@c7d01d3 · GitHub


    IP属地:河南来自Android客户端2楼2016-10-15 09:48
    回复
      =================================
      # 是谁的锅?
      百度贴吧是最多人批评的,而且……出事的客户端仅仅是 Android 系统上的。
      我后来注意到 QQ 也有这个问题,特别是上传头像。以前一直不知道为什么有一些图稍微有点绿,以为是打开了新世界的大门(x
      后来做了一点微小的测试,注意到百度贴吧、QQ,都会用 Android 系统提供的接口:
      Bitmap.compress(Bitmap.CompressFormat.JPEG, quality, outputStream);
      看起来都很干净……难不成是系统的问题?
      我自己做了一个我这辈子写的第一个 Android 的小程序(我真不敢斗胆叫做 App),模仿一个正常的应用,反复 JPEG 压缩。发现还真是那么回事。顺便完善了一下,做成了“效果拔群的绿化器”。
      源代码已开放:terribleGreen/MainActivity.java at master · LionNatsu/terribleGreen · GitHub(开源许可证:Apache License Version 2.0,欢迎提供 PR)


      IP属地:河南来自Android客户端3楼2016-10-15 09:51
      回复
        =================================
        ## 假如我们是 Skia 开发者,如何修复这个问题?(阅读本节需要 C/C++ 常识)
        交回给 libjpeg-turbo 库自己来做色彩空间变换。这也正是本文开头提到的那个历史性的修复具体做的:把原本 Skia 库 YUV 转换代码全部删掉了,把这个过程留给整个过程最底层的 libjpeg-turbo 库自己来做,并且用默认的 JDCT_ISLOW 方法代替 JDCT_IFAST 方法,那么自然就没这个问题了。
        注:libjpeg-turbo 是个运用极其广泛的库。可以说,基本上电脑上手机上能见到的 JPEG 压缩的地方用的一般都是 libjpeg-turbo。(iOS 应该也是吧?我没有苹果设备抱歉……Adobe 公司的魔法可能是另一回事)
        如果不删除呢?自己捣鼓:
        * 本节所提到的代码以及示例图片可以在这里找到:GitHub - LionNatsu/greenError: Discover the reason how `terribleGreen`(my another repo.) works on Android.
        首先我们要模拟一个 Skia 的 libjpeg-turbo 操作(略),然后,在把图片递交给 libjpeg-turbo 之前,把色彩空间像 Skia 一样,做一个变换(矩阵数据完全与 Skia 相同)。
        我们所要做的修复就是,把运算改成能够对数字进行合理四舍五入的运算:
        int R=i[0], G=i[1], B=i[2];
        #if 1 // Shift or float-divide (shift in Skia)
        int Y = (R*CYR + G*CYG + B*CYB) >> CSHIFT;
        int U = (R*CUR + G*CUG + B*CUB) >> CSHIFT;
        int V = (R*CVR + G*CVG + B*CVB) >> CSHIFT;
        o[0] = Y;
        o[1] = U + 128;
        o[2] = V + 128;
        #else
        double Y = (R*CYR + G*CYG + B*CYB) / pow(2, CSHIFT);
        double U = (R*CUR + G*CUG + B*CUB) / pow(2, CSHIFT);
        double V = (R*CVR + G*CVG + B*CVB) / pow(2, CSHIFT);
        o[0] = round(Y);
        o[1] = round(U + 128);
        o[2] = round(V + 128);
        #endif
        这里我把原版操作和修正版操作都写在一起了,把 #if 1 改成 #if 0 即可切换。(为什么我要说这些= =)


        IP属地:河南来自Android客户端6楼2016-10-15 10:02
        回复
          示例:左边为原版 Lena 酱,右边均为压缩质量设置为 80%,重复 30 次。
          完全 Skia 原版效果(即 Android 的):8-bit 变换,移位除法,JDCT_IFAST 方法。
          画质严重劣化,色彩偏绿。①
          不辣眼睛修正效果:8-bit 变换,移位除法,JDCT_FLOAT 方法。
          可以看到关闭 JDCT_IFAST 之后画面细腻了。②
          继续修复舍入漏洞的效果:8-bit 变换,正常舍入的除法,JDCT_FLOAT 方法。
          可以看到色彩偏绿的问题被正确四舍五入修正了。③
          回归原版 libjpeg-turbo 的压缩效果(现在的新版 Android):16-bit 变换,正常舍入的除法,JDCT_FLOAT 方法。(其实原版是JDCT_ISLOW,但差别不大)
          比起 8-bit,少了很多“色斑”,因为精度高了,色彩分辨率更高,或者说颜色的层次更加细腻。④
          =======
          番外
          Q:为什么不用全身版 Lena (http://www.lenna.org/full/l_hires.jpg) 做示例图?
          A:……
          (二营长,你他娘的意大利炮呢?!)





          IP属地:河南来自Android客户端7楼2016-10-15 10:08
          收起回复
            =================================
            来一个小的总结,给非专业的旁友们看:
            图片变绿是安卓系统一直以来的问题,直到 Android 7 才修复。原因是安卓系统内部的一个核心部件的代码,为了优化手机上运行的速度——写错了 = =。
            2016.8.26, 21:54 发布
            2016.8.26, 22:32 修订:修正表述错误,高亮
            2016.8.26, 22:34 修订:添加 S.B. 的网页模拟工具地址
            2016.8.26, 23:05 修订:添加概要
            2016.8.26, 23:56 修订:同步示例代码
            2016.8.27, 00:38 修订:调整令人困惑的表述
            2016.8.27, 14:38 修订:订正错字
            2016.8.27, 23:29 修订:明确阐述各修复步骤的变化
            2016.8.27, 23:31 修订:该死的我漏了句号
            2016.8.30, 00:45 修订:对符号数移位的定性从“未定义的行为”修正为“由具体实现决定”


            IP属地:河南来自Android客户端8楼2016-10-15 10:11
            回复
              全文转载完
              下面艾特基友涨姿势
              @小小小小的斗鱼 @张纯兵你好 @月亮的小生活 @为理想往前冲 @江南初春暖


              IP属地:河南来自Android客户端9楼2016-10-15 10:14
              收起回复
                链接:
                http://www.zhihu.com/question/29355920


                IP属地:河南来自Android客户端10楼2016-10-15 10:14
                回复
                  看不懂


                  IP属地:广东来自Android客户端11楼2016-10-15 10:15
                  收起回复
                    我擦,转眼间就被吞了2楼和3楼


                    IP属地:河南来自Android客户端12楼2016-10-15 10:32
                    回复
                      你够了
                      123456789多少个字了。好了够了,来自月亮的客户端


                      IP属地:河北来自Android客户端13楼2016-10-15 15:33
                      收起回复