Labelme分割标注软件

Labelme分割标注软件

  • 1、环境配置与安装
    • 1.1 创建conda虚拟环境(建议)
    • 1.2 安装Labelme
  • 2、简单使用
    • 2.1 创建label标签文件
    • 2.2 启动labelme
    • 2.3 打开文件/文件夹
    • 2.4 设置保存结果路径
    • 2.5 标注目标
    • 2.6 保存json文件格式
  • 3 格式转换
    • 3.1 转换语义分割标签
    • 3.2 转换实例分割标签

相关重要博文链接:
MS COCO数据集介绍以及pycocotools使用
Labelme项目源代码

Labelme是一款经典的标注工具,支持目标检测、语义分割、实例分割等任务。今天针对分割任务的数据标注进行简单的介绍。开源项目地址
在这里插入图片描述

1、环境配置与安装

1.1 创建conda虚拟环境(建议)

为了不影响其他python环境,建议新建一个环境。(不想新建可以跳过)
这里以创建一个名为labelme_env,python版本为3.8的环境为例:

conda create -n labelme_env python=3.8

创建完成后,进入新环境:

conda activate labelme_env

1.2 安装Labelme

安装非常简单,直接使用pip安装即可:

pip install labelme

安装pycocotools

# ubutnu
pip install pycocotools
# Windows
pip install pycocotools-windows

安装完成后在终端输入labelme即可启动:

labelme

在这里插入图片描述

2、简单使用

在这里插入图片描述

├── img_data: 存放你要标注的所有图片
├── data_annotated: 存放后续标注好的所有json文件
└── labels.txt: 所有类别信息

2.1 创建label标签文件

__ignore__
_background_
dog
cat

在这里插入图片描述

2.2 启动labelme

在这里插入图片描述

labelme --labels labels.txt

在这里插入图片描述
在这里插入图片描述

2.3 打开文件/文件夹

在这里插入图片描述
在这里插入图片描述

2.4 设置保存结果路径

在这里插入图片描述
在这里插入图片描述

2.5 标注目标

在这里插入图片描述
在这里插入图片描述

2.6 保存json文件格式

在这里插入图片描述

{"version": "4.5.9","flags": {},"shapes": [{"label": "dog","points": [[108.09090909090907,687.1818181818181],....[538.090909090909,668.090909090909],[534.4545454545454,689.0]],"group_id": null,"shape_type": "polygon","flags": {}}],"imagePath": "../img_data/1.jpg","imageData": null,"imageHeight": 690,"imageWidth": 690
}

3 格式转换

3.1 转换语义分割标签

原作者为了方便,也提供了一个脚本,帮我们方便的将json文件转换成PASCAL VOC的语义分割标签格式。示例项目链接:
https://github.com/wkentaro/labelme/tree/master/examples/semantic_segmentation
在这里插入图片描述

python labelme2voc.py data_annotated data_dataset_voc --labels labels.txt

执行后会生成如下目录:

- data_dataset_voc/JPEGImages
- data_dataset_voc/SegmentationClass
- data_dataset_voc/SegmentationClassPNG
- data_dataset_voc/SegmentationClassVisualization
- data_dataset_voc/class_names.txt

在这里插入图片描述
在这里插入图片描述
class_names.txt存储的是所有的类别信息,包括背景。

_background_
dog
cat

在这里插入图片描述

#!/usr/bin/env pythonfrom __future__ import print_functionimport argparse
import glob
import os
import os.path as osp
import sysimport imgviz
import numpy as npimport labelmedef main():parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)parser.add_argument("input_dir", help="Input annotated directory")parser.add_argument("output_dir", help="Output dataset directory")parser.add_argument("--labels", help="Labels file or comma separated text", required=True)parser.add_argument("--noobject", help="Flag not to generate object label", action="store_true")parser.add_argument("--nonpy", help="Flag not to generate .npy files", action="store_true")parser.add_argument("--noviz", help="Flag to disable visualization", action="store_true")args = parser.parse_args()if osp.exists(args.output_dir):print("Output directory already exists:", args.output_dir)sys.exit(1)os.makedirs(args.output_dir)os.makedirs(osp.join(args.output_dir, "JPEGImages"))os.makedirs(osp.join(args.output_dir, "SegmentationClass"))if not args.nonpy:os.makedirs(osp.join(args.output_dir, "SegmentationClassNpy"))if not args.noviz:os.makedirs(osp.join(args.output_dir, "SegmentationClassVisualization"))if not args.noobject:os.makedirs(osp.join(args.output_dir, "SegmentationObject"))if not args.nonpy:os.makedirs(osp.join(args.output_dir, "SegmentationObjectNpy"))if not args.noviz:os.makedirs(osp.join(args.output_dir, "SegmentationObjectVisualization"))print("Creating dataset:", args.output_dir)if osp.exists(args.labels):with open(args.labels) as f:labels = [label.strip() for label in f if label]else:labels = [label.strip() for label in args.labels.split(",")]class_names = []class_name_to_id = {}for i, label in enumerate(labels):class_id = i - 1  # starts with -1class_name = label.strip()class_name_to_id[class_name] = class_idif class_id == -1:print("------",class_name)assert class_name == "__ignore__"continueelif class_id == 0:assert class_name == "_background_"class_names.append(class_name)class_names = tuple(class_names)print("class_names:", class_names)out_class_names_file = osp.join(args.output_dir, "class_names.txt")with open(out_class_names_file, "w") as f:f.writelines("\n".join(class_names))print("Saved class_names:", out_class_names_file)for filename in sorted(glob.glob(osp.join(args.input_dir, "*.json"))):print("Generating dataset from:", filename)label_file = labelme.LabelFile(filename=filename)base = osp.splitext(osp.basename(filename))[0]out_img_file = osp.join(args.output_dir, "JPEGImages", base + ".jpg")out_clsp_file = osp.join(args.output_dir, "SegmentationClass", base + ".png")if not args.nonpy:out_cls_file = osp.join(args.output_dir, "SegmentationClassNpy", base + ".npy")if not args.noviz:out_clsv_file = osp.join(args.output_dir,"SegmentationClassVisualization",base + ".jpg",)if not args.noobject:out_insp_file = osp.join(args.output_dir, "SegmentationObject", base + ".png")if not args.nonpy:out_ins_file = osp.join(args.output_dir, "SegmentationObjectNpy", base + ".npy")if not args.noviz:out_insv_file = osp.join(args.output_dir,"SegmentationObjectVisualization",base + ".jpg",)img = labelme.utils.img_data_to_arr(label_file.imageData)imgviz.io.imsave(out_img_file, img)cls, ins = labelme.utils.shapes_to_label(img_shape=img.shape,shapes=label_file.shapes,label_name_to_value=class_name_to_id,)ins[cls == -1] = 0  # ignore it.# class labellabelme.utils.lblsave(out_clsp_file, cls)if not args.nonpy:np.save(out_cls_file, cls)if not args.noviz:clsv = imgviz.label2rgb(cls,imgviz.rgb2gray(img),label_names=class_names,font_size=15,loc="rb",)imgviz.io.imsave(out_clsv_file, clsv)if not args.noobject:# instance labellabelme.utils.lblsave(out_insp_file, ins)if not args.nonpy:np.save(out_ins_file, ins)if not args.noviz:instance_ids = np.unique(ins)instance_names = [str(i) for i in range(max(instance_ids) + 1)]insv = imgviz.label2rgb(ins,imgviz.rgb2gray(img),label_names=instance_names,font_size=15,loc="rb",)imgviz.io.imsave(out_insv_file, insv)if __name__ == "__main__":main()

3.2 转换实例分割标签

在这里插入图片描述
https://github.com/wkentaro/labelme/tree/main/examples/instance_segmentation
在这里插入图片描述

python labelme2voc.py data_annotated data_dataset_voc --labels labels.txt

执行后会生成如下目录:

- data_dataset_voc/JPEGImages
- data_dataset_voc/SegmentationClass
- data_dataset_voc/SegmentationClassPNG
- data_dataset_voc/SegmentationClassVisualization
- data_dataset_voc/SegmentationObject
- data_dataset_voc/SegmentationObjectPNG
- data_dataset_voc/SegmentationObjectVisualization
- data_dataset_voc/class_names.txt

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#!/usr/bin/env pythonfrom __future__ import print_functionimport argparse
import glob
import os
import os.path as osp
import sysimport imgviz
import numpy as npimport labelmedef main():parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)parser.add_argument("input_dir", help="Input annotated directory")parser.add_argument("output_dir", help="Output dataset directory")parser.add_argument("--labels", help="Labels file or comma separated text", required=True)parser.add_argument("--noobject", help="Flag not to generate object label", action="store_true")parser.add_argument("--nonpy", help="Flag not to generate .npy files", action="store_true")parser.add_argument("--noviz", help="Flag to disable visualization", action="store_true")args = parser.parse_args()if osp.exists(args.output_dir):print("Output directory already exists:", args.output_dir)sys.exit(1)os.makedirs(args.output_dir)os.makedirs(osp.join(args.output_dir, "JPEGImages"))os.makedirs(osp.join(args.output_dir, "SegmentationClass"))if not args.nonpy:os.makedirs(osp.join(args.output_dir, "SegmentationClassNpy"))if not args.noviz:os.makedirs(osp.join(args.output_dir, "SegmentationClassVisualization"))if not args.noobject:os.makedirs(osp.join(args.output_dir, "SegmentationObject"))if not args.nonpy:os.makedirs(osp.join(args.output_dir, "SegmentationObjectNpy"))if not args.noviz:os.makedirs(osp.join(args.output_dir, "SegmentationObjectVisualization"))print("Creating dataset:", args.output_dir)if osp.exists(args.labels):with open(args.labels) as f:labels = [label.strip() for label in f if label]else:labels = [label.strip() for label in args.labels.split(",")]class_names = []class_name_to_id = {}for i, label in enumerate(labels):class_id = i - 1  # starts with -1class_name = label.strip()class_name_to_id[class_name] = class_idif class_id == -1:print("------",class_name)assert class_name == "__ignore__"continueelif class_id == 0:assert class_name == "_background_"class_names.append(class_name)class_names = tuple(class_names)print("class_names:", class_names)out_class_names_file = osp.join(args.output_dir, "class_names.txt")with open(out_class_names_file, "w") as f:f.writelines("\n".join(class_names))print("Saved class_names:", out_class_names_file)for filename in sorted(glob.glob(osp.join(args.input_dir, "*.json"))):print("Generating dataset from:", filename)label_file = labelme.LabelFile(filename=filename)base = osp.splitext(osp.basename(filename))[0]out_img_file = osp.join(args.output_dir, "JPEGImages", base + ".jpg")out_clsp_file = osp.join(args.output_dir, "SegmentationClass", base + ".png")if not args.nonpy:out_cls_file = osp.join(args.output_dir, "SegmentationClassNpy", base + ".npy")if not args.noviz:out_clsv_file = osp.join(args.output_dir,"SegmentationClassVisualization",base + ".jpg",)if not args.noobject:out_insp_file = osp.join(args.output_dir, "SegmentationObject", base + ".png")if not args.nonpy:out_ins_file = osp.join(args.output_dir, "SegmentationObjectNpy", base + ".npy")if not args.noviz:out_insv_file = osp.join(args.output_dir,"SegmentationObjectVisualization",base + ".jpg",)img = labelme.utils.img_data_to_arr(label_file.imageData)imgviz.io.imsave(out_img_file, img)cls, ins = labelme.utils.shapes_to_label(img_shape=img.shape,shapes=label_file.shapes,label_name_to_value=class_name_to_id,)ins[cls == -1] = 0  # ignore it.# class labellabelme.utils.lblsave(out_clsp_file, cls)if not args.nonpy:np.save(out_cls_file, cls)if not args.noviz:clsv = imgviz.label2rgb(cls,imgviz.rgb2gray(img),label_names=class_names,font_size=15,loc="rb",)imgviz.io.imsave(out_clsv_file, clsv)if not args.noobject:# instance labellabelme.utils.lblsave(out_insp_file, ins)if not args.nonpy:np.save(out_ins_file, ins)if not args.noviz:instance_ids = np.unique(ins)instance_names = [str(i) for i in range(max(instance_ids) + 1)]insv = imgviz.label2rgb(ins,imgviz.rgb2gray(img),label_names=instance_names,font_size=15,loc="rb",)imgviz.io.imsave(out_insv_file, insv)if __name__ == "__main__":main()

在这里插入图片描述

python labelme2coco.py data_annotated data_dataset_coco --labels labels.txt

执行后会生成如下目录:

- data_dataset_coco/JPEGImages
- data_dataset_coco/annotations.json

代码:
在这里插入图片描述

#!/usr/bin/env pythonimport argparse
import collections
import datetime
import glob
import json
import os
import os.path as osp
import sys
import uuidimport imgviz
import numpy as npimport labelmetry:import pycocotools.mask
except ImportError:print("Please install pycocotools:\n\n    pip install pycocotools\n")sys.exit(1)def main():parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)parser.add_argument("input_dir", help="input annotated directory")parser.add_argument("output_dir", help="output dataset directory")parser.add_argument("--labels", help="labels file", required=True)parser.add_argument("--noviz", help="no visualization", action="store_true")args = parser.parse_args()if osp.exists(args.output_dir):print("Output directory already exists:", args.output_dir)sys.exit(1)os.makedirs(args.output_dir)os.makedirs(osp.join(args.output_dir, "JPEGImages"))if not args.noviz:os.makedirs(osp.join(args.output_dir, "Visualization"))print("Creating dataset:", args.output_dir)now = datetime.datetime.now()data = dict(info=dict(description=None,url=None,version=None,year=now.year,contributor=None,date_created=now.strftime("%Y-%m-%d %H:%M:%S.%f"),),licenses=[dict(url=None,id=0,name=None,)],images=[# license, url, file_name, height, width, date_captured, id],type="instances",annotations=[# segmentation, area, iscrowd, image_id, bbox, category_id, id],categories=[# supercategory, id, name],)class_name_to_id = {}for i, line in enumerate(open(args.labels).readlines()):class_id = i - 1  # starts with -1class_name = line.strip()if class_id == -1:assert class_name == "__ignore__"continueclass_name_to_id[class_name] = class_iddata["categories"].append(dict(supercategory=None,id=class_id,name=class_name,))out_ann_file = osp.join(args.output_dir, "annotations.json")label_files = glob.glob(osp.join(args.input_dir, "*.json"))for image_id, filename in enumerate(label_files):print("Generating dataset from:", filename)label_file = labelme.LabelFile(filename=filename)base = osp.splitext(osp.basename(filename))[0]out_img_file = osp.join(args.output_dir, "JPEGImages", base + ".jpg")img = labelme.utils.img_data_to_arr(label_file.imageData)imgviz.io.imsave(out_img_file, img)data["images"].append(dict(license=0,url=None,file_name=osp.relpath(out_img_file, osp.dirname(out_ann_file)),height=img.shape[0],width=img.shape[1],date_captured=None,id=image_id,))masks = {}  # for areasegmentations = collections.defaultdict(list)  # for segmentationfor shape in label_file.shapes:points = shape["points"]label = shape["label"]group_id = shape.get("group_id")shape_type = shape.get("shape_type", "polygon")mask = labelme.utils.shape_to_mask(img.shape[:2], points, shape_type)if group_id is None:group_id = uuid.uuid1()instance = (label, group_id)if instance in masks:masks[instance] = masks[instance] | maskelse:masks[instance] = maskif shape_type == "rectangle":(x1, y1), (x2, y2) = pointsx1, x2 = sorted([x1, x2])y1, y2 = sorted([y1, y2])points = [x1, y1, x2, y1, x2, y2, x1, y2]if shape_type == "circle":(x1, y1), (x2, y2) = pointsr = np.linalg.norm([x2 - x1, y2 - y1])# r(1-cos(a/2))<x, a=2*pi/N => N>pi/arccos(1-x/r)# x: tolerance of the gap between the arc and the line segmentn_points_circle = max(int(np.pi / np.arccos(1 - 1 / r)), 12)i = np.arange(n_points_circle)x = x1 + r * np.sin(2 * np.pi / n_points_circle * i)y = y1 + r * np.cos(2 * np.pi / n_points_circle * i)points = np.stack((x, y), axis=1).flatten().tolist()else:points = np.asarray(points).flatten().tolist()segmentations[instance].append(points)segmentations = dict(segmentations)for instance, mask in masks.items():cls_name, group_id = instanceif cls_name not in class_name_to_id:continuecls_id = class_name_to_id[cls_name]mask = np.asfortranarray(mask.astype(np.uint8))mask = pycocotools.mask.encode(mask)area = float(pycocotools.mask.area(mask))bbox = pycocotools.mask.toBbox(mask).flatten().tolist()data["annotations"].append(dict(id=len(data["annotations"]),image_id=image_id,category_id=cls_id,segmentation=segmentations[instance],area=area,bbox=bbox,iscrowd=0,))if not args.noviz:viz = imgif masks:labels, captions, masks = zip(*[(class_name_to_id[cnm], cnm, msk)for (cnm, gid), msk in masks.items()if cnm in class_name_to_id])viz = imgviz.instances2rgb(image=img,labels=labels,masks=masks,captions=captions,font_size=15,line_width=2,)out_viz_file = osp.join(args.output_dir, "Visualization", base + ".jpg")imgviz.io.imsave(out_viz_file, viz)with open(out_ann_file, "w") as f:json.dump(data, f)if __name__ == "__main__":main()

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

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

相关文章

从零开始之了解电机及其控制(1)磁场与磁力

&#xff08;链接&#xff1a;从零开始之电机FOC控制_foc电机_一只小白啊的博客-CSDN博客&#xff09;之后&#xff0c;总感觉整个流程都知道&#xff0c;但是深入到具体细节时&#xff0c;就不知所措&#xff0c;感觉啥也不懂一样。 那么为什么要用FOC控制无刷电机呢&#xff…

Unity下tga和png格式图片打包成AB包大小和加载速度测试

测试素材 测试素材&#xff0c;一张tga格式&#xff0c;一张png格式&#xff0c;他们的图像尺寸一样都是8K图。 两张图在AssetBundles里显示 Tga格式的图明显大很多&#xff0c;我们打包成ab包看看。 在PC 打包后看&#xff0c;明显大小一样&#xff0c;我们进行ab包加载&am…

虚拟地址到物理地址的映射(二)

虚拟内存到物理内存的推导 本文只介绍最普遍的64位地址&#xff0c;四级页表&#xff0c;每个页表4k的这种最基本最常见的情况。 linux内核将一个进程的内存映射表建立好之后&#xff0c;在该进程被调度运行的时候&#xff0c;会将PGD的物理地址放置到MMU的页表基地址寄存器中…

参与抖音官方活动:开启抖音小店的曝光与销售新机会

抖音官方活动是指由抖音平台官方组织或合作举办的各类促销活动、品牌推广活动等。参与抖音官方活动对于抖音小店来说&#xff0c;是一个重要的机会&#xff0c;可以帮助店铺获得更多的曝光和销售机会。下面四川不若与众将介绍抖音小店如何参与抖音官方活动的一般步骤和注意事项…

如何解决跨浏览器兼容性问题?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 解决跨浏览器兼容性问题⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量…

EfficientFormer:高效低延迟的Vision Transformers

我们都知道Transformers相对于CNN的架构效率并不高&#xff0c;这导致在一些边缘设备进行推理时延迟会很高&#xff0c;所以这次介绍的论文EfficientFormer号称在准确率不降低的同时可以达到MobileNet的推理速度。 Transformers能否在获得高性能的同时&#xff0c;跑得和Mobile…

Spring MVC 和 Spring Boot 的区别

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

向量数据库库Milvus Cloud2.3 的QA问题

1. Milvus 从 2.2.x 升级至 2.3.x 的最大变化是什么? 如果用一句话来总结,那就是使用的场景更加丰富了。具体可以从两个方面来体现,即部署环境和用户的使用感。 例如,从部署环境来看,Milvus 原来只支持 X86 架构的 CPU,版本升级后,不仅可以支持 GPU,还能够支持 ARM 架构…

十五、异常(2)

本章概要 自定义异常 异常与记录日志 异常声明 自定义异常 不必拘泥于 Java 已有的异常类型。Java异常体系不可能预见你将报告的所有错误&#xff0c;所以你可以创建自己的异常类&#xff0c;来表示你的程序中可能遇到的问题。 要自己定义异常类&#xff0c;必须从已有的异…

HTML详细基础(二)文件路径

目录 一.相对路径 二.绝对路径 三.超链接标签 四.锚点链接 首先&#xff0c;扩展一些HTML执行的原理&#xff1a; htmL(hypertext markup Language) 是一种规范&#xff08;或者说是一种标准&#xff09;&#xff0c;它通过标记符&#xff08;tag&#xff09;来标记要显示…

奇舞周刊第507期:通过 View Transition API 在状态之间添加丰富的过渡动画

记得点击文章末尾的“ 阅读原文 ”查看哟~ 下面先一起看下本期周刊 摘要 吧~ 奇舞推荐 ■ ■ ■ 通过 View Transition API 在状态之间添加丰富的过渡动画 W3C 2023 年度全球技术大会 (TPAC2023) 于今年9月 11 - 15 日召开。W3C CSS 工作组成员 Bramus Van Damme(Google) 为本届…

SpringBoot集成easypoi实现execl导出

<!--easypoi依赖&#xff0c;excel导入导出--><dependency><groupId>cn.afterturn</groupId><artifactId>easypoi-spring-boot-starter</artifactId><version>4.4.0</version></dependency>通过Exce注解设置标头名字和单…

Linux工具(二)

前言&#xff1a;在Linux工具&#xff08;一&#xff09;中&#xff0c;我们学习了yum软件安装工具和vim文本编辑器工具&#xff0c;那么本次我们就再来介绍两种工具&#xff0c;分别是&#xff0c;编辑器gcc/g、项目自动化构建工具-make/Makefile &#xff0c;接着我们再来写一…

React的高阶函数

1.认识高阶函数 高阶组件 本身不是一个组件&#xff0c;而是一个函数函数的参数是一个组件&#xff0c;返回值也是一个组件 高阶组件的定义 import ThemeContext from "../context/theme_context"function withTheme(OriginComponent) {return (props) > {retur…

python安全工具开发笔记(六)——Python爬虫BeautifulSoup模块的介绍

一、Python爬虫基础知识介绍 1.1 Python相关库 1、requests、re 2、BeautifulSoup 3、hackhttp 1.2 Python BeautifulSoup Python BeautifulSoup模块的使用介绍∶ 1、解析内容 from bs4 import BeautifulSoup soup BeautifulSoup(html_doc) 2、浏览数据 soup.title soup…

vue 实现数字验证码功能

需求&#xff1a;写了一个 手机发送验证码后 输入固定验证码的功能 封装成一个组件,如下: <template><div class"conts"><div class"box"><div class"code_list"><div :class"[ code_item, hideIndex 0 ? co…

mysql超级聚合with rollup

超级聚合&#xff0c;是在group by的基础上&#xff0c;再次进行聚合。 它再次聚合的列&#xff0c;是select中没有用到聚合函数的列。 文章目录 例子1解释例子2表以及数据 例子1 mysql> SELECT year, country, product, SUM(profit) AS profitFROM salesGROUP BY year, c…

Vue+ElementUI实现动态树和表格数据的分页模糊查询

目录 前言 一、动态树的实现 1.数据表 2.编写后端controller层 3.定义前端发送请求路径 4.前端左侧动态树的编写 4.1.发送请求获取数据 4.2.遍历左侧菜单 5.实现左侧菜单点击展示右边内容 5.1.定义组件 5.2.定义组件与路由的对应关系 5.3.渲染组件内容 5.4.通过动态…

PHP8中伪变量“$this->”和操作符“::”的使用-PHP8知识详解

对象不仅可以调用自己的变量和方法&#xff0c;也可以调用类中的变量和方法。PHP8通过伪变量“$this->”和操作符“::”来实现这些功能。 1.伪变量“$this->” 在通过对象名->方法调用对象的方法时&#xff0c;如果不知道对象的名称&#xff0c;而又想调用类中的方法…

zabbix实现钉钉报警

首先钉钉创建一个团队 自定义关键词 查看zabbix-server脚本存放的位置&#xff1a; [rootcontrolnode ~]# grep ^AlertScriptsPath /etc/zabbix/zabbix_server.conf AlertScriptsPath/usr/lib/zabbix/alertscripts zabbix server设置 在配置文件书写脚本目录vim /etc/za…