当前位置: 首页 > news >正文

Python调用MMDetection实现AI抠图去背景

这篇文章的内容是以 《使用MMDetection进行目标检测、实例和全景分割》 为基础,需要安装好 MMDetection 的运行环境,同时完成目标检测、实例分割和全景分割的功能实践,之后再看下面的内容。

想要实现AI抠图去背景的需求,我们需要利用 OpenMMLab 项目 MMDetection 模型库中的实例分割(Instance Segmentation)模型,来帮我们完成最核心的分类、分割图片物体任务。

接下来的主要操作是在 demo.py 编写和调试代码,首先编写下面的代码:

import cv2
import mmcv
from copy import deepcopy
from mmdet.apis import init_detector, inference_detectorconfig_file = 'configs/scnet/scnet_r50_fpn_1x_coco.py'  # 配置文件路径
checkpoint_file = 'checkpoints/scnet/scnet_r50_fpn_1x_coco-c3f09857.pth'  # 预训练模型加载路径
device = 'cpu'  # cpu|gpu
model = init_detector(config_file, checkpoint_file, device=device)  # 构建模型
img_raw = mmcv.imread('demo/demo.jpg')  # 待推理图像的原始 numpy.ndarray 对象img_h = img_raw.shape[0]  # 图像高度像素值
img_w = img_raw.shape[1]  # 图像宽度像素值result = inference_detector(model, img_raw)  # 推理的结果
img_result = model.show_result(img_raw, result)  # 结果图像的 numpy.ndarray 对象
model.show_result(img_raw, result, out_file='demo/demo_result.jpg')  # 将结果图像另存为文件

执行 demo.py 文件后,项目下面会生成 demo/demo_result.jpg 文件:

demo_result_1676859284

之前,我们是通过另存为文件的方式,来可视化推理的结果,现在,我们要进一步的解析推理结果。由模型文件名称 scnet_r50_fpn_1x_coco-c3f09857.pth 可知,这是个使用 coco 数据集训练出来的预训练模型,所以我们需要用 coco 数据集的定义方式去解析推理结果。

demo.py 文件里继续编写,提前定义一个存储和解析推理结果的 coco_80_dict 字典:

coco_80_dict = {'person': {'index': 0, 'id': 1, 'describe': '人'},'bicycle': {'index': 1, 'id': 2, 'describe': '自行车'},'car': {'index': 2, 'id': 3, 'describe': '车'},'motorcycle': {'index': 3, 'id': 4, 'describe': '摩托车'},'airplane': {'index': 4, 'id': 5, 'describe': '飞机'},'bus': {'index': 5, 'id': 6, 'describe': '公交车'},'train': {'index': 6, 'id': 7, 'describe': '火车'},'truck': {'index': 7, 'id': 8, 'describe': '卡车'},'boat': {'index': 8, 'id': 9, 'describe': '船'},'traffic light': {'index': 9, 'id': 10, 'describe': '红绿灯'},'fire hydrant': {'index': 10, 'id': 11, 'describe': '消防栓'},'stop sign': {'index': 11, 'id': 13, 'describe': '停车标志'},'parking meter': {'index': 12, 'id': 14, 'describe': '停车收费表'},'bench': {'index': 13, 'id': 15, 'describe': '板凳'},'bird': {'index': 14, 'id': 16, 'describe': '鸟'},'cat': {'index': 15, 'id': 17, 'describe': '猫'},'dog': {'index': 16, 'id': 18, 'describe': '狗'},'horse': {'index': 17, 'id': 19, 'describe': '马'},'sheep': {'index': 18, 'id': 20, 'describe': '羊'},'cow': {'index': 19, 'id': 21, 'describe': '牛'},'elephant': {'index': 20, 'id': 22, 'describe': '大象'},'bear': {'index': 21, 'id': 23, 'describe': '熊'},'zebra': {'index': 22, 'id': 24, 'describe': '斑马'},'giraffe': {'index': 23, 'id': 25, 'describe': '长颈鹿'},'backpack': {'index': 24, 'id': 27, 'describe': '背包'},'umbrella': {'index': 25, 'id': 28, 'describe': '雨伞'},'handbag': {'index': 26, 'id': 31, 'describe': '手提包'},'tie': {'index': 27, 'id': 32, 'describe': '领带'},'suitcase': {'index': 28, 'id': 33, 'describe': '手提箱'},'frisbee': {'index': 29, 'id': 34, 'describe': '飞盘'},'skis': {'index': 30, 'id': 35, 'describe': '雪橇'},'snowboard': {'index': 31, 'id': 36, 'describe': '滑雪板'},'sports ball': {'index': 32, 'id': 37, 'describe': '运动球'},'kite': {'index': 33, 'id': 38, 'describe': '风筝'},'baseball bat': {'index': 34, 'id': 39, 'describe': '棒球棒'},'baseball glove': {'index': 35, 'id': 40, 'describe': '棒球手套'},'skateboard': {'index': 36, 'id': 41, 'describe': '滑板'},'surfboard': {'index': 37, 'id': 42, 'describe': '冲浪板'},'tennis racket': {'index': 38, 'id': 43, 'describe': '网球拍'},'bottle': {'index': 39, 'id': 44, 'describe': '瓶'},'wine glass': {'index': 40, 'id': 46, 'describe': '酒杯'},'cup': {'index': 41, 'id': 47, 'describe': '杯'},'fork': {'index': 42, 'id': 48, 'describe': '叉子'},'knife': {'index': 43, 'id': 49, 'describe': '刀'},'spoon': {'index': 44, 'id': 50, 'describe': '汤匙'},'bowl': {'index': 45, 'id': 51, 'describe': '碗'},'banana': {'index': 46, 'id': 52, 'describe': '香蕉'},'apple': {'index': 47, 'id': 53, 'describe': '苹果'},'sandwich': {'index': 48, 'id': 54, 'describe': '三明治'},'orange': {'index': 49, 'id': 55, 'describe': '橙'},'broccoli': {'index': 50, 'id': 56, 'describe': '西兰花'},'carrot': {'index': 51, 'id': 57, 'describe': '胡萝卜'},'hot dog': {'index': 52, 'id': 58, 'describe': '热狗'},'pizza': {'index': 53, 'id': 59, 'describe': '披萨'},'donut': {'index': 54, 'id': 60, 'describe': '甜甜圈'},'cake': {'index': 55, 'id': 61, 'describe': '蛋糕'},'chair': {'index': 56, 'id': 62, 'describe': '椅子'},'couch': {'index': 57, 'id': 63, 'describe': '沙发'},'potted plant': {'index': 58, 'id': 64, 'describe': '盆栽植物'},'bed': {'index': 59, 'id': 65, 'describe': '床'},'dining table': {'index': 60, 'id': 67, 'describe': '餐桌'},'toilet': {'index': 61, 'id': 70, 'describe': '厕所'},'tv': {'index': 62, 'id': 72, 'describe': '电视'},'laptop': {'index': 63, 'id': 73, 'describe': '笔记本电脑'},'mouse': {'index': 64, 'id': 74, 'describe': '鼠标'},'remote': {'index': 65, 'id': 75, 'describe': '遥控器'},'keyboard': {'index': 66, 'id': 76, 'describe': '键盘'},'cell phone': {'index': 67, 'id': 77, 'describe': '手机'},'microwave': {'index': 68, 'id': 78, 'describe': '微波炉'},'oven': {'index': 69, 'id': 79, 'describe': '烤箱'},'toaster': {'index': 70, 'id': 80, 'describe': '烤面包机'},'sink': {'index': 71, 'id': 81, 'describe': '水槽'},'refrigerator': {'index': 72, 'id': 82, 'describe': '冰箱'},'book': {'index': 73, 'id': 84, 'describe': '书'},'clock': {'index': 74, 'id': 85, 'describe': '时钟'},'vase': {'index': 75, 'id': 86, 'describe': '花瓶'},'scissors': {'index': 76, 'id': 87, 'describe': '剪刀'},'teddy bear': {'index': 77, 'id': 88, 'describe': '泰迪熊'},'hair drier': {'index': 78, 'id': 89, 'describe': '吹风机'},'toothbrush': {'index': 79, 'id': 90, 'describe': '牙刷'},
}

推理结果 result 的长度为 len(result) == 2,其中:

  • 检测框 result[0] 的长度固定为 len(result[0]) == 80,因为 coco 数据集用于检测、分割的部分共有 80 个类别
    • 检测框的解析方式
      • 长度为 5 的列表,内容分别代表的是 [x1, y1, x2, y2, 置信度]
    • 检测框的数据示例
      [array([[2.54207230e+02, 1.04094536e+02, 2.62954681e+02, 1.12405502e+02, 7.87650794e-02],[3.75357147e+02, 1.20013657e+02, 3.81758453e+02, 1.33114960e+02, 6.91782460e-02],[5.32841248e+02, 1.09704865e+02, 5.40237061e+02, 1.24966133e+02, 1.59687027e-02],[6.22804871e+02, 1.05815277e+02, 6.35152161e+02, 1.15566162e+02, 5.09093935e-03]], dtype=float32),array([], shape=(0, 5), dtype=float32),array([[4.81168701e+02, 1.10373505e+02, 5.23180786e+02, 1.30380783e+02, 9.89862204e-01],[4.32057190e+02, 1.04940285e+02, 4.83949829e+02, 1.32065140e+02, 9.75807786e-01],[8.68552148e-01, 1.12007828e+02, 6.18107605e+01, 1.44267593e+02, 9.75750148e-01],[6.08653687e+02, 1.11829590e+02, 6.35857971e+02, 1.37511154e+02, 9.74755704e-01],[2.66471069e+02, 1.05501320e+02, 3.26720917e+02, 1.27970596e+02, 9.69615817e-01],[1.91124908e+02, 1.08998817e+02, 2.99086731e+02, 1.55649002e+02, 9.68829334e-01],[3.98783081e+02, 1.11128929e+02, 4.33258514e+02, 1.33113083e+02, 9.66077983e-01],......[2.54207230e+02, 1.04094536e+02, 2.62954681e+02, 1.12405502e+02, 5.26814163e-03]], dtype=float32),......array([[3.7397647e+02, 1.3353706e+02, 4.3298822e+02, 1.8852022e+02, 2.0611383e-01],[2.1776900e+02, 1.7274181e+02, 4.5755392e+02, 3.8911337e+02, 6.3707083e-03]], dtype=float32),array([], shape=(0, 5), dtype=float32)
      ]
      
  • 分割对象 result[1] 的长度固定为 len(result[1]) == 80,同样代表 coco 数据集里的 80 个类别
    • 分割对象的解析方式,是一个 2 维数组
      • 1 个维度代表图像的高度
      • 2 个维度代表图像的框度
      • 具体的 bool 值用于判断下标 [h][w] 的像素点是否为识别目标的分割像素点,以抠图需求为例
        • True 表示下标 [h][w] 的像素点应该被保留
        • False 表示下标 [h][w] 的像素点应该被去掉
    • 分割对象的数据示例
      [[array([[False, False, False, ..., False, False, False],[False, False, False, ..., False, False, False],[False, False, False, ..., False, False, False],...,[False, False, False, ..., False, False, False],[False, False, False, ..., False, False, False],[False, False, False, ..., False, False, False]]),array([[False, False, False, ..., False, False, False],[False, False, False, ..., False, False, False],[False, False, False, ..., False, False, False],...,[False, False, False, ..., False, False, False],[False, False, False, ..., False, False, False],[False, False, False, ..., False, False, False]]),array([[False, False, False, ..., False, False, False],[False, False, False, ..., False, False, False],[False, False, False, ..., False, False, False],...,[False, False, False, ..., False, False, False],[False, False, False, ..., False, False, False],[False, False, False, ..., False, False, False]])],...,[],[]
      ]
      

因为推理结果 result 的组织结构是 result [0=检测框,1=分割对象] [coco 80 个类别的顺序下标],这种组织结构不方便我们提取抠图的数据,所以需要将推理结果 result 拆解后存储到前面定义好 coco_80_dict 字典中。

demo.py 文件里继续编写,拆解推理结果 result 数据,以方便我们提取抠图的结构存储到 coco_80_dict 字典:

for cc_key, cc_value in coco_80_dict.items():coco_80_dict[cc_key]['number'] = len(result[0][cc_value['index']])coco_80_dict[cc_key]['detection'] = result[0][cc_value['index']]coco_80_dict[cc_key]['segmentation'] = result[1][cc_value['index']]

此时 coco_80_dict 字典的数据结构如下所示:

{'person': {'index': 0,'id': 1,'describe': '人','number': 4,'detection': 检测框 list[[x1, y1, x2, y2, 置信度]],'segmentation': 分割对象 list[[h][w]=是否为识别目标的分割像素点]},......'toothbrush': {'index': 79,'id': 90,'describe': '牙刷','number': 0,'detection': array([], shape=(0, 5), dtype=float32),'segmentation': []}
}

接下来以位于图像中心位置的板凳为例,把板凳的检测框和分割对象可视化成图像文件,我们开始解析,首先可视化板凳的检测框。

demo.py 文件里继续编写,读取第 1 个板凳的检测框( coco_80_dict['bench']['detection'][0] ),并将其可视化:

img_detection = deepcopy(img_raw)  # 深拷贝原始 numpy.ndarray 对象
img_detection_data = coco_80_dict['bench']['detection'][0]  # 第1个板凳的检测框
img_detection_data_x1 = int(img_detection_data[0])
img_detection_data_y1 = int(img_detection_data[1])
img_detection_data_x2 = int(img_detection_data[2])
img_detection_data_y2 = int(img_detection_data[3])
print(f'目标置信度 = {img_detection_data[4]*100}%')  # 绘制比较麻烦,就直接打印for x_i in range(img_detection_data_x1, img_detection_data_x2):img_detection[img_detection_data_y1, x_i] = [0,0,255]  # 重置为 [0,0,255] 即红色像素点img_detection[img_detection_data_y2, x_i] = [0,0,255]for y_i in range(img_detection_data_y1, img_detection_data_y2):img_detection[y_i, img_detection_data_x1] = [0,0,255]img_detection[y_i, img_detection_data_x2] = [0,0,255]cv2.imwrite(f'demo/demo_result_detection.jpg', img_detection)

执行 demo.py 文件后,控制台会打印 目标置信度 = 99.38850998878479%,然后项目下面会生成 demo/demo_result_detection.jpg 文件:

demo_result_detection_1676874870

demo.py 文件里继续编写,读取第 1 个板凳的分割对象( coco_80_dict['bench']['segmentation'][0] ),并将其可视化:

img_segmentation = deepcopy(img_raw)  # 深拷贝原始 numpy.ndarray 对象
img_segmentation_data = coco_80_dict['bench']['segmentation'][0]  # 第1个板凳的分割对象
for h_i in range(img_h):for w_i in range(img_w):# 通过分割对象的 bool 值来判断像素点是否需要去除(重置为 [255,255,255] 即纯白背景)if not img_segmentation_data[h_i, w_i]:img_segmentation[h_i, w_i] = [255,255,255]cv2.imwrite(f'demo/demo_result_segmentation.jpg', img_segmentation)

执行 demo.py 文件后,项目下面会生成 demo/demo_result_segmentation.jpg 文件:

demo_result_segmentation_1676875708

最后,就是抠图最后一步,把现在的 3 通道图像数据改成 4 通道(添加透明度的通道),输出透明背景的图像文件。

demo.py 文件里继续编写,读取第 1 个板凳的分割对象( coco_80_dict['bench']['segmentation'][0] ),先添加透明度通道再并将其可视化:

img_segmentation_png_raw = deepcopy(img_raw)  # 深拷贝原始numpy.ndarray对象
img_segmentation_png = np.full((img_h, img_w, 4), 0, dtype='uint8')  # 创建(图像高,图像宽,4通道)大小的numpy.ndarray对象
img_segmentation_png_data = coco_80_dict['bench']['segmentation'][0]
for h_i in range(img_h):for w_i in range(img_w):if img_segmentation_png_data[h_i, w_i]:img_segmentation_png[h_i, w_i] = np.append(img_segmentation_png_raw[h_i, w_i], 255)cv2.imwrite(f'demo/demo_result_segmentation_png.png', img_segmentation_png)

执行 demo.py 文件后,项目下面会生成 demo/demo_result_segmentation_png.png 文件:

demo_result_segmentation__1676881555

至此,就完成了Python调用MMDetection实现AI抠图去背景的功能!

相关文章:

Python调用MMDetection实现AI抠图去背景

这篇文章的内容是以 《使用MMDetection进行目标检测、实例和全景分割》 为基础,需要安装好 MMDetection 的运行环境,同时完成目标检测、实例分割和全景分割的功能实践,之后再看下面的内容。 想要实现AI抠图去背景的需求,我们需要…...

Java代码使用最小二乘法实现线性回归预测

最小二乘法简介最小二乘法是一种在误差估计、不确定度、系统辨识及预测、预报等数据处理诸多学科领域得到广泛应用的数学工具。它通过最小化误差(真实目标对象与拟合目标对象的差)的平方和寻找数据的最佳函数匹配。利用最小二乘法可以简便地求得未知的数…...

linux-rockchip-音频相关

一、查看当前配置声卡状态 cat /proc/asound/cards二、查看当前声卡工作状态 声卡分两种通道,一种是Capture、一种是Playback。Capture是输入通道,Playback是输出通道。例如pcm0p属于声卡输出通道,pcm0c属于声卡输入通道。 ls /proc/asoun…...

Android Handler的内存抖动以及子线程创建Handler

一、介绍 Handler,作为一个在主线程存活的消息分发工具,在App开发过程使用频率很高,也是面试问的比较多的。 面试常见的比如:子线程如何创建?Handler的机制是什么?内存抖动等,接下来我们会针对H…...

机器学习算法原理之k近邻 / KNN

文章目录k近邻 / KNN主要思想模型要素距离度量分类决策规则kd树主要思想kd树的构建kd树的搜索总结归纳k近邻 / KNN 主要思想 假定给定一个训练数据集,其中实例标签已定,当输入新的实例时,可以根据其最近的 kkk 个训练实例的标签&#xff0c…...

【期末复习】例题说明Prim算法与Kruskal算法

点睛Prim与Kruskal算法是用来求图的最小生成树的算法。最小生成树有n个顶点,n-1条边,不能有回路。Prim算法Prim算法的特点是从个体到整体,随机选定一个顶点为起始点出发,然后找它的权值最小的边对应的另一个顶点,这两个…...

AtCoder Beginner Contest 290 A-E F只会n^2

ABC比较简单就不再复述 D - Marking 简要题意 :给你一个长度为nnn的数组,下标为0到n−10 到 n-10到n−1,最初指针位于0,重复执行n-1次操作,每次操作的定义为将当前指针加上ddd,如果该位置为空(未填数),否则我们向右找到第一个为空…...

springMvc源码解析

入口:找到springboot的自动配置,将DispatcherServlet和DispatcherServletRegistrationBean注入spring容器(DispatcherServletRegistrationBean间接实现了ServletContextInitializer接口,最终ServletContextInitializer的onStartup…...

采用aar方式将react-native集成到已有安卓APP

关于react-native和android的开发环境搭建、环境变量配置等可以查看官方文档。 官方文档地址 文章中涉及的node、react等版本: node:v16.18.1 react:^18.1.0 react-native:^0.70.6 gradle:gradle-7.2开发工具:VSCode和android studio 关于react-native和…...

Tomcat目录介绍,结构目录有哪些?哪些常用?

bin 启动,关闭和其他脚本。这些 .sh文件(对于Unix系统)是这些.bat文件的功能副本(对于Windows系统)。由于Win32命令行缺少某些功能,因此此处包含一些其他文件。 比如说:windows下启动tomcat用的…...

Elasticsearch也能“分库分表“,rollover实现自动分索引

一、自动创建新索引的方法 MySQL的分库分表大家是非常熟悉的,在Elasticserach中有存在类似的场景需求。为了不让单个索引太过于庞大,从而引发性能变差等问题,我们常常有根据索引大小、时间等创建新索引的需求,解决方案一般有两个…...

6 大经典机器学习数据集,3w+ 用户票选得出,建议收藏

内容一览:本期汇总了超神经下载排名众多的 6 个数据集,涵盖图像识别、机器翻译、遥感影像等领域。这些数据集质量高、数据量大,经历人气认证值得收藏码住。 关键词:数据集 机器翻译 机器视觉 数据集是机器学习模型训练的基础&…...

Logview下载

Logview下载 之前一直用的NotePad 后来偶尔的看到作者有发布不当言论 就卸载了又去下载了NotePad– 但是,其实不管是 还是 – 打开大一些的文件都会卡死 所以就搜了这个logview 用起来还不错,目前我这再大的文件 这个软件都是秒打开 但是也会有一点点小…...

macos 下载 macOS 系统安装程序及安装U盘制作方法

01 下载 macOS 系统安装程序的方法 本文来自: https://discussionschinese.apple.com/docs/DOC-250004259 简介 Mac 用户时不时会需要下载 macOS 的安装程序,目的不同,或者升级或者降级,或者研究或者收藏。为了方便不同用户,除…...

c++动态内存分布以及和C语言的比较

文章目录 前言一.c/c内存分布 C语言的动态内存管理方式 C内存管理方式 operator new和operator delete函数 malloc/free和new/delete的区别 定位new 内存泄漏的危害总结前言 c是在c的基础上开发出来的,所以关于内存管理这一方面是兼容c的&…...

软考高级信息系统项目管理师系列之三十一:项目变更管理

软考高级信息系统项目管理师系列之三十一:项目变更管理 一、项目变更管理内容二、项目变更管理基本概念1.项目变更管理定义2.项目变更产生的原因3.项目变更的分类三、项目变更管理的原则和工作流程1.项目变更管理的原则2.变更管理的组织机构3.变更管理的工作程序四、项目变更管…...

【Vue3源码】第二章 effect功能的完善补充

【Vue3源码】第二章 effect功能的完善补充 前言 上一章节我们实现了effect函数的功能stop和onstop,这次来优化下stop功能。 优化stop功能 之前我们的单元测试中,stop已经可以成功停止了响应式更新(清空了收集到的dep依赖) st…...

CHAPTER 2 Web Server - apache(httpd)

Web Server - httpd2.1 http2.1.1 协议版本2.1.2 http报文2.1.3 web资源(web resource)2.1.4 一次完整的http请求处理过程2.1.5 接收请求的模型2.2 httpd配置2.2.1 MPM(多进程处理模块)1. 工作模式2. 切换MPM3. MPM参数配置2.2.2 主配置文件1. 基本配置2. 站点访问控制常见机制…...

【Vagrant】下载安装与基本操作

文章目录概述软件安装安装VirtualBox安装Vagrant配置环境用Vagrant创建一个VMVagrantfile文件配置常用命令概述 Vagrant是一个创建虚拟机的技术,是用来创建和管理虚拟机的工具,本身自己并不能创建管理虚拟机。创建和管理虚拟机必须依赖于其他的虚拟化技…...

常用类(五)System类

(1)System类常见方法和案例: (1)exit:退出当前程序 我们设计的代码如下所示: package com.ypl.System_;public class System_ {public static void main(String[] args) {//exit: 退出当前程序System.out.println("ok1"…...

从零实现富文本编辑器#5-编辑器选区模型的状态结构表达

先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)

文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错

出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上,所以报错,到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本,cu、torch、cp 的版本一定要对…...

前端开发面试题总结-JavaScript篇(一)

文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包(Closure)?闭包有什么应用场景和潜在问题?2.解释 JavaScript 的作用域链(Scope Chain) 二、原型与继承3.原型链是什么?如何实现继承&a…...

OpenLayers 分屏对比(地图联动)

注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...

Map相关知识

数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2

每日一言 今天的每一份坚持,都是在为未来积攒底气。 案例:OLED显示一个A 这边观察到一个点,怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 : 如果代码里信号切换太快(比如 SDA 刚变,SCL 立刻变&#…...

dify打造数据可视化图表

一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...

稳定币的深度剖析与展望

一、引言 在当今数字化浪潮席卷全球的时代,加密货币作为一种新兴的金融现象,正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而,加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下,稳定…...

C++.OpenGL (14/64)多光源(Multiple Lights)

多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...