Skip to content

VisDrone 数据集处理与分析详细实操指南 ​

字数
11387 字
阅读时间
50 分钟

目录 ​

  1. 前言与环境准备
  2. WSL2环境与工具准备
  3. 项目环境搭建
  4. 获取VisDrone数据集
  5. 创建样本数据集
  6. 数据可视化
  7. 数据格式转换
  8. 数据探索性分析
  9. 常见问题与解决方案
  10. 扩展应用与后续步骤

1. 前言与环境准备 ​

1.1 实验目标 ​

本实操指南旨在帮助你完成 VisDrone 数据集的准备、可视化、格式转换和探索性分析。我们将使用 dronefreak/VisDrone-dataset-python-toolkit 工具包,适合无人机图像处理和目标检测任务入门学习。

1.2 技能要求 ​

即使你是新手也能完成,只需具备:

  • 基本的命令行操作能力
  • 简单的 Python 知识
  • 对图像处理有基础了解

1.3 环境要求清单 ​

你需要具备以下环境:

  • 操作系统: Windows 10/11 带有 WSL 2
  • 编程环境: Python 3.8 (通过 Conda 安装)
  • 必要软件:
    • Conda (Anaconda/Miniconda)
    • Git
    • 文本编辑器/IDE (如 VS Code)
    • Web 浏览器

2. WSL 2 环境与工具准备 ​

2.1 确认 WSL 2 已安装并支持 GUI ​

步骤 1: 检查 WSL 2 状态

  1. 打开 Windows PowerShell 或命令提示符(管理员权限)
  2. 运行以下命令检查 WSL 版本:
wsl -l -v
  1. 确保你的 Linux 发行版 (如 Ubuntu) 显示为 VERSION 2

如果尚未安装 WSL 2,请参照以下步骤:

# 在PowerShell(管理员)中运行
wsl --install

步骤 2: 确认 WSLg 功能可用 较新版本的 Windows 10/11 已默认支持 WSLg,这使你可以在 WSL 2 中运行 GUI 应用程序。

2.2 安装 Miniconda 到 WSL 2 ​

步骤 1: 打开 WSL 2 终端 在 Windows 搜索栏中输入"Ubuntu"或你安装的其他 Linux 发行版名称,打开终端。

步骤 2: 下载 Miniconda 安装脚本

bash
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh

步骤 3: 运行安装脚本

bash
bash Miniconda3-latest-Linux-x86_64.sh

按照提示操作:

  • 阅读并接受许可协议(输入"yes")
  • 确认安装路径(默认为 ~/miniconda3,推荐使用默认路径)
  • 当询问是否初始化 Miniconda 3 时,输入"yes"

步骤 4: 使更改生效 关闭并重新打开 WSL 2 终端,或运行:

bash
source ~/.bashrc

步骤 5: 验证安装

bash
conda --version

应显示已安装的 Conda 版本号。

2.3 加速配置:使用国内镜像源 ​

配置 pip 使用国内镜像(强烈推荐)

  1. 创建 pip 配置目录和文件:
bash
mkdir -p ~/.pip
  1. 用文本编辑器创建或编辑 ~/.pip/pip.conf 文件:
bash
nano ~/.pip/pip.conf
  1. 添加以下内容:
[global]
index-url = https://pypi.tuna.tsinghua.edu.cn/simple
[install]
trusted-host = pypi.tuna.tsinghua.edu.cn
  1. 保存文件(在 nano 中按 Ctrl+O,然后按 Enter,再按 Ctrl+X 退出)

配置 Conda 使用国内镜像(可选但推荐)

bash
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
conda config --set show_channel_urls yes

2.4 安装系统级依赖 ​

这些依赖对于 OpenCV 的 GUI 功能、Git 和其他工具是必需的:

bash
sudo apt update && sudo apt upgrade -y
sudo apt install -y git protobuf-compiler libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev libtbb2 libtbb-dev libjpeg-dev libpng-dev libtiff-dev libdc1394-22-dev libgtk-3-dev wget unzip

3. 项目环境搭建 ​

3.1 克隆 VisDrone 工具包 ​

步骤 1: 选择工作目录

bash
cd ~  # 或者你希望存放项目的其他目录

步骤 2: 克隆仓库

bash
git clone https://github.com/dronefreak/VisDrone-dataset-python-toolkit.git
cd VisDrone-dataset-python-toolkit

📝 提示: 当前工作目录应为 ~/VisDrone-dataset-python-toolkit

3.2 创建并激活 Conda 环境 ​

步骤 1: 创建名为 visdrone_env 的 Python 3.8 环境

bash
conda create -n visdrone_env python=3.8 -y

步骤 2: 激活环境

bash
conda activate visdrone_env

你的终端提示符前应该出现 (visdrone_env),表示环境已激活。

3.3 安装 Python 依赖包 ​

使用配置好的国内 pip 镜像安装核心库:

bash
pip install numpy opencv-python matplotlib tensorflow jupyter notebook pandas seaborn scikit-learn tqdm

⚠️ 注意: 如果遇到依赖冲突,请尝试先卸载冲突包,例如:

bash
Pip uninstall typing-extensions -y

然后重新运行完整的安装命令。

4. 获取 VisDrone 数据集 ​

VisDrone 数据集包含多个任务数据集,我们主要关注目标检测任务。

4.1 官方数据集信息 ​

数据集来源: 天津大学 AISKYEYE 团队收集的无人机航拍图像和视频数据集。

数据集下载链接:

目标检测训练集(VisDrone-DET trainset, 1.44 GB):

目标检测验证集(VisDrone-DET valset, 0.07 GB):

目标检测测试集-开发版(VisDrone-DET testset-dev, 0.28 GB):

4.2 下载与解压数据集 ​

步骤 1: 创建数据存放目录

bash
mkdir -p ~/VisDrone_datasets
cd ~/VisDrone_datasets

步骤 2: 下载数据集文件 从上述链接下载至少一个数据集(推荐训练集)。 你可以使用以下方式之一:

  • 通过浏览器下载后,将文件移动到 WSL 中
  • 直接在 WSL 中使用 wget 下载(如果有直接链接)

步骤 3: 解压数据集文件

bash
unzip VisDrone2019-DET-train.zip  # 解压训练集,文件名可能不同

数据集结构: 解压后,你会得到一个结构如下的目录:

VisDrone2019-DET-train/
├── annotations/  # 包含.txt标注文件
└── images/       # 包含.jpg图像文件

5. 创建样本数据集 ​

为了快速测试工具包功能,我们将从完整数据集创建一个小型样本数据集。

5.1 创建样本目录结构 ​

步骤 1: 在项目根目录创建样本目录

bash
cd ~/VisDrone-dataset-python-toolkit  # 确保在项目根目录
mkdir -p data/samples/images
mkdir -p data/samples/annotations

5.2 复制样本文件 ​

步骤 1: 选择几个样本文件 从完整数据集中选择 5-10 个图像和对应的标注文件:

bash
# 假设完整数据集位于~/VisDrone_datasets/VisDrone2019-DET-train/
# 复制10个图像文件
ls ~/VisDrone_datasets/VisDrone2019-DET-train/images/ | head -10 | xargs -I{} cp ~/VisDrone_datasets/VisDrone2019-DET-train/images/{} data/samples/images/

# 复制对应的标注文件
ls data/samples/images/ | sed 's/\.jpg/\.txt/g' | xargs -I{} cp ~/VisDrone_datasets/VisDrone2019-DET-train/annotations/{} data/samples/annotations/

步骤 2: 验证样本数据集结构

bash
ls -l data/samples/images/
ls -l data/samples/annotations/

确保图像和标注文件数量相同,且文件名(不含扩展名)一一对应。

6. 数据可视化 ​

现在我们将创建一个脚本来可视化 VisDrone 数据,并在图像上标注目标边界框。

6.1 创建可视化脚本 ​

步骤 1: 创建脚本文件 在项目根目录下创建 enhanced_visualize.py 文件:

bash
nano enhanced_visualize.py

步骤 2: 添加以下代码

python
# enhanced_visualize.py 
import os
import cv2
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
import argparse
from datetime import datetime
import time

# VisDrone类别定义 (中文)
CATEGORIES = {
    0: '忽略区域', 1: '行人', 2: '人群', 3: '自行车', 4: '汽车',
    5: '面包车', 6: '卡车', 7: '三轮车', 8: '遮棚三轮车',
    9: '公交车', 10: '摩托车', 11: '其他'
}

def visualize_image_annotations(image_path, annotation_path, output_dir="visual_output", show_image=False):
    """
    可视化单个图像及其标注,并在图像上添加统计信息。
    """
    if not os.path.exists(image_path):
        print(f"错误: 图像文件未找到 {image_path}")
        return
    if not os.path.exists(annotation_path):
        print(f"错误: 标注文件未找到 {annotation_path}")
        return

    # 读取图像
    image = cv2.imread(image_path)
    image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # Matplotlib 使用 RGB
    img_h, img_w = image.shape[:2]

    # 读取标注
    with open(annotation_path, 'r') as f:
        annotations = [line.strip().split(',') for line in f if line.strip()]

    fig, ax = plt.subplots(1, figsize=(14, 10))
    ax.imshow(image_rgb)
    ax.axis('off') # 不显示坐标轴

    obj_counts = {}
    total_objs = 0

    # 随机颜色映射,确保每个类别颜色不同
    colors = plt.cm.get_cmap('tab20', len(CATEGORIES))

    for ann in annotations:
        if len(ann) < 6: continue # 跳过格式不正确的标注

        x, y, w, h = int(ann[0]), int(ann[1]), int(ann[2]), int(ann[3])
        category_id = int(ann[5])

        # 统计各类物体
        if category_id > 0: # 忽略类别0 (忽略区域)
            category_name = CATEGORIES.get(category_id, f"未知类别{category_id}")
            obj_counts[category_name] = obj_counts.get(category_name, 0) + 1
            total_objs += 1

            # 绘制边界框
            rect_color = colors(category_id % len(CATEGORIES)) # 使用模运算防止索引越界
            rect = Rectangle((x, y), w, h, linewidth=2, edgecolor=rect_color, facecolor='none')
            ax.add_patch(rect)

            # 添加类别标签
            ax.text(x, y - 10, category_name, color='white', fontsize=8,
                    bbox=dict(facecolor=rect_color, alpha=0.7, pad=0.2))

    # 在图像上添加统计信息
    stats_text_lines = [f"图像: {os.path.basename(image_path)}",
                        f"尺寸: {img_w}x{img_h}px",
                        f"总检测目标数: {total_objs}"]
    stats_text_lines.extend([f"- {cat}: {count}" for cat, count in sorted(obj_counts.items())])

    y_offset = 20
    for i, line_text in enumerate(stats_text_lines):
        ax.text(10, y_offset + i * 18, line_text, color='lime', fontsize=9,
                bbox=dict(facecolor='black', alpha=0.6, pad=1))

    # 保存图像
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)

    output_filename = os.path.join(output_dir, f"vis_{os.path.basename(image_path)}")
    plt.savefig(output_filename, bbox_inches='tight', dpi=150)
    print(f"可视化结果已保存到: {output_filename}")

    if show_image:
        plt.show()
    plt.close(fig) # 关闭图像,释放内存

def find_dataset_paths(base_data_dir):
    """尝试在给定目录下找到images和annotations子目录"""
    for root, dirs, _ in os.walk(base_data_dir):
        if "images" in dirs and "annotations" in dirs:
            return os.path.join(root, "images"), os.path.join(root, "annotations")
    print(f"错误: 在 {base_data_dir} 下未找到 'images' 和 'annotations' 目录。")
    return None, None

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="VisDrone 数据集可视化工具")
    parser.add_argument("--data_root", type=str, default="data", help="包含VisDrone数据集的根目录 (如 'data/VisDrone2019-DET-train' 或 'data')")
    parser.add_argument("--output_vis_dir", type=str, default="visual_output", help="保存可视化结果的目录")
    parser.add_argument("--max_vis_images", type=int, default=5, help="最多可视化多少张图片")
    parser.add_argument("--show", action="store_true", help="是否在处理时显示每张图片")
    args = parser.parse_args()

    images_dir, annotations_dir = find_dataset_paths(args.data_root)

    if images_dir and annotations_dir:
        print(f"使用图像目录: {images_dir}")
        print(f"使用标注目录: {annotations_dir}")

        image_files = sorted([f for f in os.listdir(images_dir) if f.lower().endswith(('.jpg', '.png', '.jpeg'))])

        processed_count = 0
        for img_file in image_files:
            if processed_count >= args.max_vis_images:
                break

            base_name = os.path.splitext(img_file)[0]
            image_path = os.path.join(images_dir, img_file)
            annotation_path = os.path.join(annotations_dir, f"{base_name}.txt")

            print(f"\n正在处理: {img_file}...")
            visualize_image_annotations(image_path, annotation_path, args.output_vis_dir, args.show)
            processed_count += 1

        print(f"\n可视化完成! 共处理 {processed_count} 张图片。")
    else:
        print("未能找到数据集路径,请检查 --data_root 参数。")

步骤3: 保存脚本 在nano编辑器中按Ctrl+O然后Enter保存文件,再按Ctrl+X退出。

6.2 运行可视化脚本 ​

运行脚本并处理样本数据

bash
python enhanced_visualize.py --data_root data/samples --max_vis_images 5 --output_vis_dir visualizations_enhanced

命令参数解释:

  • --data_root data/samples: 指向我们手动创建的样本数据目录
  • --max_vis_images 5: 最多处理5张图片(可以根据样本数量调整)
  • --output_vis_dir visualizations_enhanced: 输出目录名称

6.3 检查可视化结果 ​

步骤1: 打开输出目录

bash
ls -l visualizations_enhanced/

步骤2: 查看可视化图像 你可以通过多种方式查看生成的图像:

  1. 使用WSLg的图像查看器:
bash
eog visualizations_enhanced/vis_*.jpg  # 若已安装eog (Eye of GNOME)
# 或
xdg-open visualizations_enhanced/  # 打开文件夹
  1. 通过Windows资源管理器访问: Windows资源管理器可以直接访问WSL文件系统。在Windows地址栏输入:
\\wsl$\Ubuntu\home\你的用户名\VisDrone-dataset-python-toolkit\visualizations_enhanced

这些图像应显示带有彩色边界框、类别标签和统计信息的无人机图像。

7. 数据格式转换 ​

VisDrone 数据集使用其特定的标注格式。不同的目标检测框架可能需要不同的标注格式。

7.1 转换为 PASCAL-VOC 格式 (使用工具包自带脚本) ​

dronefreak/VisDrone-dataset-python-toolkit 工具包内主要包含一个用于将 VisDrone 标注转换为 PASCAL-VOC XML 格式的脚本:convertVis_to_xml.py。PASCAL-VOC 是一种广泛支持的格式。

步骤 1: 确认脚本位置 该脚本 convertVis_to_xml.py 应该位于你克隆的 VisDrone-dataset-python-toolkit 项目的根目录下。

步骤 2: (示例) 执行转换为 PASCAL-VOC 格式 你需要根据 convertVis_to_xml.py 脚本的具体用法来执行。通常,你要指定输入(VisDrone 格式的标注和图像)和输出目录。请查阅该脚本的说明或其仓库的 README 文件获取确切的命令行参数。

把训练库放到工具包的文件夹后运行命令

bash
# 示例命令,具体请参考 convertVis_to_xml.py 的实际用法
python convertVis_to_xml.py
# (可能还需要图像路径等其他参数)

8. 数据探索性分析 ​

使用Jupyter Notebook进行数据探索分析,可视化数据分布特征。

8.1 启动Jupyter Notebook ​

步骤1: 确保在项目根目录

bash
cd ~/VisDrone-dataset-python-toolkit

步骤2: 启动Notebook服务器

bash
jupyter notebook --ip=0.0.0.0 --no-browser

步骤3: 访问Notebook 服务器启动后,终端将显示URL和访问令牌。复制URL(通常类似于http://127.0.0.1:8888/?token=...)到浏览器访问Jupyter界面。

8.2 创建并编写分析Notebook ​

步骤1: 创建新Notebook 在Jupyter界面中:

  1. 点击右上角的"New"按钮
  2. 选择"Python 3"创建新的Notebook
  3. 重命名为"VisDrone_EDA_Samples.ipynb"(点击顶部的"Untitled",输入新名称)

步骤2: 添加代码单元格

单元格1: 导入库和基本设置

python
import os
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import cv2 # OpenCV
from collections import Counter
import glob # 用于查找文件路径
import json

# Matplotlib 和 Seaborn 的美化设置
%matplotlib inline
plt.style.use('ggplot')
sns.set_palette("viridis") # 设置颜色主题
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签 (如果系统有此字体)
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号

print("库导入完成,环境设置完毕!")

单元格2: 定义类别和数据路径

python
# VisDrone类别定义 (与 enhanced_visualize.py 中一致)
CATEGORIES = {
    0: '忽略区域', 1: '行人', 2: '人群', 3: '自行车', 4: '汽车',
    5: '面包车', 6: '卡车', 7: '三轮车', 8: '遮棚三轮车',
    9: '公交车', 10: '摩托车', 11: '其他'
}

# 自动查找数据集路径的函数
def find_dataset_paths_notebook(base_data_dir, subfolder_name=None):
    """在基础目录下查找images和annotations子目录"""
    search_dir = base_data_dir
    if subfolder_name:
        search_dir = os.path.join(base_data_dir, subfolder_name)
  
    # 首先检查直接子目录结构
    images_dir = os.path.join(search_dir, "images")
    annotations_dir = os.path.join(search_dir, "annotations")
  
    if os.path.isdir(images_dir) and os.path.isdir(annotations_dir):
        print(f"在 {search_dir} 直接找到数据集。")
        return images_dir, annotations_dir
  
    # 然后递归搜索
    for root, dirs, _ in os.walk(search_dir):
        if "images" in dirs and "annotations" in dirs:
            print(f"在 {root} 找到数据集。")
            return os.path.join(root, "images"), os.path.join(root, "annotations")
  
    print(f"警告: 在 {search_dir} 下未找到 'images' 和 'annotations' 目录。")
    return None, None

# 设置数据集根目录
dataset_base_dir = "data"
# 为样本数据指定subfolder_name
images_dir, annotations_dir = find_dataset_paths_notebook(dataset_base_dir, subfolder_name="samples")

print(f"图像目录: {images_dir}")
print(f"标注目录: {annotations_dir}")

# 如果未找到,可以直接指定路径
if not images_dir or not annotations_dir:
    images_dir = "data/samples/images"
    annotations_dir = "data/samples/annotations"
    print(f"手动设置 - 图像目录: {images_dir}")
    print(f"手动设置 - 标注目录: {annotations_dir}")

单元格3: 加载和解析标注数据

python
all_annotations_data = []

if annotations_dir and os.path.exists(annotations_dir):
    annotation_files = sorted(glob.glob(os.path.join(annotations_dir, "*.txt")))
    print(f"找到 {len(annotation_files)} 个标注文件。")

    for ann_file_path in annotation_files:
        image_filename_base = os.path.splitext(os.path.basename(ann_file_path))[0]

        with open(ann_file_path, 'r') as f:
            for line in f:
                parts = line.strip().split(',')
                if len(parts) < 6: continue # 确保标注格式基本正确

                try:
                    bbox_left = int(parts[0])
                    bbox_top = int(parts[1])
                    bbox_width = int(parts[2])
                    bbox_height = int(parts[3])
                    # score = int(parts[4]) # 根据VisDrone格式,score为1表示物体,0表示忽略
                    category_id = int(parts[5])
                    # truncation = int(parts[6])
                    # occlusion = int(parts[7])

                    # 计算面积和长宽比
                    area = bbox_width * bbox_height
                    aspect_ratio = bbox_width / bbox_height if bbox_height > 0 else 0

                    all_annotations_data.append({
                        'image_id': image_filename_base,
                        'category_id': category_id,
                        'category_name': CATEGORIES.get(category_id, f"未知{category_id}"),
                        'x': bbox_left,
                        'y': bbox_top,
                        'width': bbox_width,
                        'height': bbox_height,
                        'area': area,
                        'aspect_ratio': aspect_ratio
                    })
                except ValueError:
                    print(f"警告: 解析标注行失败 {parts} in {ann_file_path}")
                    continue
else:
    print("错误: 标注目录未找到或不存在,无法加载数据。")
    
# 创建DataFrame
df_annotations = pd.DataFrame(all_annotations_data)

if not df_annotations.empty:
    print(f"\n成功加载 {len(df_annotations)} 条标注数据。")
    print("DataFrame 前5行预览:")
    display(df_annotations.head())
    print("\nDataFrame 基本信息:")
    df_annotations.info()
else:
    print("\n未能加载任何标注数据到DataFrame。")

单元格4: 目标类别分布统计与可视化

python
if not df_annotations.empty and 'category_name' in df_annotations.columns:
    # 过滤掉"忽略区域"进行主要类别分析
    df_objects_only = df_annotations[df_annotations['category_id'] != 0]

    plt.figure(figsize=(12, 7))
    category_counts = df_objects_only['category_name'].value_counts()
    sns.barplot(x=category_counts.index, y=category_counts.values)
    plt.title('数据集中各目标类别数量分布 (不含忽略区域)', fontsize=16)
    plt.xlabel('目标类别', fontsize=12)
    plt.ylabel('数量', fontsize=12)
    plt.xticks(rotation=45, ha="right", fontsize=10)
    plt.tight_layout() # 调整布局以防止标签重叠
    plt.show()

    print("\n各类别数量:")
    print(category_counts)
else:
    print("DataFrame为空或缺少'category_name'列,无法进行类别分布分析。")

单元格5: 目标区域面积分布

python
if not df_annotations.empty and 'area' in df_annotations.columns:
    # 确保df_objects_only已定义
    if 'df_objects_only' not in locals():
        df_objects_only = df_annotations[df_annotations['category_id'] != 0]
      
    plt.figure(figsize=(10, 6))
    # 使用对数刻度可能有助于观察广泛分布的面积
    sns.histplot(df_objects_only['area'], bins=50, kde=True, log_scale=(False, True)) # Y轴对数刻度
    plt.title('目标边界框面积分布', fontsize=16)
    plt.xlabel('面积 (像素平方)', fontsize=12)
    plt.ylabel('频数 (对数刻度)', fontsize=12)
    plt.show()

    print("\n面积描述性统计:")
    print(df_objects_only['area'].describe())
else:
    print("DataFrame为空或缺少'area'列,无法进行面积分布分析。")

单元格6: 每张图像中的目标数量分布

python
if not df_annotations.empty and 'image_id' in df_annotations.columns:
    # 确保df_objects_only已定义
    if 'df_objects_only' not in locals():
        df_objects_only = df_annotations[df_annotations['category_id'] != 0]
      
    objects_per_image = df_objects_only.groupby('image_id').size()

    plt.figure(figsize=(10, 6))
    sns.histplot(objects_per_image, bins=30, kde=False)
    plt.title('每张图像中的目标数量分布', fontsize=16)
    plt.xlabel('图像中的目标数量', fontsize=12)
    plt.ylabel('图像数量', fontsize=12)
    plt.show()

    print("\n每张图像目标数量描述性统计:")
    print(objects_per_image.describe())
else:
    print("DataFrame为空或缺少'image_id'列,无法进行图像目标数量分析。")

单元格7: 目标边界框长宽比分布

python
if not df_annotations.empty and 'aspect_ratio' in df_annotations.columns:
    # 确保df_objects_only已定义
    if 'df_objects_only' not in locals():
        df_objects_only = df_annotations[df_annotations['category_id'] != 0]
  
    # 过滤掉极端或无效的长宽比值,例如 area=0 或 height=0 导致的长宽比为0或inf
    valid_aspect_ratios = df_objects_only[(df_objects_only['aspect_ratio'] > 0) &
                                          (df_objects_only['aspect_ratio'] < 10)]['aspect_ratio']
                                          # 设定一个合理的上限,如10

    plt.figure(figsize=(10, 6))
    sns.histplot(valid_aspect_ratios, bins=50, kde=True)
    plt.title('目标边界框长宽比分布 (0 < AR < 10)', fontsize=16)
    plt.xlabel('长宽比 (宽度/高度)', fontsize=12)
    plt.ylabel('频数', fontsize=12)
    plt.show()

    print("\n长宽比描述性统计:")
    print(valid_aspect_ratios.describe())
else:
    print("DataFrame为空或缺少'aspect_ratio'列,无法进行长宽比分析。")

单元格8: 不同类别的目标尺寸比较 (箱线图)

python
if not df_annotations.empty and 'category_name' in df_annotations.columns and 'area' in df_annotations.columns:
    # 确保df_objects_only已定义
    if 'df_objects_only' not in locals():
        df_objects_only = df_annotations[df_annotations['category_id'] != 0]
  
    # 选择前6个最常见的类别,以避免图表过于拥挤
    top_categories = df_objects_only['category_name'].value_counts().nlargest(6).index.tolist()
    df_top_categories = df_objects_only[df_objects_only['category_name'].isin(top_categories)]
  
    plt.figure(figsize=(14, 8))
    sns.boxplot(x='category_name', y='area', data=df_top_categories)
    plt.title('不同类别目标的面积分布 (箱线图)', fontsize=16)
    plt.xlabel('目标类别', fontsize=12)
    plt.ylabel('面积 (像素平方)', fontsize=12)
    plt.yscale('log')  # 对数刻度更容易看清差异
    plt.xticks(rotation=45, ha="right")
    plt.tight_layout()
    plt.show()
else:
    print("DataFrame为空或缺少必要列,无法进行类别尺寸比较分析。")

单元格9: 热力图 - 目标位置分布

python
if not df_annotations.empty and 'x' in df_annotations.columns and 'y' in df_annotations.columns:
    # 确保df_objects_only已定义
    if 'df_objects_only' not in locals():
        df_objects_only = df_annotations[df_annotations['category_id'] != 0]
  
    # 创建位置热力图
    plt.figure(figsize=(12, 10))
  
    # 计算目标的中心点
    df_objects_only['center_x'] = df_objects_only['x'] + df_objects_only['width'] / 2
    df_objects_only['center_y'] = df_objects_only['y'] + df_objects_only['height'] / 2
  
    # 使用KDE图显示目标位置分布
    sns.kdeplot(
        x=df_objects_only['center_x'],
        y=df_objects_only['center_y'],
        cmap="hot",
        fill=True,
        thresh=0,
        levels=100
    )
  
    plt.title('目标位置分布热力图', fontsize=16)
    plt.xlabel('图像X坐标', fontsize=12)
    plt.ylabel('图像Y坐标', fontsize=12)
    plt.colorbar(label='密度')
    plt.show()
else:
    print("DataFrame为空或缺少必要的坐标列,无法创建位置热力图。")

步骤3: 运行并分析结果 依次执行每个代码单元格(点击单元格,然后按Shift+Enter或单击工具栏中的运行按钮)。

观察每个可视化结果,了解数据集的以下特性:

  • 各类别对象的分布情况
  • 目标尺寸(面积)的分布特征
  • 每张图像中目标数量的分布
  • 目标形状特征(通过长宽比)
  • 不同类别目标的尺寸差异
  • 目标在图像中的位置分布

9. 常见问题与解决方案 ​

9.1 中文字体显示问题 ​

问题: 可视化图表中的中文显示为方块或乱码。

解决方案:

  1. 安装中文字体包:
bash
sudo apt install -y fonts-wqy-zenhei fonts-wqy-microhei
  1. 更新字体缓存:
bash
fc-cache -fv
  1. 在代码中明确指定可用的中文字体:
python
plt.rcParams['font.sans-serif'] = ['WenQuanYi Zen Hei', 'WenQuanYi Micro Hei', 'SimHei', 'AR PL UMing CN']

9.2 依赖冲突问题 ​

问题: 安装Python包时出现依赖冲突错误。

解决方案:

  1. 创建新的干净环境:
bash
conda create -n visdrone_new python=3.8 -y
conda activate visdrone_new
  1. 按特定顺序安装依赖:
bash
pip install numpy
pip install opencv-python matplotlib
pip install tensorflow jupyter notebook pandas seaborn scikit-learn tqdm

9.3 内存不足问题 ​

问题: 处理大型数据集时遇到内存不足(特别是在Jupyter Notebook中)。

解决方案:

  1. 分批处理数据:
python
# 示例:分批读取与处理
batch_size = 1000
for i in range(0, len(all_files), batch_size):
    batch_files = all_files[i:i+batch_size]
    # 处理这个批次
    process_batch(batch_files)
    # 清理内存
    gc.collect()
  1. 减少加载的数据量:
python
# 随机抽样10%的数据进行分析
sampled_files = random.sample(all_files, int(len(all_files) * 0.1))

9.4 找不到文件或目录 ​

问题: 脚本无法找到指定的数据集目录或文件。

解决方案:

  1. 使用绝对路径:
bash
python enhanced_visualize.py --data_root ~/VisDrone_datasets/VisDrone2019-DET-train
  1. 手动检查文件结构:
bash
ls -la ~/VisDrone_datasets/  # 检查顶层目录
ls -la ~/VisDrone_datasets/VisDrone2019-DET-train/  # 检查子目录
find ~/VisDrone_datasets/ -name "*.jpg" | head  # 检查是否有jpg文件

9.5 WSLg显示问题 ​

问题: 在WSL环境下,尝试运行需要图形界面的Python脚本(例如使用OpenCV的 cv2.imshow() 进行图像可视化)时,无法正常显示可视化窗口,或者出现类似 "cannot open display" 的错误。

解决方案:

以下是几种排查和解决WSLg显示问题的方法,建议按顺序尝试:

  • 方法一:检查并尝试设置 DISPLAY 变量 (推荐首先尝试)

    1. 检查当前 DISPLAY 值: 在你的 WSL 终端 (激活了 visdrone_env 的那个),检查当前的 DISPLAY 环境变量:

      bash
      (visdrone_env) your_user@your_machine:~$ echo $DISPLAY

      根据你遇到的错误,它可能会输出一个具体的IP地址和端口号,例如 172.23.176.1:0。

    2. 尝试 WSLg 默认 DISPLAY 值: 如果你的 Windows 10/11 版本较新,应该支持 WSLg。WSLg 通常期望 DISPLAY 变量设置为 :0 (指向 WSLg 内部的 Wayland compositor)。尝试手动设置它:

      bash
      (visdrone_env) your_user@your_machine:~$ export DISPLAY=:0
    3. 测试脚本: 在同一个终端中,立即再次尝试运行你的 GUI 测试脚本 (例如 test_cv_gui.py 或其他包含 cv2.imshow() 的可视化脚本):

      bash
      (visdrone_env) your_user@your_machine:~$ python your_gui_script.py
    4. 结果判断与持久化设置:

      • 如果这样可行 (脚本成功显示了图形界面):
        • 这说明 WSLg 已激活,问题通过设置 DISPLAY=:0 解决了。
        • 为了方便,你可以将 export DISPLAY=:0 添加到你的 WSL shell 配置文件中,使其在每次打开新终端时自动生效。
          • 如果你使用 bash (默认shell):
            bash
            echo 'export DISPLAY=:0' >> ~/.bashrc
            source ~/.bashrc  # 使更改立即生效或重新打开终端
          • 如果你使用 zsh:
            bash
            echo 'export DISPLAY=:0' >> ~/.zshrc
            source ~/.zshrc  # 使更改立即生效或重新打开终端
        • 注意: 虽然 WSLg 旨在自动处理 DISPLAY变量,但在某些情况下,手动设置可能有所帮助。如果将来 WSLg 的行为发生变化,或者你使用了其他 X 服务器(如 VcXsrv 并配置了特定的 IP 地址),这个固定的设置可能会与之冲突,届时你可能需要从配置文件中移除或注释掉这一行。
      • 如果仍然不行:继续尝试下面的方法。
  • 方法二:确认 WSLg 基础功能是否正常

    1. 安装 x11-apps (如果尚未安装): 这个软件包包含一些简单的 X11 图形应用程序,可以用来测试基础的图形显示功能。

      bash
      sudo apt update
      sudo apt install -y x11-apps
    2. 运行 xeyes 测试:

      bash
      xeyes
    3. 结果判断:

      • 如果屏幕上显示了一双跟随鼠标指针移动的眼睛图标,那么 WSLg 的基础图形显示功能是正常的。问题可能更多地与你的 Python 环境、OpenCV 的 GUI 后端或其他特定于应用的配置有关。
      • 如果 xeyes 也不显示,或者报错,那么 WSLg 本身可能没有正确启动或配置。你需要:
        • 检查你的 Windows 版本是否支持 WSLg (通常需要 Windows 10 build 21364+ 或 Windows 11)。
        • 确保 WSL 已更新到最新版本 (wsl --update)。
        • 检查 WSLg 的相关组件是否正常运行。
  • 方法三:间接验证——通过文件系统查看生成的图像 如果你的脚本的主要目的是处理数据并生成图像文件(即使可视化窗口无法显示),你可以检查文件是否已按预期生成。

    1. 定位输出目录: 确定你的脚本将可视化结果保存到了哪个目录。例如,教程中提到的路径是 VisDrone-dataset-python-toolkit/visualizations_enhanced。

    2. 通过 Windows 文件资源管理器访问: 在 Windows 文件资源管理器的地址栏中,输入以下格式的路径(请将 Ubuntu 替换为你的WSL发行版名称,你的用户名 替换为你的实际 WSL 用户名,并根据实际情况调整后续路径):

      \\wsl$\Ubuntu\home\你的用户名\VisDrone-dataset-python-toolkit\visualizations_enhanced

      或者,如果你的脚本将文件保存在WSL中挂载的Windows驱动器路径下 (例如 /mnt/c/Users/YourWindowsUser/output_images),你可以直接在Windows中导航到该路径 (C:\Users\YourWindowsUser\output_images)。

    3. 检查文件: 查看该目录下是否有新生成的图像文件。这可以帮助判断脚本的核心逻辑(不包括GUI显示部分)是否在正常工作。如果文件存在且内容正确,那么问题主要集中在GUI的显示环节。

进一步排查方向 (如果以上方法均无效):

  • Python OpenCV GUI 后端: 确保你安装的 OpenCV (通常是 opencv-python 包) 是带有 GUI 支持的版本。opencv-python-headless 版本不包含 GUI 功能。
  • 虚拟环境依赖: 再次确认在 visdrone_env 虚拟环境中,所有必要的依赖项(包括 opencv-python)都已正确安装且版本兼容。
  • Windows 防火墙或安全软件: 极少数情况下,防火墙或安全软件可能会干扰 WSLg 的通信。
  • 查看详细错误信息: 当 GUI 脚本失败时,仔细阅读终端中输出的完整错误信息,它们通常会包含定位问题的关键线索。
  • 重启WSL: 尝试完全关闭并重启WSL服务:在PowerShell或CMD中运行 wsl --shutdown,然后重新打开WSL终端。

10. 扩展应用与后续步骤 ​

完成本教程后,你可以进一步探索以下方向:

10.1 处理完整数据集 ​

步骤1: 使用完整数据集运行工具

bash
# 可视化
python enhanced_visualize.py --data_root ~/VisDrone_datasets/VisDrone2019-DET-train --max_vis_images 20 --output_vis_dir full_visualizations

# 格式转换
python visdrone2coco.py --data_dir ~/VisDrone_datasets/VisDrone2019-DET-train --output_dir full_coco_output
python visdrone2yolo.py --data_dir ~/VisDrone_datasets/VisDrone2019-DET-train --output_dir full_yolo_output

步骤2: 对完整数据集进行分析 创建新的Jupyter Notebook,使用与第8节相同的代码,但将dataset_base_dir调整为完整数据集路径。

10.2 训练目标检测模型 ​

使用转换后的数据集训练深度学习目标检测模型。

YOLOv5示例:

bash
# 克隆YOLOv5仓库
git clone https://github.com/ultralytics/yolov5
cd yolov5

# 安装依赖
pip install -r requirements.txt

# 创建数据配置文件 (visdrone.yaml)
# 使用我们转换的YOLO格式数据训练
python train.py --img 640 --batch 16 --epochs 50 --data visdrone.yaml --weights yolov5s.pt

COCO格式与TensorFlow Object Detection API示例: 可以参考TensorFlow官方文档,使用我们转换的COCO格式数据。

10.3 自定义数据处理工具 ​

开发自己的脚本来扩展VisDrone工具包功能。

示例: 数据增强脚本

python
# data_augmentation.py
import cv2
import numpy as np
import os
import glob
from tqdm import tqdm
import imgaug.augmenters as iaa
import imgaug as ia
from imgaug.augmentables.bbs import BoundingBox, BoundingBoxesOnImage

# 定义增强序列
seq = iaa.Sequential([
    iaa.Fliplr(0.5),  # 水平翻转
    iaa.Affine(rotate=(-10, 10)),  # 旋转
    iaa.AddToBrightness((-30, 30)),  # 亮度变化
    iaa.GaussianBlur(sigma=(0, 0.5)),  # 轻微模糊
    iaa.Sometimes(0.3, iaa.Rain(speed=(0.1, 0.3)))  # 随机添加雨天效果,增强无人机拍摄场景多样性
])

def augment_dataset(
    input_images_dir, 
    input_annotations_dir, 
    output_dir, 
    augmentations_per_image=3
):
    """对VisDrone数据集进行数据增强
  
    参数:
        input_images_dir: 输入图像目录
        input_annotations_dir: 输入标注目录
        output_dir: 输出目录
        augmentations_per_image: 每张原始图像生成的增强图像数量
    """
    # 创建输出目录
    output_images_dir = os.path.join(output_dir, "images")
    output_annotations_dir = os.path.join(output_dir, "annotations")
    os.makedirs(output_images_dir, exist_ok=True)
    os.makedirs(output_annotations_dir, exist_ok=True)
  
    # 获取所有图像文件
    image_files = glob.glob(os.path.join(input_images_dir, "*.jpg")) + \
                 glob.glob(os.path.join(input_images_dir, "*.png"))
  
    for img_path in tqdm(image_files, desc="增强数据集"):
        try:
            # 读取图像
            img = cv2.imread(img_path)
            if img is None:
                print(f"警告: 无法读取图像 {img_path}")
                continue
              
            # 获取对应的标注文件
            basename = os.path.splitext(os.path.basename(img_path))[0]
            ann_path = os.path.join(input_annotations_dir, f"{basename}.txt")
          
            if not os.path.exists(ann_path):
                print(f"警告: 找不到对应的标注文件 {ann_path}")
                continue
              
            # 读取标注
            bboxes = []
            bbox_categories = []
            with open(ann_path, 'r') as f:
                for line in f:
                    parts = line.strip().split(',')
                    if len(parts) >= 6:  # VisDrone格式
                        try:
                            x, y, w, h = map(int, parts[:4])
                            category_id = int(parts[5])
                          
                            # 排除忽略区域(category_id=0)或无效框
                            if category_id > 0 and w > 0 and h > 0:
                                bboxes.append(BoundingBox(x1=x, y1=y, x2=x+w, y2=y+h))
                                bbox_categories.append(category_id)
                        except (ValueError, IndexError):
                            continue
          
            # 初始化BoundingBoxesOnImage对象
            bbs = BoundingBoxesOnImage(bboxes, shape=img.shape)
          
            # 生成多个增强版本
            for aug_idx in range(augmentations_per_image):
                # 应用数据增强
                aug_img, aug_bbs = seq(image=img, bounding_boxes=bbs)
              
                # 确保所有边界框都在图像边界内
                aug_bbs = aug_bbs.remove_out_of_image().clip_out_of_image()
              
                # 保存增强后的图像
                aug_image_filename = f"{basename}_aug{aug_idx}.jpg"
                aug_image_path = os.path.join(output_images_dir, aug_image_filename)
                cv2.imwrite(aug_image_path, aug_img)
              
                # 保存增强后的标注
                aug_annotation_filename = f"{basename}_aug{aug_idx}.txt"
                aug_annotation_path = os.path.join(output_annotations_dir, aug_annotation_filename)
              
                with open(aug_annotation_path, 'w') as f:
                    valid_box_count = 0
                    for i, bbox in enumerate(aug_bbs.bounding_boxes):
                        if i >= len(bbox_categories):
                            break
                          
                        # 取整数值
                        x1, y1, x2, y2 = map(int, [bbox.x1, bbox.y1, bbox.x2, bbox.y2])
                        w, h = x2 - x1, y2 - y1
                      
                        # 排除无效框
                        if w <= 1 or h <= 1:
                            continue
                          
                        # VisDrone格式:
                        # <bbox_left>,<bbox_top>,<bbox_width>,<bbox_height>,<score>,<object_category>,<truncation>,<occlusion>
                        f.write(f"{x1},{y1},{w},{h},1,{bbox_categories[i]},0,0\n")
                        valid_box_count += 1
              
                if valid_box_count == 0:
                    # 如果没有有效框,删除这个增强样本
                    os.remove(aug_image_path)
                    os.remove(aug_annotation_path)
          
        except Exception as e:
            print(f"处理 {img_path} 时出错: {e}")
            continue
  
    print(f"数据增强完成! 增强后的数据保存在 {output_dir}")

# 示例调用
if __name__ == "__main__":
    import argparse
  
    parser = argparse.ArgumentParser(description="VisDrone数据集增强工具")
    parser.add_argument("--input_dir", type=str, required=True, help="输入数据集目录")
    parser.add_argument("--output_dir", type=str, required=True, help="输出增强数据集目录")
    parser.add_argument("--augmentations", type=int, default=3, help="每张图像的增强版本数量")
    args = parser.parse_args()
  
    # 查找images和annotations子目录
    base_dir = args.input_dir
    for root, dirs, _ in os.walk(base_dir):
        if "images" in dirs and "annotations" in dirs:
            input_images_dir = os.path.join(root, "images")
            input_annotations_dir = os.path.join(root, "annotations")
            print(f"找到数据集: {root}")
          
            augment_dataset(
                input_images_dir, 
                input_annotations_dir, 
                args.output_dir, 
                args.augmentations
            )
            break
    else:
        print(f"错误: 在 {base_dir} 下未找到 'images' 和 'annotations' 目录结构")

这个脚本可以帮助增强VisDrone数据集,通过添加旋转、翻转、亮度变化和天气效果等,提高模型的泛化能力,尤其是对无人机在各种天气条件下捕获的图像。

11. 在实际项目中的应用案例 ​

11.1 智慧城市场景 ​

  1. 使用VisDrone数据集训练道路交通监控系统
python
# 使用车辆与行人类别的子集生成专用数据集
def create_traffic_monitoring_subset():
    import shutil
    from tqdm import tqdm
  
    source_images_dir = "data/VisDrone2019-DET-train/images"
    source_annots_dir = "data/VisDrone2019-DET-train/annotations"
    target_dir = "data/traffic_monitoring_subset"
  
    # 创建目标目录
    target_images_dir = os.path.join(target_dir, "images")
    target_annots_dir = os.path.join(target_dir, "annotations")
    os.makedirs(target_images_dir, exist_ok=True)
    os.makedirs(target_annots_dir, exist_ok=True)
  
    # 目标类别:行人(1), 人群(2), 汽车(4), 面包车(5), 卡车(6), 公交车(9)
    target_categories = {1, 2, 4, 5, 6, 9}
  
    ann_files = os.listdir(source_annots_dir)
    for ann_file in tqdm(ann_files, desc="创建交通监控子集"):
        ann_path = os.path.join(source_annots_dir, ann_file)
      
        # 读取标注文件,检查是否包含目标类别
        relevant_objects = 0
        filtered_lines = []
      
        with open(ann_path, 'r') as f:
            for line in f:
                parts = line.strip().split(',')
                if len(parts) >= 6:
                    category_id = int(parts[5])
                    if category_id in target_categories:
                        relevant_objects += 1
                        filtered_lines.append(line)
      
        # 如果图像包含至少3个相关目标,将其添加到子集
        if relevant_objects >= 3:
            # 复制图像
            img_name = os.path.splitext(ann_file)[0] + ".jpg"
            img_src = os.path.join(source_images_dir, img_name)
            img_dst = os.path.join(target_images_dir, img_name)
          
            if os.path.exists(img_src):
                shutil.copy(img_src, img_dst)
              
                # 创建过滤后的标注文件
                ann_dst = os.path.join(target_annots_dir, ann_file)
                with open(ann_dst, 'w') as f:
                    f.writelines(filtered_lines)
                  
    print(f"交通监控子集已创建在 {target_dir}")

11.2 农业监测应用 ​

  1. 使用VisDrone数据集训练无人机农田巡检系统
python
# 修改本教程中的数据集转换器,添加农田监测特定类别
def customize_for_agriculture(output_label_map_path):
    """创建农业监测的标签映射文件"""
    # 原始VisDrone类别
    visdrone_categories = {
        0: 'ignored-regions', 1: 'pedestrian', 2: 'people', 3: 'bicycle',
        4: 'car', 5: 'van', 6: 'truck', 7: 'tricycle', 8: 'awning-tricycle',
        9: 'bus', 10: 'motor', 11: 'others'
    }
  
    # 为农业监测重新映射类别
    agriculture_mapping = {
        # 保留部分原始类别
        4: 'farm-vehicle',       # 原car变为farm-vehicle
        5: 'trailer',            # 原van变为trailer
        6: 'harvester',          # 原truck变为harvester
        7: 'tractor',            # 原tricycle变为tractor
        1: 'farm-worker',        # 原pedestrian变为farm-worker
        10: 'irrigation-device', # 原motor变为irrigation-device
        11: 'crop-anomaly'       # 原others变为crop-anomaly
    }
  
    # 写入新的标签映射文件
    with open(output_label_map_path, 'w') as f:
        for id, name in agriculture_mapping.items():
            f.write(f"item {{\n")
            f.write(f"  id: {id}\n")
            f.write(f"  name: '{name}'\n")
            f.write(f"}}\n\n")
  
    print(f"农业监测标签映射已保存至 {output_label_map_path}")
    return agriculture_mapping

11.3 安全监控场景 ​

  1. 使用VisDrone数据集训练无人机安全巡检系统
python
def detect_zone_intrusion(model_path, test_image_path, restricted_zone_coords):
    """使用训练好的模型检测限制区域内的入侵行为
  
    参数:
        model_path: 训练好的目标检测模型路径
        test_image_path: 测试图像路径
        restricted_zone_coords: 限制区域的多边形坐标,格式为 [(x1,y1), (x2,y2), ...]
    """
    import cv2
    import numpy as np
    import tensorflow as tf
    from shapely.geometry import Point, Polygon
  
    # 加载模型
    model = tf.saved_model.load(model_path)
    detect_fn = model.signatures['serving_default']
  
    # 读取图像
    image = cv2.imread(test_image_path)
    image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    input_tensor = tf.convert_to_tensor([image_rgb], dtype=tf.uint8)
  
    # 执行检测
    detections = detect_fn(input_tensor)
  
    # 处理检测结果
    boxes = detections['detection_boxes'][0].numpy()
    scores = detections['detection_scores'][0].numpy()
    classes = detections['detection_classes'][0].numpy().astype(np.int32)
  
    # 创建限制区域多边形
    restricted_zone = Polygon(restricted_zone_coords)
  
    # 设置检测阈值
    threshold = 0.5
  
    # 检测结果处理和可视化
    h, w, _ = image.shape
    intrusion_detected = False
  
    # 绘制限制区域
    zone_points = np.array(restricted_zone_coords, np.int32)
    zone_points = zone_points.reshape((-1, 1, 2))
    cv2.polylines(image, [zone_points], True, (0, 0, 255), 2)
  
    for i in range(len(scores)):
        if scores[i] > threshold:
            # 获取实际像素坐标
            box = boxes[i]
            y1, x1, y2, x2 = int(box[0] * h), int(box[1] * w), int(box[2] * h), int(box[3] * w)
          
            # 计算边界框的中心点
            center_x = (x1 + x2) // 2
            center_y = (y1 + y2) // 2
            center_point = Point(center_x, center_y)
          
            # 检查中心点是否在限制区域内
            if restricted_zone.contains(center_point):
                # 区域内入侵对象用红色显示
                cv2.rectangle(image, (x1, y1), (x2, y2), (0, 0, 255), 2)
                label = f"Class: {classes[i]}, Score: {scores[i]:.2f}, INTRUSION!"
                cv2.putText(image, label, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
                intrusion_detected = True
            else:
                # 区域外对象用绿色显示
                cv2.rectangle(image, (x1, y1), (x2, y2), (0, 255, 0), 2)
                label = f"Class: {classes[i]}, Score: {scores[i]:.2f}"
                cv2.putText(image, label, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
  
    # 在图像上显示警告状态
    if intrusion_detected:
        warning_text = "⚠️ INTRUSION DETECTED ⚠️"
        cv2.putText(image, warning_text, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
  
    # 保存结果
    result_path = test_image_path.replace('.jpg', '_intrusion_result.jpg')
    cv2.imwrite(result_path, image)
    print(f"结果已保存至 {result_path}")
  
    return intrusion_detected, result_path

11.4 工业应用示例 ​

  1. 使用 VisDrone 数据集训练矿区安全监控系统
python
# 矿区无人机监控示例使用案例
def mining_site_monitoring_demo():
    # 示例代码:矿区监控应用
    import cv2
    import numpy as np
    import matplotlib.pyplot as plt
    from matplotlib.patches import Rectangle
  
    def analyze_mining_site_image(image_path, model):
        """分析矿区航拍图像"""
        # 读取图像
        image = cv2.imread(image_path)
        image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
      
        # 模拟目标检测结果
        # 实际应用中,这里会调用加载好的模型进行检测
        detections = {
            'equipment': [(150, 200, 50, 30), (300, 250, 70, 40)],  # x, y, w, h
            'vehicle': [(450, 300, 60, 30), (200, 400, 80, 50)],
            'personnel': [(100, 150, 20, 40), (350, 180, 20, 40), (500, 350, 20, 40)]
        }
      
        # 创建可视化
        plt.figure(figsize=(12, 10))
        plt.imshow(image_rgb)
      
        # 绘制检测结果
        colors = {'equipment': 'blue', 'vehicle': 'green', 'personnel': 'red'}
        for obj_type, boxes in detections.items():
            for box in boxes:
                x, y, w, h = box
                rect = Rectangle((x, y), w, h, linewidth=2, edgecolor=colors[obj_type], facecolor='none')
                plt.gca().add_patch(rect)
                plt.text(x, y-5, obj_type, color=colors[obj_type], fontsize=10)
      
        # 检查危险情况:人员与设备距离过近
        safety_issues = []
        for person_box in detections['personnel']:
            px, py, pw, ph = person_box
            person_center = (px + pw/2, py + ph/2)
          
            # 检查与设备的距离
            for equip_box in detections['equipment']:
                ex, ey, ew, eh = equip_box
                equip_center = (ex + ew/2, ey + eh/2)
              
                # 计算距离
                distance = np.sqrt((person_center[0] - equip_center[0])**2 + 
                                  (person_center[1] - equip_center[1])**2)
              
                # 如果距离小于阈值,标记为不安全
                if distance < 100:  # 像素距离阈值
                    safety_issues.append((person_box, equip_box))
                    # 绘制连接线表示危险关系
                    plt.plot([person_center[0], equip_center[0]], 
                             [person_center[1], equip_center[1]], 
                             'y--', linewidth=2)
      
        plt.title('矿区安全监控分析')
        plt.axis('off')
      
        # 添加图例
        for obj_type, color in colors.items():
            plt.plot([], [], color=color, label=obj_type)
        if safety_issues:
            plt.plot([], [], 'y--', label='安全隐患')
        plt.legend()
      
        # 保存结果
        result_path = image_path.replace('.jpg', '_mining_analysis.jpg')
        plt.savefig(result_path, dpi=300, bbox_inches='tight')
      
        return {
            'total_equipment': len(detections['equipment']),
            'total_vehicles': len(detections['vehicle']),
            'total_personnel': len(detections['personnel']),
            'safety_issues': len(safety_issues),
            'result_image': result_path
        }
  
    # 模拟调用分析函数
    print ("模拟矿区无人机监控系统...")
    Result = analyze_mining_site_image ('example_mining_site.jpg', None)
    print (f"分析结果: 检测到 {result['total_equipment']} 台设备, {result['total_vehicles']} 辆车辆, "
          f"{result['total_personnel']} 名人员")
    print (f"发现 {result['safety_issues']} 处安全隐患")
    print (f"结果图像已保存到: {result['result_image']}")

12. 课程实验与报告指南 ​

为完成林云老师布置的无人机相关作业,以下是实验步骤和报告编写指南:

12.1 实验步骤详细规划 ​

步骤 1: 准备环境与数据集

bash
# 配置开发环境
# 创建虚拟环境
conda create -n visdrone python=3.8 -y
conda activate visdrone

# 安装依赖
pip install opencv-python matplotlib numpy pandas seaborn scikit-learn \
    Jupyter notebook tqdm tensorflow pillow imgaug shapely

# 下载 VisDrone 数据集
Mkdir -p ~/VisDrone_datasets
Cd ~/VisDrone_datasets

# 从官方网站手动下载并上传到服务器
# https://github.com/VisDrone/VisDrone-Dataset

# 解压数据集
tar -xf VisDrone 2019-DET-train.tar

步骤 2: 实验流程记录

  1. 应用本教程的可视化工具:

    bash
    # 克隆工具库
    git clone https://github.com/your_username/VisDrone-dataset-python-toolkit.git
    Cd VisDrone-dataset-python-toolkit
    
    # 运行可视化脚本
    python enhanced_visualize.py --data_root ~/VisDrone_datasets/VisDrone 2019-DET-train --max_vis_images 10
  2. 格式转换演示:

    bash
    # COCO 格式转换
    python visdrone 2 coco.py --data_dir ~/VisDrone_datasets/VisDrone 2019-DET-train --output_dir coco_output
    
    # YOLO 格式转换
    python visdrone 2 yolo.py --data_dir ~/VisDrone_datasets/VisDrone 2019-DET-train --output_dir yolo_output
  3. 数据集统计分析:

    bash
    # 运行 Jupyter Notebook 进行交互式分析
    Jupyter notebook visdrone_analysis. Ipynb
  4. 数据增强演示:

    bash
    # 运行数据增强脚本
    python data_augmentation.py --input_dir ~/VisDrone_datasets/VisDrone 2019-DET-train --output_dir augmented_data --augmentations 3

12.2 实验报告编写指南 ​

一、引言

  • 无人机图像识别技术的背景和重要性
  • VisDrone 数据集介绍和特点
  • 实验目标和内容概述

二、理论基础

  • 目标检测算法基础
  • 无人机视角下的目标检测挑战
  • VisDrone 数据集标注格式解析

三、实验环境与配置

  • 硬件环境
  • 软件环境(系统版本、Python 版本、相关库版本)
  • 数据集获取与预处理

四、数据可视化与分析

  • 可视化工具实现原理
  • 数据集样本展示
  • 标注信息统计与分析
    • 类别分布
    • 目标尺寸分布
    • 目标位置分布热力图

五、数据格式转换实现

  • COCO 格式转换原理与实现
  • YOLO 格式转换原理与实现
  • 转换结果验证

六、数据增强技术应用

  • 增强策略选择原理
  • Imgaug 库应用方法
  • 增强效果对比与展示

七、应用案例设计

  • 选择一个具体应用场景(安全监控/交通监控/农业监测等)
  • 应用流程设计
  • 关键代码实现与解析

八、实验结果与分析

  • 各工具的执行结果
  • 数据集特点总结
  • 应用案例效果评估

九、结论与展望

  • 实验总结
  • VisDrone 数据集的优缺点分析
  • 未来改进方向

十、参考文献

  • 格式规范的文献引用列表

12.3 录屏与演示建议 ​

按照林云老师的要求进行完整的实验过程录屏,建议按以下几个部分进行:

  1. 环境配置录屏

    • 操作系统环境展示
    • Python 环境配置
    • 依赖包安装
  2. 数据集预处理录屏

    • 数据集下载/解压过程
    • 数据集结构介绍
    • 示例图像与标注展示
  3. 工具使用录屏

    • 可视化工具运行过程
    • 格式转换工具运行过程
    • Jupyter Notebook 中的数据分析过程
  4. 应用场景演示录屏

    • 选择的应用案例执行过程
    • 结果展示与解释

在录屏过程中,建议:

  • 使用麦克风实时解说操作步骤和原理
  • 放慢关键步骤的操作速度,确保观众能够跟上
  • 对遇到的错误及解决方案进行详细说明
  • 在录屏结束时对整个实验过程进行总结

13. 总结与进一步学习资源 ​

13.1 教程总结 ​

本教程全面介绍了VisDrone数据集的处理与应用方法,包括:

  1. 数据集基础:详细解析了VisDrone目标检测数据集的结构、标注格式和类别定义。
  2. 可视化工具:提供了多种可视化方法,帮助直观理解数据集特点。
  3. 数据分析:通过统计分析揭示了数据集的类别分布、目标尺寸特征和密度特性。
  4. 格式转换:实现了从VisDrone原始格式到COCO、YOLO等主流格式的转换工具。
  5. 数据增强:提供了针对无人机视角特点的数据增强方法。
  6. 应用案例:展示了VisDrone数据集在智慧城市、安全监控和农业等领域的实际应用。

通过掌握这些工具和方法,你可以高效地利用VisDrone数据集来训练和优化目标检测模型,解决无人机视角下的各种实际问题。

13.2 进一步学习资源 ​

为了深入研究无人机视觉领域,以下是推荐的学习资源:

学术论文 ​

  1. Zhu, P., Wen, L., Du, D., Bian, X., Ling, H., et al. (2020). Vision Meets Drones: Past, Present and Future. arXiv preprint arXiv:2001.06303.

  2. Wen, L., Zhu, P., Du, D., Bian, X., et al. (2019). VisDrone-DET2019: The Vision Meets Drone Object Detection in Image Challenge Results. ICCV Workshop.

  3. Bozcan, I., & Kalkan, S. (2021). ADDR: Anomaly Detection in Drone Recordings Using Spatio-Temporal Autoencoders. IEEE Robotics and Automation Letters.

在线课程和教程 ​

  1. 无人机遥感与计算机视觉 - 清华大学/Stanford大学联合课程

  2. Coursera: Aerial Robotics - 宾夕法尼亚大学 这门课程涵盖了无人机的基本原理,包括感知系统。

  3. Udacity: Flying Car Nanodegree 包含无人机计算机视觉和感知系统的专业课程。

开源项目与代码库 ​

  1. MMDetection - 支持VisDrone数据集的目标检测工具箱 GitHub: open-mmlab/mmdetection

  2. DroneVision - 无人机视觉处理框架 GitHub: VisDrone (VisDrone)

  3. AerialDetection - 专注于航空图像目标检测的工具库 GitHub: dingjiansw101/AerialDetection

竞赛与挑战 ​

  1. VisDrone Challenge - 年度无人机视觉算法竞赛 网址:VisDrone Challenge

  2. NTIRE Workshop - 经常举办无人机图像处理相关竞赛 网址:NTIRE Workshop

  3. Kaggle - 定期举办无人机图像分析相关竞赛

数据集 ​

  1. UAVid - 无人机城市场景语义分割数据集 网址:UAVid

  2. UAVDT - 无人机视角下的目标检测与跟踪数据集 网址:UAVDT

  3. Aerial Image Dataset - 航空图像分析集合 网址:Aerial Image Dataset

13.3 实用工具推荐 ​

  1. LabelImg - 适用于目标检测标注的图形界面工具 GitHub: tzutalin/labelImg

  2. CVAT - 强大的计算机视觉标注工具,支持视频和图像 GitHub: opencv/cvat

  3. Roboflow - 提供数据集管理、增强和格式转换的在线平台 网址:Roboflow

  4. ImgAug - 本教程中使用的图像增强库 GitHub: aleju/imgaug

  5. Albumentations - 高性能的图像增强库 GitHub: albumentations-team/albumentations

13.4 致谢与贡献指南 ​

感谢所有为VisDrone数据集做出贡献的研究者,特别是来自天津大学、布里斯托大学等机构的团队。本教程旨在帮助更多研究者和开发者有效利用这一宝贵资源。

如果您想为本教程做出贡献,请通过以下方式参与:

  • 提交Bug报告或功能请求
  • 贡献新的数据处理或可视化代码
  • 分享您使用VisDrone数据集的实际应用案例
  • 改进文档或添加更多教程内容

13.5 许可证信息 ​

本教程中的代码采用MIT许可证发布,您可以自由使用、修改和分发这些代码,但需保留原始版权声明。请注意,VisDrone数据集本身有其特定的使用条款,使用前请查阅官方网站。

贡献者 ​

The avatar of contributor named as RainbowRain9 RainbowRain9
The avatar of contributor named as Cai Hongyu Cai Hongyu

文件历史 ​

用 ♥ 撰写