V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
xiaoming1992
V2EX  ›  程序员

如何降低图片解码(Image Decode)时间?

  •  
  •   xiaoming1992 · 2019-12-24 21:03:02 +08:00 · 5543 次点击
    这是一个创建于 1800 天前的主题,其中的信息可能已经有所发展或是发生改变。

    伪代码如下:

    // 分别有 36 张不同的图片,尺寸均为 2000*1000
    const imgList1 = []
    const imgList2 = []
    const imgList3 = []
    
    function render() {
      // i 会变化
      ctx.drawImage(imgList1[i], ...params)
      ctx.drawImage(imgList2[i], ...params)
      ctx.drawImage(imgList3[i], ...params)
    
      window.requestAnimationFrame(render)
    }
    
    render()
    

    一个渲染循环大概 60ms,但是 Image Decode 就占据了 48ms,怎么能降低这个时间呢?明天上班上 demo 页。

    第 1 条附言  ·  2019-12-25 09:11:35 +08:00

    -- demo页 --

    -- 演示gif --

    由于解码过于费时,我现在做的hack是转的时候就将其他层的图片隐藏,只留下最底下一层,但是这样体验不好,不知道大家有什么好的办法吗?

    64 条回复    2021-08-26 00:51:07 +08:00
    zhs227
        1
    zhs227  
       2019-12-24 21:16:20 +08:00
    每次 decode 的都是相同的图片吗,如果是,可以先保存 decode 的结果
    dawn009
        2
    dawn009  
       2019-12-24 21:20:03 +08:00
    Decode 应当只在「从磁盘到内存」的过程中进行一次。不需要在循环中重复解码过程。
    xiaoming1992
        3
    xiaoming1992  
    OP
       2019-12-24 21:37:46 +08:00
    @zhs227 都是不同的图片。。。

    @dawn009 但问题是浏览器他就是重复解码了啊
    luozic
        4
    luozic  
       2019-12-24 21:41:01 +08:00 via iPhone
    听说过缓存和预加载么?
    dtysky
        5
    dtysky  
       2019-12-24 21:45:20 +08:00 via Android
    来用压缩纹理吧(逃
    111qqz
        6
    111qqz  
       2019-12-24 21:48:13 +08:00 via Android
    来用显卡解码吧(
    dawn009
        7
    dawn009  
       2019-12-24 21:57:29 +08:00
    @xiaoming1992 #3 imgList 里的 image sorece 是文件 URL 吗?
    试试先用 createImageBitmap 预读成 ImageBitmap,然后用这个当作 drawImage 的参数
    xiaoming1992
        8
    xiaoming1992  
    OP
       2019-12-24 22:20:58 +08:00
    @dawn009 createImageBitmap 确实没用过,但是看 MDN 兼容性一片红,不太敢用啊,imgList 里面是 new Image()出来的 png 图片。

    @luozic 不太清楚你说的缓存和预加载是什么意思

    @111qqz 前端如何使用显卡解码?

    @dtysky 简单查了一下,还没看懂压缩纹理是什么,明天仔细查查吧。

    谢谢各位
    sx90
        9
    sx90  
       2019-12-24 23:00:20 +08:00 via Android
    全部加载干嘛?

    显示器分辨率就这些

    一般壁纸网站,全加载小图,点击查看高清图,或下载

    地图网站,也是先 ip 定位,小图加载,拼接而成

    图片如果是固定,可以缓存,第一次慢,后面就快
    HTTP/Caching_FAQ (新人发不了网址,自己搜吧)

    不固定的,自动工具切成小图,再加载
    yixiang
        10
    yixiang  
       2019-12-24 23:07:45 +08:00
    这是……要用 canvas 放 40k 的视频?

    1. 不需要这么高的分辨率吧
    2. gif 了解一下?
    xiaoming1992
        11
    xiaoming1992  
    OP
       2019-12-24 23:25:21 +08:00 via Android
    @sx90
    @yixiang
    其实是环物展示,需要在大屏上展示,如果需要放大的话,2000*1000 还嫌小了呢。。。需要响应手动操作,所以 gif 可能不合适
    chuxiaonan
        12
    chuxiaonan  
       2019-12-25 00:26:53 +08:00
    提个想法 不知道适合不 off screen 渲染如何
    mxT52CRuqR6o5
        13
    mxT52CRuqR6o5  
       2019-12-25 09:12:45 +08:00 via Android
    展示机你们控制不了吗?兼容性一片红又无所谓,把浏览器升级就是了,又不是放到公网上提供个各种不同的用户使用
    其他方法可以试试转图片格式或转成视频
    xiaoming1992
        14
    xiaoming1992  
    OP
       2019-12-25 09:13:44 +08:00
    @zhs227 @dawn009 @luozic @dtysky @111qqz @dawn009 @sx90 @yixiang @chuxiaonan

    抱歉打扰了,附言中已更新了 demo 和演示 gif
    xiaoming1992
        15
    xiaoming1992  
    OP
       2019-12-25 09:14:34 +08:00
    @mxT52CRuqR6o5 大哥,就是放到公网上啊,要做 toC 的啊。。。
    mxT52CRuqR6o5
        16
    mxT52CRuqR6o5  
       2019-12-25 09:19:09 +08:00 via Android
    @xiaoming1992 你前面说放到大屏展示我就以为没有 toC 需求,你那个 demo 不是性能挺好的啊
    xiaoming1992
        17
    xiaoming1992  
    OP
       2019-12-25 09:44:06 +08:00 via Android
    @mxT52CRuqR6o5 我做了 hack,转的时候只绘一层,,性能勉强可以,但是体验就不好了
    Michaelssss
        18
    Michaelssss  
       2019-12-25 09:52:17 +08:00
    无非是空间换时间吧。。比方说你把所有的 image.decode()提前 cache,你本身是要避免 decode 这操作吧
    xiaoming1992
        19
    xiaoming1992  
    OP
       2019-12-25 09:55:46 +08:00 via Android
    @Michaelssss 由于大部分图片长时间不在视口中,decode 信息缓存不住,浏览器不时会清理掉
    mxT52CRuqR6o5
        20
    mxT52CRuqR6o5  
       2019-12-25 10:06:38 +08:00
    @xiaoming1992 体验不好是指开发体验还是用户体验?这个需求优化要么空间换时间,把所有图片都渲染出来,用 z-index 或 transform3d 控制最上面的展示图片(手机上可能会炸),要么尝试把所有图片合成视频
    janus77
        21
    janus77  
       2019-12-25 10:11:27 +08:00
    分块并缓存,多分辨率,不同缩放度使用不同分辨率
    具体的看一下百度地图就明白了
    CallMeReznov
        22
    CallMeReznov  
       2019-12-25 10:14:00 +08:00 via Android
    扔给 api?
    xiaoming1992
        23
    xiaoming1992  
    OP
       2019-12-25 10:36:24 +08:00 via Android
    @mxT52CRuqR6o5 @janus77 现在的性能瓶颈是在旋转的时候,同时需要绘制 3 层图片,我令其只绘一层,性能好了,但是用户转的时候只能看到一层,因此体验不好。


    @CallMeReznov 有这样的 api 吗?
    mxT52CRuqR6o5
        24
    mxT52CRuqR6o5  
       2019-12-25 10:46:38 +08:00 via Android
    @xiaoming1992 大概听明白了,你可以多开 3 个 canvas 叠一起,这样绘制底层图片时即使为了性能不去刷新上面两层图片也不至于把上面的图层清掉,雪碧图也是个思路(就是手机上可能会炸)
    mxT52CRuqR6o5
        25
    mxT52CRuqR6o5  
       2019-12-25 10:50:33 +08:00
    感觉可以把 3 个图层每个角度的图片 3 张图片合成 1 张图啊,如果上面图层元素的位置不变的话
    xiaoming1992
        26
    xiaoming1992  
    OP
       2019-12-25 11:24:08 +08:00 via Android
    @mxT52CRuqR6o5 本来就是三个 canvas 叠在一起,转的时候加载第二个“3 张图片”时需要解码,频道更换“3 张图片”导致解码耗时。而由于特殊原因,没办法将“3 张图片”合成为一张。
    mxT52CRuqR6o5
        27
    mxT52CRuqR6o5  
       2019-12-25 12:12:06 +08:00 via Android
    @xiaoming1992 雪碧图方案或 video 方案呢?
    xiaoming1992
        28
    xiaoming1992  
    OP
       2019-12-25 12:32:39 +08:00 via Android
    @mxT52CRuqR6o5 开始总没放在心上,总感觉雪碧图太大了,仔细一想,才 12000*6000,或许浏览器能抗住,晚些试试
    xiaoming1992
        29
    xiaoming1992  
    OP
       2019-12-25 12:36:29 +08:00 via Android
    @mxT52CRuqR6o5 video 应该不行吧?需要响应用户交互,video 的“播放到某个时刻”的 api 不太熟悉,好像有兼容性问题,况且还设计到缩放
    mxT52CRuqR6o5
        30
    mxT52CRuqR6o5  
       2019-12-25 13:14:43 +08:00 via Android
    @xiaoming1992 响应交互是 video 如果要播放声音,需要交互才能执行 element.play,你把 video 设成静音就不影响
    seakingii
        31
    seakingii  
       2019-12-25 14:26:43 +08:00
    @mxT52CRuqR6o5 他这个是要用鼠标控制旋转缩放的,就像一个游戏一样,不能使用视频来解决(如果光是固定的展示效果是可以用编程生成视频的方式)

    解码是真的没必要在 渲染循环里做,可以把解码结果缓存起来.
    zhw2590582
        32
    zhw2590582  
       2019-12-25 14:48:40 +08:00
    没看懂,是从 url 下载图片,然后把图片陆续插入到 canvas 里是吗?
    mxT52CRuqR6o5
        33
    mxT52CRuqR6o5  
       2019-12-25 14:56:57 +08:00 via Android
    @seakingii 我知道,我是想通过 js 控制 video 元素的进度来展示不同角度的图片,没实际调研过不知道效果如何
    seakingii
        34
    seakingii  
       2019-12-25 15:07:52 +08:00
    @mxT52CRuqR6o5 视频不能缩放啊,还讲什么效果...
    mxT52CRuqR6o5
        35
    mxT52CRuqR6o5  
       2019-12-25 15:35:04 +08:00
    @seakingii 你控制 video 的 css 属性不就能缩放了
    xiaoming1992
        36
    xiaoming1992  
    OP
       2019-12-25 15:42:22 +08:00 via Android
    @mxT52CRuqR6o5 大哥,我要缩放(滚动滚轮或者手指捏合缩放),可是视频不方便缩放啊,我需要交互,你却告诉我怎么样禁用交互。。。

    @seakingii 我也不想解码啊,是调用 drawImage 的时候浏览器被动解码啊。。。

    @zhw2590582 是的
    zhw2590582
        37
    zhw2590582  
       2019-12-25 15:48:41 +08:00
    假如是我做的话,还是考虑使用缩略图,像那些三维软件那样,当静止时就渲染大图,发生缩放或者转动时就使用缩略图,动作结束后再大图,而不是全程都是大图。
    zhw2590582
        38
    zhw2590582  
       2019-12-25 15:49:57 +08:00
    不过你图片量又不大,全部大图 60ms 我觉得可以接受
    xiaoming1992
        39
    xiaoming1992  
    OP
       2019-12-25 15:53:11 +08:00 via Android
    @zhw2590582 嗯嗯,缩略图是个方向,我试试,谢谢。
    xiaoming1992
        40
    xiaoming1992  
    OP
       2019-12-25 15:54:47 +08:00 via Android
    @zhw2590582 60ms 确实不大,但是当转速高了(20 帧每秒)之后,感觉就有些卡顿了。
    mxT52CRuqR6o5
        41
    mxT52CRuqR6o5  
       2019-12-25 16:08:05 +08:00 via Android
    @xiaoming1992 video 肯定可以缩放啊,控制 video 的样式属性就好了啊,你是哪里没想明白
    andyzhshg
        42
    andyzhshg  
       2019-12-25 16:26:57 +08:00
    准备一套小图资源,旋转的时候如果没有对应的大图就渲染小图,大图 load 完了在替换成大图。全景图的展示都是这个路数,算是最简化版的 LOD。
    ltq918
        43
    ltq918  
       2019-12-25 16:29:24 +08:00
    ctx.drawImage 在画布中载入全部图片,然后通过调整图片大小、位置或遮罩模式来显示要显示出来的图片,隐藏其他图片
    omph
        44
    omph  
       2019-12-25 16:30:03 +08:00
    贴图慢,像游戏一样建 3D 模型
    [3DCloud,照片建模的开创者!有照片,就有模型!]( http://www.3dcloud.cn/)
    我编不下去了
    ltq918
        45
    ltq918  
       2019-12-25 16:31:36 +08:00
    不在载入图片环节循环,在调整图片显示模式环节循环
    ltq918
        46
    ltq918  
       2019-12-25 16:35:01 +08:00
    用 threejs 模型不好弄吧
    mxT52CRuqR6o5
        47
    mxT52CRuqR6o5  
       2019-12-25 16:41:45 +08:00 via Android
    刚刚实测,设置 video 元素的 transform 样式就能控制缩放,设置 video 元素的 currentTime 属性就能控制展示第几张图片
    mxT52CRuqR6o5
        48
    mxT52CRuqR6o5  
       2019-12-25 16:46:26 +08:00 via Android
    刚查了一下浏览器似乎放不了带 alpha 通道的视频
    xiaoming1992
        49
    xiaoming1992  
    OP
       2019-12-25 17:28:07 +08:00
    @mxT52CRuqR6o5 currentTime 同样受不了每秒 20+次的切换,一样卡顿
    xiaoming1992
        50
    xiaoming1992  
    OP
       2019-12-25 17:40:22 +08:00
    @ltq918 #43 ctx.drawImage 在画布中载入全部图片,然后通过调整图片大小、位置或遮罩模式来显示要显示出来的图片,隐藏其他图片

    图片从隐藏到显示同样需要解码啊

    @mxT52CRuqR6o5 对于 video,一些操作(例如自动播放)属于敏感操作,不同浏览器、不同设备可能会有不同的执行策略,所以我个人不太喜欢 video。
    xiaoming1992
        51
    xiaoming1992  
    OP
       2019-12-25 17:44:03 +08:00
    @omph 说实话,看他们首页的 demo,就有点看不下去了 ToT
    mxT52CRuqR6o5
        52
    mxT52CRuqR6o5  
       2019-12-25 17:58:15 +08:00 via Android
    @xiaoming1992 给 video 个 mute 属性就没有限制了,各种各样的限制规则是限制有声视频的,用视频对网络很友好,因为能压得很小,要不要用你自己衡量就是了
    xiaoming1992
        53
    xiaoming1992  
    OP
       2019-12-25 18:08:58 +08:00
    @mxT52CRuqR6o5 相同清晰度,相同帧数,视频应该大于图片吧?我曾经碰到过微信(还是 QQ ?)内置浏览器,在 touchstart (或者 touchend ?)事件中调用 play()方法无效,部分浏览器在非 wifi 环境播放视频时会弹出“播放视频将消耗大量流量”的提示,所以个人不太喜欢 video。
    mxT52CRuqR6o5
        54
    mxT52CRuqR6o5  
       2019-12-25 18:14:00 +08:00 via Android
    @xiaoming1992 如果有国产各种魔改版的手机浏览器用 video 确实不太好
    相同帧数视频肯定是图片小的,所以 chrome 才会给静音视频开自动播放的口子。最开始没开这个口子时,有些网站会尝试使用 gif 来做自动播放,导致用户流量消耗更大体验更差
    ltq918
        55
    ltq918  
       2019-12-25 20:18:49 +08:00
    @xiaoming1992 对已载入的图片进行变换位移等操作似乎不会增加 Image Decode 时间
    ltq918
        56
    ltq918  
       2019-12-25 20:23:57 +08:00
    使用 setInterval()替代 window.requestAnimationFrame(render) 能进一步提高帧速率,但会影响效能
    xiaoming1992
        57
    xiaoming1992  
    OP
       2019-12-25 21:18:14 +08:00 via Android
    @ltq918 就算图片已经载入,只要不在视口,浏览器就很可能会清除掉 decode cache,好像某乎有篇文章介绍过,忘了。
    xiaoming1992
        58
    xiaoming1992  
    OP
       2019-12-25 21:19:38 +08:00 via Android
    @ltq918 setInterval 不是会掉帧还是怎么的吗?体验应该不会比 rAF 好啊
    Mutoo
        59
    Mutoo  
       2019-12-25 21:32:38 +08:00
    最佳作法是 webgl + frame buffer,把图像上传到显卡,需要的时候直接调用显卡的 draw call 显示出来。
    xiaoming1992
        60
    xiaoming1992  
    OP
       2019-12-25 22:11:02 +08:00 via Android
    @Mutoo 最开始用的就是 webgl,需要频繁切换 texture,比这个性能还差。。。或许是我 webgl 不精吧
    Mutoo
        61
    Mutoo  
       2019-12-25 23:17:13 +08:00 via iPhone
    @xiaoming1992 texture 的 frame buffer 只需要创建一次,之后只要重复调用 drawcall 就行了。后者开销几乎忽略不计。游戏引擎都是这么做的。
    xiaoming1992
        62
    xiaoming1992  
    OP
       2019-12-25 23:29:52 +08:00
    @Mutoo 感谢,那确实是我用错了,我是在循环中创建 texture 的,webgl 不熟,用错了,回头重新试试
    signalas1
        63
    signalas1  
       2021-08-24 16:32:16 +08:00
    最后怎么办了楼主
    xiaoming1992
        64
    xiaoming1992  
    OP
       2021-08-26 00:51:07 +08:00 via Android
    @signalas1 好像是采用的楼上的,页面加载时将需要的图片(缩略图和大图)全部加载完毕(浏览器会帮我们换存下来),旋转时用缩略图、停下后切换为大图。离职了,太长时间忘了。(其实这个事的瓶颈在于,高速切换图片时,浏览器需要将图片依次绘制到视口,即使图片有缓存,仍然会卡顿,图片越大,卡顿越明显。)
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1129 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 517ms · UTC 19:03 · PVG 03:03 · LAX 11:03 · JFK 14:03
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.