# 图像

# 前言

前端加载超大图片时,一般可以采取以下措施实现加速:

  1. 图片压缩:将图片进行压缩可以大幅减小图片的大小,从而缩短加载时间。压缩图片时需要注意保持图片质量,以免影响图片显示效果。
  2. 图片分割:将超大图片分割成多个小图块进行加载,可以避免一次性加载整个图片,从而加快加载速度。这种方式需要在前端实现图片拼接,需要确保拼接后的图片无缝衔接。
  3. CDN 加速:使用 CDN(内容分发网络)可以将图片缓存在离用户更近的节点上,从而加速图片加载速度。如果需要加载的图片是静态资源,可以将其存储在 CDN 上,以便快速访问。
  4. 懒加载:懒加载是一种图片延迟加载的方式,即当用户浏览到需要加载的图片时才进行加载,可以有效避免一次性加载大量图片而导致页面加载速度缓慢。
  5. WebP 格式:使用 WebP 格式可以将图片大小减小到 JPEG 和 PNG 的一半以下,从而加快图片加载速度。
  6. HTTP/2:使用 HTTP/2 协议可以并行加载多个图片,从而加快页面加载速度。
  7. 预加载:预加载是在页面加载完毕后,提前加载下一步所需要的资源。在图片加载方面,可以在页面加载完毕后提前加载下一个需要显示的图片,以便用户快速浏览。

而对于几百M或上G的大图而言,不管对图片进行怎么优化或加速处理,要实现秒开也是不太可能的事情。而上面介绍的第二条“图像分割切片”是最佳解决方案。

唯杰地图支持常用的图像格式如tif;png;jpeg;tiff;hdr;avif;webp;jpe;jpc;jpg;heif;bmp;dib;heic;jfif;gif;zip;hif;pdf;等格式,通过上传图像,在后台进行图像处理后切片完成后,在前端实现秒开展示。

# 图像切片原理介绍

图像切片是指将一张大图分割成若干个小图的过程,以便于存储和处理。图像切片常用于网络地图、瓦片地图、图像拼接等应用中。

切片原理主要包括以下几个步骤:

  1. 定义切片大小:首先需要定义每个小图的大小,一般情况下是正方形或矩形。
  2. 计算切片数量:根据定义的切片大小,计算原始图像需要被切成多少个小图。计算公式为:切片数量 = 原始图像宽度 / 切片宽度 × 原始图像高度 / 切片高度。
  3. 切割图像:按照计算出的切片数量,将原始图像分割成相应数量的小图。可以使用图像处理库或自己编写代码实现。
  4. 存储切片:将切割后的小图存储到磁盘上,可以使用常见的图片格式,如JPEG、PNG等。
  5. 加载切片:在需要显示切片的地方,根据需要加载相应的小图,组合成完整的图像。

使用图像切片可以降低处理大图像的复杂度,同时也能够提高图像的加载速度,使得用户可以更快地查看图像的细节。图像切片广泛应用于需要处理大图像的场景,能够提高图像处理和显示效率,同时也能够提高用户的体验。

# 图像地图范围设置

一些影像地图通常会有图像对应的地图范围。在上传图像的同时,或上传图像后需要设置相应的地理范围。

如tiff图像,通常会有相应的地理范围tfw文件.

tiff/tfw, jpg/jpgw坐标文件的格式(6个参数) 0.030000

0.0000000000

0.0000000000

-0.030000

451510.875000

3358045.000000

以上每行对应的含义:

1 地图单元中的一个象素在X方向上的X分辨率尺度。

2 平移量。

3 旋转量。

4 地图单元中的一个象素在Y方向上的Y分辨率尺度的负值。

5 象素1,1(左上方)的X地坐标。

6 象素1,1(左上方)的Y地坐标。

在上传图时需要根据文件中的第一个,第五个和第六个值设置地图范围

image-20230319205612982

或者上传完后,操作菜单中点击设置地图范围进行设置

image-20230319205732187

# 图像处理

如果需要对上传的图像进行旋转,拼接等处理后,才进行切片的话,可以新建一个cmd.txt文件,把处理的命令写进文件中,然后和图像一起打包成zip上传。

如需要把1.jpg,2.jpg拼接成一个新的图片m1.png再打开,cmd.txt的写法如下:

join
1.jpg
2.jpg
m1.png
horizontal
1
2
3
4
5

再把1.jpg,2.jpg,cmd.txt三个文件打包成zip文件上传即可。

如原始图片为

image-20230319204505566

如需要把这三个文件拼接成一个文件,如进行如下操作

同目录下面新建 cmd.txt,内容为

join
0.jpg
1.jpg
m1.png
horizontal

join
m1.png
2.jpg
m3.png
horizontal
1
2
3
4
5
6
7
8
9
10
11

再把0.jpg、1.jpg、2.jpg、cmd.txt 打包压缩成 image.zip (image可定义命名) 上传 image.zip文件即可。

image-20230319204822323

cmd.txt 格式为:

图像处理命令名1
图像处理命令名1参数1
图像处理命令名1参数2

图像处理命令名2
图像处理命令名2参数1
图像处理命令名2参数2
图像处理命令名3参数3

图像处理命令名3
图像处理命令名3参数1
1
2
3
4
5
6
7
8
9
10
11

多个命令名之间用空白行分开

命名名和参数之间换行分开

# 图像处理常用命令及说明

# 旋转图像

rot
in - 输入图像,输入 图像文件名
out - 输出图像,输出 图像文件名
angle - 旋转图像的角度,默认值:d90 允许:d0、d90、d180、d270
1
2
3
4

示例,把图像1.jpg旋转90度。再进行切片处理

cmd.txt中可输入

rot
1.jpg
result.jpg
d90
1
2
3
4

其中result.jpg名称可随意命名(只能英文或数字)

把1.jpg和cmd.txt压缩成zip文件上传即可

# 裁剪图像,可以指定裁剪的左上角坐标、裁剪区域的宽度和高度等参数。

crop
in - 输入图像,输入 图像文件名
out - 输出图像,输出 图像文件名
int left
int top
int width
int height
1
2
3
4
5
6
7

# 缩放图像

resize
in - 输入图像,输入 图像文件名
out - 输出图像,输出 图像文件名
scale - 缩放的倍数 如2表示把图像放大2倍,0.5表示缩小一倍
1
2
3
4

# 图像翻转

flip
in - 输入图像,输入 图像文件名
out - 输出图像,输出 图像文件名
direction - 方向  horizontal 或 vertical
1
2
3
4

# 图像变换

affine
in - 输入图像,输入 图像文件名
out - 输出图像,输出 图像文件名
"2 0 0 1" 2x2 变换矩阵需要一个包含四个数字的数组。将数组作为空格分隔的列表传递
1
2
3
4

# 图像拼接

join
in - 输入图像,输入 图像文件名
in - 输入图像,输入 图像文件名
out - 输出图像,输出 图像文件名
direction - 拼接的方向  horizontal 或 vertical
1
2
3
4
5

# 图像阵列

用于将多个文件或通配符指定的文件合并成一个数组。该工具通常用于将多个图像拼接成一个大图像或将多个通道的图像合并成一个多通道图像。

arrayjoin
image1.jpg
image2.jpg
image3.jpg
...可以多个输入文件
output.jpg
1
2
3
4
5
6

# 图像格式支持后台配置

在config.json中map项中增加supportImageFormat

{
    "map" : {
      "supportImageFormat": "jpc;tif;tiff;png;jpg;jpeg;jpe;jfif;bmp;dib;gif;pdf;hdr;webp;heic;hif;heif;avif;zip",
      "supportImageFileMinSize": 0,
      "supportImageFileMaxSize": 0
    }
}
1
2
3
4
5
6
7

supportImageFormat为支持的文件类型,可修改或增加删除格式

supportImageFileMinSizesupportImageFileMaxSize为上传允许的最小和最大尺寸。为0表示不限制

修改完参数重启服务即可.

# 实现

# 先上效果图

imageslice.gif

# 上传打开图形

先上传大图,至后台进行切片处理, 上传相关代码为:

async onChangeFile(file) {
            try {
                message.info('文件上传中,请稍候...')
                this.isSelectFile = false;
                this.uploadMapResult = await svc.uploadMap(file.raw);
                if (this.uploadMapResult.error) {
                    message.error('上传图形失败!' + this.uploadMapResult.error)
                    return
                }
                this.form.mapid = this.uploadMapResult.mapid;
                this.form.uploadname = this.uploadMapResult.uploadname;
                this.maptype = this.uploadMapResult.maptype || '';
                this.dialogVisible = true;
            } catch (error) {
                console.error(error);
                message.error('上传图形失败!', error)
            }
        }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

如果需要上传后对图像进行处理,可以新建一个cmd.txt文件,把处理的命令写进文件中,然后和图像一起打包成zip上传。

如需要把1.jpg,2.jpg拼接成一个新的图片m1.png再打开,cmd.txt的写法如下:

join
1.jpg
2.jpg
m1.png
horizontal
1
2
3
4
5

再把1.jpg,2.jpg,cmd.txt三个文件打包成zip文件上传即可

打开图像相关代码

async onOpenMap() {
            try {
                let mapid = this.form.mapid;
                let param = {
                    ...this.uploadMapResult,
                    // 图名称
                    mapid: this.form.mapid,
                    // 上传完返回的fileid
                    fileid: this.uploadMapResult.fileid,
                    // 上传完返回的文件名
                    uploadname: this.form.uploadname,
                    // 地图打开方式
                    mapopenway: this.form.openway === "直接打开图形" ? vjmap.MapOpenWay.Memory : vjmap.MapOpenWay.GeomRender,
                    // 如果要密码访问的话,设置秘钥值
                    secretKey: this.form.isPasswordProtection ? svc.pwdToSecretKey(this.form.password) : undefined,
                    style: vjmap.openMapDarkStyle(),// div为深色背景颜色时,这里也传深色背景样式
                    // 图像类型设置地图左上角坐标和分辨率
                    imageLeft: this.form.imageLeft ? +this.form.imageLeft : undefined,
                    imageTop: this.form.imageTop ? +this.form.imageTop : undefined,
                    imageResolution: this.form.imageResolution ? +this.form.imageResolution : undefined,
                }
                let isVectorStyle = this.form.openway === "存储后渲染矢量";
                await openMap(param, isVectorStyle);
            } catch (error) {
                console.error(error);
                message.error('打开图形失败!', error)
            }
        }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 应用案例

# 应用一 对图像进行拼接前端查看

原始图片为

image-20230319204505566

image-20230319204822323

最终效果为:

image-20230319204924823

体验地址: https://vjmap.com/app/cloud/#/map/t3c93d14ff09?version=v1&mapopenway=GeomRender&vector=false (opens new window)

# 应用二 对tiff影像进行切片并与CAD图叠加校准

对tiff影像上传时可设置地理坐标范围。

tiff/tfw, jpg/jpgw坐标文件的格式(6个参数) 0.030000 0.0000000000 0.0000000000 -0.030000 451510.875000 3358045.000000

以上每行对应的含义:

1 地图单元中的一个象素在X方向上的X分辨率尺度。 2 平移量。 3 旋转量。 4 地图单元中的一个象素在Y方向上的Y分辨率尺度的负值。 5 象素1,1(左上方)的X地坐标。 6 象素1,1(左上方)的Y地坐标。

在上传图时需要根据文件中的第一个,第五个和第六个值设置地图范围

image-20230319205612982

或者上传完后,操作菜单中点击设置地图范围进行设置

image-20230319205732187

影像地图切片完成后,可与CAD图进行叠加校准。效果如下

image-20230319205945507

体验地址: https://vjmap.com/demo/#/demo/map/comprehensive/04imagecadmap (opens new window)

# imageproc代码开源

main.go

package main

import (
    "fmt"
    "os"
    "os/exec"
)

func main() {
    // 要执行的exe程序的路径
    cmdPath := os.Args[1]
    // 传递给程序的参数
    args := os.Args[2:]

    // 执行命令
    cmd := exec.Command(cmdPath, args...)
    output, err := cmd.CombinedOutput()
    if err != nil {
        fmt.Println("ERROR_MSG:", err.Error(), string(output))
    } else {
        // 输出结果
        fmt.Println(string(output))
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25