基于轻量级神经网络GhostNet开发构建CIFAR100数据集场景下的图像识别分析系统,对比不同分辨路尺度下模型的性能情况

Cifar100数据集是一个经典的图像分类数据集,常用于计算机视觉领域的研究和算法测试。以下是关于Cifar100数据集的详细介绍:

  1. 数据集构成:Cifar100数据集包含60000张训练图像和10000张测试图像。其中,训练图像分为100个类别,每个类别有600张图像;测试图像也分为100个类别,每个类别有100张图像。这些图像都是32x32像素的彩色图像,包含RGB三个通道。
  2. 类别划分:Cifar100数据集的100个类别被划分为20个大类别,每个大类别包含5个小类别。这些类别涵盖了动物、植物、交通工具、日常生活用品、自然景观、人物、体育、食物、艺术等多个领域。例如,动物类别包括鸟类、鱼类、爬行动物、哺乳动物等;植物类别包括树木、花卉、农作物等;交通工具类别包括汽车、火车、飞机、船等。
  3. 层次结构:Cifar100数据集的层次结构使得数据集更加丰富,包含了各种各样的对象和场景。这种层次结构有助于研究人员更好地理解数据集,并设计出更有效的算法来处理图像分类任务。
  4. 应用场景:Cifar100数据集常用于评估和比较各种图像分类算法的性能。研究人员可以使用该数据集来训练图像分类器,并测试其在不同类别上的识别准确率。此外,Cifar100数据集还可以用于开展图像生成和图像翻译等任务,如训练生成对抗网络(GAN)来生成逼真的图像。
  5. 数据集下载与预处理:Cifar100数据集可以通过官方渠道进行下载。下载后,需要对数据集进行预处理,包括加载图像、划分训练集和测试集、数据增强等操作。在Python中,可以使用pickle模块来加载Cifar100数据集,并使用numpy等库来处理图像数据。

Cifar100数据集是一个非常重要的图像分类数据集,具有广泛的应用场景和丰富的类别划分。通过使用Cifar100数据集,研究人员可以开发出更准确、更强大的图像分类算法,从而推动计算机视觉技术的发展。本文的主要目的就是想要基于Cifar100数据集来应用实践分析轻量级网络模型的性能。

首先看下实例效果:

由于CIFAR-100的完整类别列表较长,这里只给出部分类别实例,如下所示:

label_dict = {  'aquatic mammals': '水生哺乳动物',  # 超类示例  'beaver': '河狸',                   # 水生哺乳动物超类下的类别  'dolphin': '海豚',                  # 水生哺乳动物超类下的类别  'fish': '鱼',                       # 超类示例(直接作为类别)  'shark': '鲨鱼',                    # 鱼类超类(或作为独立类别)下的类别  'flowers': '花',                    # 超类示例  'tulip': '郁金香',                  # 花卉超类下的类别  'sunflower': '向日葵',              # 花卉超类下的类别  'food containers': '食物容器',      # 超类示例  'bowl': '碗',                       # 食物容器超类下的类别  'cup': '杯子',                      # 食物容器超类下的类别  'household electrical devices': '家用电气设备',  # 超类示例  'keyboard': '键盘',                 # 家用电气设备超类下的类别  'mouse': '鼠标',                    # 注意:这里的“mouse”指的是计算机鼠标,而非动物  'insect': '昆虫',                   # 超类(或作为独立类别)示例  'bee': '蜜蜂',                      # 昆虫超类下的类别  'butterfly': '蝴蝶',                # 昆虫超类下的类别  # ...(其他类别)  
}

数据集官方网站在这里,如下所示:

这里给出来CIFAR-10-dataset和CIFAR-100-dataset两个数据集的下载地址。

CIFAR-10-dataset,如下所示:

CIFAR-100-dataset,如下所示:

官方提供的数据集不是图像形式的,这里可以直接加载使用,实例实现如下所示:

CIFAR_PATH = "自己的路径"
mean = [0.5070751592371323, 0.48654887331495095, 0.4409178433670343]
std = [0.2673342858792401, 0.2564384629170883, 0.27615047132568404]
num_workers= 2def cifar100_dataset(args):transform_train = transforms.Compose([transforms.RandomCrop(32, padding=4),transforms.RandomHorizontalFlip(),transforms.RandomRotation(15),  # 数据增强transforms.ToTensor(),transforms.Normalize(mean, std)])transform_test = transforms.Compose([transforms.ToTensor(),transforms.Normalize(mean, std)])cifar100_training = torchvision.datasets.CIFAR100(root=CIFAR_PATH, train=True, download=True, transform=transform_train)trainloader = torch.utils.data.DataLoader(cifar100_training, batch_size=args.bs, shuffle=True, num_workers=num_workers)cifar100_testing = torchvision.datasets.CIFAR100(root=CIFAR_PATH, train=False, download=True, transform=transform_test)testloader = torch.utils.data.DataLoader(cifar100_testing, batch_size=100, shuffle=False, num_workers=num_workers)return trainloader,testloader

也可以选择将官方的数据集转化为图像的形式,之后再按照图像分类模型的标准做法来进行模型的开发训练。代码实现如下所示:

import os
import pickle
import numpy as np
from tqdm import trange
from os.path import join
from PIL import Imagedef createDir(path):if not os.path.exists(path):os.makedirs(path)def unpickle(file):with open(file, "rb") as fo:dict = pickle.load(fo, encoding="latin1")return dict# settings
src_dir = "./cifar-100-python"
dst_dir = "./cifar100"if __name__ == "__main__":meta = unpickle(join(src_dir, "meta"))createDir(dst_dir)for data_set in ["train", "test"]:print("Unpickling {} dataset......".format(data_set))data_dict = unpickle(join(src_dir, data_set))createDir(join(dst_dir, data_set))for fine_label_name in meta["fine_label_names"]:createDir(join(dst_dir, data_set, fine_label_name))for i in trange(data_dict["data"].shape[0]):img = np.reshape(data_dict["data"][i], (3, 32, 32))i0 = Image.fromarray(img[0])i1 = Image.fromarray(img[1])i2 = Image.fromarray(img[2])img = Image.merge("RGB", (i0, i1, i2))img.save(join(dst_dir,data_set,meta["fine_label_names"][data_dict["fine_labels"][i]],data_dict["filenames"][i],))

处理完成之后我们就得到了可用的图像数据集了,实例数据如下所示:

可以看到:官方的数据集都是标准的32x32的尺寸,整体来说分辨率还是比较小的:

以下是一些常用的轻量级卷积神经网络模型:

MobileNet:MobileNet是一种基于深度可分离卷积的轻量级模型,通过Depthwise Separable Convolution减少参数量和计算量,适用于移动设备上的图像分类和目标检测。

ShuffleNet:ShuffleNet通过使用通道洗牌操作来减少参数量和计算量。它采用逐点卷积和组卷积,将通道分为组并进行特征图的混洗,以增加特征的多样性。

EfficientNet:EfficientNet是一系列模型,通过使用复合缩放方法在深度、宽度和分辨率上进行均衡扩展。它在减少参数和计算量的同时,保持高准确性。

MobileNetV3:MobileNetV3是MobileNet的改进版本,引入了候选网络和网络搜索方法,通过优化模型结构和激活函数,进一步提升了轻量级模型的性能。

ProxylessNAS:ProxylessNAS是使用神经网络搜索算法来自动搜索轻量级模型结构的方法。它通过替代器生成网络中的每个操作,以有效地搜索高效的模型结构。

SqueezeNet:SqueezeNet是一种极小化的卷积神经网络模型,使用Fire模块将降维卷积和扩展卷积组合在一起,以减少参数量和计算量。

这些轻量级模型在参数量和计算量上相对较少,适用于资源受限的设备或场景。然而,每个模型都有不同的性能和特点,根据应用需求和资源限制,选择合适的模型进行使用。同时,还可以根据具体任务的要求进行模型的调整和优化。

GhostNet是一种轻量级的卷积神经网络模型,旨在在计算资源有限的设备上实现高效的图像分类和目标检测。其主要原理是通过使用Ghost Module来减少参数量和计算量,并提高模型在资源受限条件下的性能。

Ghost Module是GhostNet的关键组成部分,其主要思想是通过将一个普通的卷积层分解为两个部分:主要卷积(或称为Ghost指示器)和辅助卷积。具体构建原理如下:

主要卷积(Ghost指示器):该部分包含少量的输出通道数(称为精简通道),可以看作是对原始卷积的一种降维表示。它对输入进行低维特征提取,并通过学习有效的过滤器来减少参数量和计算量。

辅助卷积:该部分包含更多的输出通道数(称为扩展通道),用于捕捉更丰富的特征表达。这种设计有助于模型在较少的参数量下保持较高的表示能力,提高对复杂图像的判别能力。

GhostNet模型的优点如下:

轻量高效:GhostNet通过使用Ghost Module,减少了模型的参数量和计算量,使得它在计算资源受限的设备上运行速度更快,能够满足更多应用的需求。

参数效率:Ghost Module通过以较少的参数产生较多的特征图,提高了参数的利用效率。这使得模型更具可扩展性,并能够更好地适应低功耗的设备和移动端应用需求。

准确性保持:尽管GhostNet是为了追求高效而设计的,但经过实证研究表明,在一些图像分类和目标检测任务中,它的准确性能够与一些常用的大型模型相媲美,或接近。

GhostNet模型的缺点如下:

空间复杂性:尽管GhostNet在参数和计算量上显著减少,但由于采用了辅助卷积来提取更丰富的特征,其空间复杂性相对较高。这可能使得在计算资源极度有限的设备上推理速度较慢。

特定任务的局限性:GhostNet主要用于图像分类和目标检测任务。对于其他类型的任务,如语义分割或实例分割等,GhostNet可能需要额外的定制和改进来适应任务的需求。

总之,GhostNet作为一种轻量级的模型设计,通过Ghost Module降低了模型的参数量和计算量,提高了在计算资源有限的设备上的性能。尽管存在一些局限性,但它在保持一定准确性的同时,能够在资源受限情况下提供高效的图像分类和目标检测能力。

在以往的项目中使用Mobilenet模型居多,较少使用GhostNet,所以这里以实地项目开发的方式也是想进一步熟悉GhostNet模型,这里模型搭建实现代码如下所示:

# encoding:utf-8
from __future__ import division"""
__Author__:沂水寒城
功能:  GhostNet
"""import torch
import torch.nn as nn
import math
import numpy as np
from torch.hub import load_state_dict_from_url
from utils.utils import load_weights_from_state_dictdef _make_divisible(v, divisor, min_value=None):"""参考https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet/mobilenet.py"""if min_value is None:min_value = divisornew_v = max(min_value, int(v + divisor / 2) // divisor * divisor)if new_v < 0.9 * v:new_v += divisorreturn new_vclass SELayer(nn.Module):"""SE Layer"""def __init__(self, channel, reduction=4):super(SELayer, self).__init__()self.avg_pool = nn.AdaptiveAvgPool2d(1)self.fc = nn.Sequential(nn.Linear(channel, channel // reduction),nn.ReLU(inplace=True),nn.Linear(channel // reduction, channel),)def forward(self, x):b, c, _, _ = x.size()y = self.avg_pool(x).view(b, c)y = self.fc(y).view(b, c, 1, 1)y = torch.clamp(y, 0, 1)return x * ydef depthwise_conv(inp, oup, kernel_size=3, stride=1, relu=False):"""DW"""return nn.Sequential(nn.Conv2d(inp, oup, kernel_size, stride, kernel_size // 2, groups=inp, bias=False),nn.BatchNorm2d(oup),nn.ReLU(inplace=True) if relu else nn.Sequential(),)class GhostModule(nn.Module):"""Ghost"""def __init__(self, inp, oup, kernel_size=1, ratio=2, dw_size=3, stride=1, relu=True):super(GhostModule, self).__init__()self.oup = oupinit_channels = math.ceil(oup / ratio)new_channels = init_channels * (ratio - 1)self.primary_conv = nn.Sequential(nn.Conv2d(inp, init_channels, kernel_size, stride, kernel_size // 2, bias=False),nn.BatchNorm2d(init_channels),nn.ReLU(inplace=True) if relu else nn.Sequential(),)self.cheap_operation = nn.Sequential(nn.Conv2d(init_channels,new_channels,dw_size,1,dw_size // 2,groups=init_channels,bias=False,),nn.BatchNorm2d(new_channels),nn.ReLU(inplace=True) if relu else nn.Sequential(),)def forward(self, x):x1 = self.primary_conv(x)x2 = self.cheap_operation(x1)out = torch.cat([x1, x2], dim=1)return out[:, : self.oup, :, :]class GhostBottleneck(nn.Module):"""GhostBottleneck"""def __init__(self, inp, hidden_dim, oup, kernel_size, stride, use_se):super(GhostBottleneck, self).__init__()assert stride in [1, 2]self.conv = nn.Sequential(GhostModule(inp, hidden_dim, kernel_size=1, relu=True),depthwise_conv(hidden_dim, hidden_dim, kernel_size, stride, relu=False)if stride == 2else nn.Sequential(),SELayer(hidden_dim) if use_se else nn.Sequential(),GhostModule(hidden_dim, oup, kernel_size=1, relu=False),)if stride == 1 and inp == oup:self.shortcut = nn.Sequential()else:self.shortcut = nn.Sequential(depthwise_conv(inp, inp, 3, stride, relu=True),nn.Conv2d(inp, oup, 1, 1, 0, bias=False),nn.BatchNorm2d(oup),)def forward(self, x):return self.conv(x) + self.shortcut(x)class GhostNet(nn.Module):"""GhostNet"""def __init__(self, cfgs, num_classes=1000, width_mult=1.0):super(GhostNet, self).__init__()self.cfgs = cfgsoutput_channel = _make_divisible(16 * width_mult, 4)layers = [nn.Sequential(nn.Conv2d(3, output_channel, 3, 2, 1, bias=False),nn.BatchNorm2d(output_channel),nn.ReLU(inplace=True),)]input_channel = output_channelblock = GhostBottleneckfor k, exp_size, c, use_se, s in self.cfgs:output_channel = _make_divisible(c * width_mult, 4)hidden_channel = _make_divisible(exp_size * width_mult, 4)layers.append(block(input_channel, hidden_channel, output_channel, k, s, use_se))input_channel = output_channelself.features = nn.Sequential(*layers)output_channel = _make_divisible(exp_size * width_mult, 4)self.squeeze = nn.Sequential(nn.Conv2d(input_channel, output_channel, 1, 1, 0, bias=False),nn.BatchNorm2d(output_channel),nn.ReLU(inplace=True),nn.AdaptiveAvgPool2d((1, 1)),)input_channel = output_channeloutput_channel = 1280self.classifier = nn.Sequential(nn.Linear(input_channel, output_channel, bias=False),nn.BatchNorm1d(output_channel),nn.ReLU(inplace=True),nn.Dropout(0.2),nn.Linear(output_channel, num_classes),)self._initialize_weights()def forward(self, x, need_fea=False):if need_fea:features, features_fc = self.forward_features(x, need_fea)x = self.classifier(features_fc)return features, features_fc, xelse:x = self.forward_features(x)x = self.classifier(x)return xdef forward_features(self, x, need_fea=False):if need_fea:input_size = x.size(2)scale = [4, 8, 16, 32]features = [None, None, None, None]for idx, layer in enumerate(self.features):x = layer(x)if input_size // x.size(2) in scale:features[scale.index(input_size // x.size(2))] = xx = self.squeeze(x)return features, x.view(x.size(0), -1)else:x = self.features(x)x = self.squeeze(x)return x.view(x.size(0), -1)def _initialize_weights(self):for m in self.modules():if isinstance(m, nn.Conv2d):nn.init.kaiming_normal_(m.weight, mode="fan_out", nonlinearity="relu")elif isinstance(m, nn.BatchNorm2d):m.weight.data.fill_(1)m.bias.data.zero_()def cam_layer(self):return self.features[-1]

可以直接去华为开源仓库里面或者是开源社区其他的项目里面选择自己喜欢的代码实现即可,不需要自己去重头实现,理解应用即可。这里就不再赘述了,很多项目整体已经是比较完善的了。

这里对模型预测结果也进行了簇群可视化,如下所示:

这里类别比较多,显得比较混乱一些,接下来绘制模型的混淆矩阵,如下所示:

训练过程中,loss走势曲线和acc变化曲线如下所示:

这组实验结果是在224x224的标准尺寸下得到的结果。

前面我们讲到过官方原始的数据集其实是32x32的小尺寸数据集,这里我们做了第二组的实验,就是直接使用官方的尺寸来进行模型的开发训练,得到结果详情如下所示:

可以看到:32x32的尺寸图像精度与224x224的尺寸图像差了40%左右的精度,不难分析出图像的原始分辨率对于最终的分类识别效果的影响是巨大的。

混淆矩阵如下:

详细指标的对比结果如下:

224x224尺度图像结果
+------------+-----------+-----------+----------+
| train_loss | test_loss | train_acc | test_acc | 
+------------+-----------+-----------+----------+
|  0.92347   |  1.33351  |  0.97965  | 0.84315  |
+------------+-----------+-----------+----------+32x32尺度图像结果
+------------+-----------+-----------+----------+
| train_loss | test_loss | train_acc | test_acc |
+------------+-----------+-----------+----------+
|  1.93752   |  2.35085  |  0.64693  | 0.52852  |   
+------------+-----------+-----------+----------+

感兴趣的话也都可以对应实践尝试下!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/329491.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

webgl入门-绘制三角形

绘制三角形 前言 三角形是一个最简单、最稳定的面&#xff0c;webgl 中的三维模型都是由三角面组成的。咱们这一篇就说一下三角形的绘制方法。 课堂目标 理解多点绘图原理。可以绘制三角形&#xff0c;并将其组合成多边形。 知识点 缓冲区对象点、线、面图形 第一章 web…

C# run Node.js

C# run nodejs Inter-Process Communication&#xff0c;IPC Process类 启动Node.js进程&#xff0c;通过标准输入输出与其进行通信。 // n.js// 监听来自标准输入的消息 process.stdin.on(data, function (data) {// 收到消息后&#xff0c;在控制台输出并回复消息console.l…

C++设计模式---面向对象原则

面向对象设计原则 原则的目的&#xff1a;高内聚&#xff0c;低耦合 1. 单一职责原则 类的职责单一&#xff0c;对外只提供一种功能&#xff0c;而引起类变化的原因都应该只有一个。 2. 开闭原则 对扩展开放&#xff0c;对修改关闭&#xff1b;增加功能是通过增加代码来实现的&…

探索 Rust 语言的精髓:深入 Rust 标准库

探索 Rust 语言的精髓&#xff1a;深入 Rust 标准库 Rust&#xff0c;这门现代编程语言以其内存安全、并发性和性能优势而闻名。它不仅在系统编程领域展现出强大的能力&#xff0c;也越来越多地被应用于WebAssembly、嵌入式系统、分布式服务等众多领域。Rust 的成功&#xff0…

计算机网络数据链路层知识点总结

3.1 数据链路层功能概述 &#xff08;1&#xff09;知识总览 &#xff08;2&#xff09;数据链路层的研究思想 &#xff08;3&#xff09;数据链路层基本概念 &#xff08;4&#xff09;数据链路层基本功能 3.1 封装成帧和透明传输 &#xff08;1&#xff09;数据链路层功能…

Redis常见数据类型(3)-String, Hash

目录 String 命令小结 内部编码 典型的使用场景 缓存功能 计数功能 共享会话 手机验证码 Hash 哈希 命令 hset hget hexists hdel hkeys hvals hgetall hmget hlen hsetnx hincrby hincrbyfloat String 上一篇中介绍了了String里的基本命令, 接下来总结一…

《Python编程从入门到实践》day37

# 昨日知识点回顾 制定规范、创建虚拟环境并激活&#xff0c;正在虚拟环境创建项目、数据库和应用程序 # 今日知识点学习 18.2.4 定义模型Entry # models.py from django.db import models# Create your models here. class Topic(models.Model):"""用户学习的…

Vue2基础及其进阶面试(一)

简单版项目初始化 新建一个vue2 官网文档&#xff1a;介绍 — Vue.js 先确保下载了vue的脚手架 npm install -g vue-cli npm install -g vue/cli --force vue -V 创建项目 vue create 自己起个名字 选择自己选择特性 选择&#xff1a; Babel&#xff1a;他可以将我们写…

微软开发者大会,Copilot Agents发布,掀起新一轮生产力革命!

把AI融入生产力工具的未来会是什么样&#xff1f;微软今天给出了蓝图。 今天凌晨&#xff0c;微软召开了Microsoft Build 2024 开发者大会&#xff0c;同前两天的Google I/O开发者大会一样&#xff0c;本次大会的核心词还是“AI”&#xff0c;其中最主要的内容是最新的Copilot…

拼多多暂时超越阿里成为电商第一

关注卢松松&#xff0c;会经常给你分享一些我的经验和观点。 拼多多的财报又炸裂了&#xff1a; 拼多多发布了第一季度财报&#xff0c;营收868亿&#xff0c;增长了131%&#xff0c;净利润279亿&#xff0c;增长了246%&#xff0c;营销服务收入424亿&#xff0c;也就是商家的…

BCD编码Java实现

最常用的BCD编码&#xff0c;就是使用"0"至"9"这十个数值的二进码来表示。这种编码方式&#xff0c;在称之为“8421码”&#xff08;日常所说的BCD码大都是指8421BCD码形式&#xff09;。除此以外&#xff0c;对应不同需求&#xff0c;各人亦开发了不同的编…

Linux多线程系列三: 生产者消费者模型,信号量使用,基于阻塞队列和环形队列的这两种生产者消费者代码的实现

Linux多线程系列三: 生产者消费者模型,信号量,基于阻塞队列和环形队列的这两种生产者消费者代码的实现 一.生产者消费者模型的理论1.现实生活中的生产者消费者模型2.多线程当中的生产者消费者模型3.理论 二.基于阻塞队列的生产者消费者模型的基础代码1.阻塞队列的介绍2.大致框架…

rust的版本问题,安装问题,下载问题

rust的版本、安装、下载问题 rust版本问题&#xff0c; 在使用rust的时候&#xff0c;应用rust的包&#xff0c;有时候包的使用和rust版本有关系。 error: failed to run custom build command for pear_codegen v0.1.2 Caused by: process didnt exit successfully: D:\rus…

【Mac】Dreamweaver 2021 for mac v21.3 Rid中文版安装教程

软件介绍 Dreamweaver是Adobe公司开发的一款专业网页设计与前端开发软件。它集成了所见即所得&#xff08;WYSIWYG&#xff09;编辑器和代码编辑器&#xff0c;可以帮助开发者快速创建和编辑网页。Dreamweaver提供了丰富的功能和工具&#xff0c;包括代码提示、语法高亮、代码…

es数据备份和迁移Elasticsearch

Elasticsearch数据备份与恢复 前提 # 注意&#xff1a; 1.在进行本地备份时使用--type需要备份索引和数据&#xff08;mapping,data&#xff09; 2.在将数据备份到另外一台ES节点时需要比本地备份多备份一种数据类型&#xff08;analyzer,mapping,data,template&#xff09; …

STM32看门狗

文章目录 WDG&#xff08;Watchdog&#xff09;看门狗独立看门狗独立看门狗框图超时时间计算 窗口看门狗超时时间 独立看门狗与窗口看门狗对比补充 WDG&#xff08;Watchdog&#xff09;看门狗 看门狗可以监控程序的运行状态&#xff0c;当程序因为设计漏洞、硬件故障、电磁干…

SpringCloud Alibaba的相关组件的简介及其使用

Spring Cloud Alibaba是阿里巴巴为开发者提供的一套微服务解决方案&#xff0c;它基于Spring Cloud项目&#xff0c;提供了一系列功能强大的组件&#xff0c;包括服务注册与发现、配置中心、熔断与限流、消息队列等。 本文将对Spring Cloud Alibaba的相关组件进行简介&#xff…

如何搭建一个vue项目(完整步骤)

搭建一个新的vue项目 一、安装node环境二、搭建vue项目环境1、全局安装vue-cli2、进入你的项目目录&#xff0c;创建一个基于 webpack 模板的新项目3、进入项目&#xff1a;cd vue-demo&#xff0c;安装依赖4、npm run dev&#xff0c;启动项目 三、vue项目目录讲解四、开始我们…

安装和使用图像处理软件GraphicsMagick @FreeBSD

GraphicsMagick是一个用于处理图像的读取、写入和操作的工具软件。它被誉为图像处理领域的“瑞士军刀”&#xff0c;短小精悍&#xff0c;支持超过88种图像格式&#xff0c;包括DPX、GIF、JPEG、JPEG-2000、PNG、PDF、PNM和TIFF等。 GraphicsMagick的主要特点包括&#xff1a;…

【openlayers系统学习】3.5colormap详解(颜色映射)

五、colormap详解&#xff08;颜色映射&#xff09; ​colormap​ 包是一个很好的实用程序库&#xff0c;用于创建颜色图。该库已作为项目的依赖项添加&#xff08;1.7美化&#xff08;设置style&#xff09;&#xff09;。要导入它&#xff0c;请编辑 main.js​ 以包含以下行…