独辟蹊径:逆推Krpano切图算法,实现在浏览器切多层级瓦片图
前言
- 趁着隔离梳理一下之前做的一个有用的功能:在浏览器中去切割多分辨率瓦片图
- 这是一个有趣的过程,跟我一起探索吧
- 阅读本文需具备前置知识:对krpano有所了解,如:使用krpano去开发全景
本着故弄玄虚的原则,最精彩的会放到最后揭晓,由浅入深,层层递进!
1.功能简介
- 减轻服务器压力,krpano切图比较消耗CPU和内存,我们团队的服务器曾经因为太多人同时切图导致卡顿、宕机
- 提升切图速度,在js切图速度会比后端快,前端切图与后端切图可以同时使用,这样切图速度可以快100%以上
- 无水印,krpano是需要花钱注册的,没有注册的情况下去切图会有无数水印,使用本工具可以解决这个问题
- 提升用户体验,立方体切图仅需要几秒钟,在移动端APP中,切图可以立马生成全景且仅保留在本地,点击保存的时候才上传到云端
- …
DEMO: https://irispro.github.io/krpanoJSToolDemo/dist/index.html
GitHub源码地址:https://github.com/IrisPro/KrpanoToolJS
NPM地址:https://www.npmjs.com/package/@krpano/js-tools
2.回顾krpano切图
- 在以往,咱们最常用的切图方式是使用krpanotools命令行工具在服务器切图,如果是手动切图的话,就会在本地使用 MAKE VTOUR (MULTIRES) droplet、MAKE VTOUR (NORMAL) droplet,在1.20开始使用krpano Tools应用程序可以进行可视化操作,除了切图外,还能方便还原全景图。
3.krpano切图最常用的方式
- 第一,普通切图,即立方体切图,将全景图切为上、下、左、右、前、后6张图。优点:切图速度快,占用存储少。缺点:场景启动时间不够快,放大模糊。
- 第二,多分辨率切图,跟瓦片地图原理类似。优点:启动速度快,图片清晰。缺点:占用存储较多,切图时间较久,一般用于航拍、风景等大范围场景的需求,室内样板间预览,几乎不太需要。比较著名的应用案例大家可以看看720云上的何同学六百万粉丝合影,这一个场景的图片超过了百万张。在地图领域中是必用的技术.(如此就能够根据不同的缩放等级来显示不同的清晰度的图像,这样的好处是如果要加载一张4k的全景图,不需要一次性就将整个全景图都加载进来,可以先加载一个缩放等级低的全景,然后当使用者进行缩放查看细节的时候再加载清晰度更高的图像,这样就可以明显提高加载速度,避免因为图片过大使得加载时间过长和不必要的流量浪费。不足之处就是需要为一张全景图额外准备不同清晰度的图片,增加了图片处理的工作量,也增加了图片存储的空间占用。)
4.瓦片地图金字塔模型
瓦片地图金字塔模型是一种多分辨率层次模型,从瓦片金字塔的底层到顶层,分辨率越来越低,但表示的地理范围不变。首先确定地图服务平台所要提供的缩放级别的数量N,把缩放级别最高、地图比例尺最大的地图图片作为金字塔的底层,即第0层,并对其进行分块,从地图图片的左上角开始,从左至右、从上到下进行切割,分割成相同大小(比如256x256像素)的正方形地图瓦片,形成第0层瓦片矩阵;在第0层地图图片的基础上,按每像素分割为2×2个像素的方法生成第1层地图图片,并对其进行分块,分割成与下一层相同大小的正方形地图瓦片,形成第1层瓦片矩阵;采用同样的方法生成第2层瓦片矩阵;…;如此下去,直到第N一1层,构成整个瓦片金字塔。
- 其实,krpano多分辨率就是借鉴这种原理。
正片开始
5.前置知识介绍:ImageData
-
ImageData是图片的数据化,保存了图片每个像素的信息,它有以下属性:
- data:包含图像隐藏像素的 Uint8ClampedArray 数组。如果数组没有给定,指定大小的黑色矩形图像将会被创建。
- width: 描述图片宽度
- height:描述图片高度
-
ImageData中的data,是一个数组,每四个元素描述一个像素,分别表示rgba,所以一张100x100px的图片,data的数组长度为 100 x 100 x 4 = 40000。我们平时用全景图渲染精度一般在10000点~20000点。
-
data数组会随着分辨率的提高指数级增长,如10000x10000的全景图与20000*20000的全景图,前者数组长度为4亿,后者16亿。所以,在处理ImageData的时候,如此复杂的计算,我们需要使用多线程技术web worker,否则会阻塞渲染进程。
-
canvas这个就不多介绍了,大家都懂。
6.普通切图(立方体切图)
其实切立方体图网上很多现成的方案,难点在于如何切瓦片图。
我使用了现成的方案,在我仓库地址中最底部有提及。
原理:将输入的图片使用canvas画出来,然后转为ImageData,通过球体转立方体的算法,将对应像素映射到每一个面上,最终再通过ImageData转回图片。
https://jaxry.github.io/panorama-to-cubemap/
demo中有三个选项:
- Liner(柔和的细节)
- Cubic(锋利的细节,我选择这一种,与krpano一致)
- Lanczos(画质最好,耗时是第二种的3.5倍,太耗时且结果肉眼感知不明显)
7.多分辨率瓦片图算法(重头戏)
01.通过krpano切图结果推理多分辨率切图高清的原因
一张全景图,可以切出几百上千张碎图,越放大就越清晰,并且初次缩放和旋转场景,可以看到控制台一直在加载图片。
首先,我们来看看krpano切出来的图片的目录结构:
(图一)多分辨率切图:
(图二)普通切图:
普通切图我们好理解。除了preview.jpg和thumb.jpg,其它以pano_开头的图片都代表立方体其中一个面。
通过对比,我猜测多分辨率每一个文件夹对应立方体每一个面。
为了探究这些碎图是什么东西,我打开Photoshop,将图一中文件夹b->l1里面文件夹的图片都放在画布中,如下图三所示:
图三:
紧接着,我把剩下l2、l3文件夹里面的所有图片,按照上文同样的操作,放在Photoshop中把图片合并,惊奇地发现l1、l2、l3这三个文件夹每个文件夹合并的图片都是一样的,除了分辨率不一样以外,分辨率等级:l3 > l2 > l1,层级越高分辨率越高。如下图所示:
02.小结krpano切图规律
- 每一面图片的多个文件夹(l1、l2、l3)代表多张不同分辨率的图片
- 文件夹名称l1、l2、l3,其中的英文字母l是level的缩写,数字代表图片的层级
- 每一层级里面的文件夹表示这张图片的第几行,按顺序把每一行都拼起来就可以变成一面完整的图
- 多分辨率瓦片图高清的原因:普通切图分辨率为2048x2048,而的分辨率切图最高分辨率可以达到3200x3200,分辨率越高肯定越清晰
03.算法思路推理与实现
小思路:
- 每一面的图片我们可以通过普通切图拿到
- 把每一面的图使用canvas转成不同分辨率的图片,然后逐行对它进行切割
问题:
- 一张全景图需要分多少层级?
- 每一层级的分辨率是多少?
- 每一张瓦片图的最大尺寸和最小尺寸是多少?
为了能找出规律,我制作了非常多不同分辨率的全景图,使用krpano Tools去切图,并根据输出记录不同分辨率的层级、每一层级的分辨率,试图找出他们的规律。
如图所示,这是krpano Tools 1.20.10:
从上图中可以发现,每次切图的时候控制台会输出几个参数:
- 全景图的分辨率
- 一共多少层级,如图中所示 levels=3,表示有3个层级
- 每一层级的分辨率,如图中所示,3200x3200 1664x1664 768x768