从零开始:Roboflow Supervision 计算机视觉工具库完全指南

从零开始:Roboflow Supervision 计算机视觉工具库完全指南

从零开始:Roboflow Supervision 计算机视觉工具库完全指南

Supervision 是由 Roboflow 团队开发的一个强大的 Python 计算机视觉工具库,旨在为开发者提供简单、高效、一站式的图像和视频处理解决方案。无论是进行目标检测、实例分割,还是视频分析、跟踪追踪,Supervision 都能提供直观易用的 API,让复杂的计算机视觉任务变得轻而易举。本文将带领读者从环境搭建开始,逐步深入了解 Supervision 的各项核心功能,通过丰富的实战案例帮助读者掌握这一工具库的使用技巧。

第一部分:环境搭建与安装

安装前的准备工作

在开始安装 Supervision 之前,我们需要确保开发环境中已经安装了 Python。Supervision 支持 Python 3.8 及以上版本,建议使用 Python 3.9 或更高版本以获得最佳兼容性。同时,为了充分利用 Supervision 的各项功能,我们还需要安装一些可选的依赖项。

首先,让我们检查当前 Python 的版本。在终端或命令提示符中执行以下命令:

python --version
# 或者
python3 --version

如果显示的版本号在 3.8.0 以上,那么就可以继续进行安装步骤了。建议同时安装 NumPy 和 OpenCV,因为这两个库是 Supervision 运行的基础依赖。

使用 pip 安装 Supervision

安装 Supervision 的最简单方式是使用 pip 包管理器。打开终端并执行以下命令:

pip install supervision

如果你想要安装包含所有可选依赖的完整版本,可以使用以下命令:

pip install "supervision[desktop]"

这个命令会安装 Supervision 及其所有依赖项,包括用于图像处理、桌面应用支持等方面的库。对于大多数用户来说,基础安装已经足够使用,但如果遇到某些功能无法使用的情况,可以尝试安装完整版本。

验证安装是否成功

安装完成后,我们可以通过一个简单的 Python 脚本来验证 Supervision 是否正确安装:

import supervision as sv

print("Supervision 版本:", sv.__version__)
print("安装成功!")

如果上述代码能够正常执行并打印出版本号,说明 Supervision 已经成功安装到你的 Python 环境中。

创建虚拟环境(推荐)

为了避免依赖冲突,建议在项目中使用虚拟环境。Python 3.3 及以上版本内置了 venv 模块,我们可以这样创建一个虚拟环境:

# 创建名为 cv_env 的虚拟环境
python -m venv cv_env

# 激活虚拟环境
# 在 Linux 或 macOS 上:
source cv_env/bin/activate
# 在 Windows 上:
cv_env\Scripts\activate

# 在虚拟环境中安装 Supervision
pip install supervision

创建独立的虚拟环境可以让我们在不同项目之间保持依赖的独立性,避免版本冲突问题。当项目完成后,可以通过简单的 deactivate 命令退出虚拟环境。

IDE 配置建议

对于 Python 开发,推荐使用 PyCharm 或 VS Code 作为集成开发环境。这两个 IDE 都提供了良好的 Python 支持,包括代码补全、调试功能以及终端集成。在配置 IDE 时,确保选择了正确的 Python 解释器(如果你使用了虚拟环境,应该选择虚拟环境中的 Python 解释器)。

配置好 IDE 后,创建一个新的 Python 文件,尝试导入 Supervision 库:

import supervision as sv
print("IDE 配置正确,可以开始使用了!")

如果看到成功打印的消息,说明 IDE 与 Supervision 的集成没有问题,可以开始愉快的开发之旅了。

第二部分:核心数据结构详解

Supervision 提供了一系列核心数据结构来统一处理不同计算机视觉任务中的数据格式。理解这些数据结构是掌握 Supervision 的基础。

Detections 类:目标检测结果的核心容器

Detections 是 Supervision 中最核心的数据结构之一,用于存储目标检测模型输出的结果。它封装了边界框、类别ID、类别名称、置信度分数等检测相关信息,并提供了丰富的操作方法。

创建 Detections 对象的基本方式如下:

import numpy as np
import supervision as sv

# 假设我们从某个检测模型获得了以下结果
# bounding_boxes: Nx4 的 numpy 数组,格式为 [x1, y1, x2, y2]
bounding_boxes = np.array([
    [100, 100, 200, 300],
    [250, 150, 400, 450],
    [50, 200, 150, 350]
])

# class_id: 每个检测框对应的类别ID
class_id = np.array([0, 1, 0])

# confidence: 每个检测框的置信度分数
confidence = np.array([0.95, 0.88, 0.72])

# 创建 Detections 对象
detections = sv.Detections(
    xyxy=bounding_boxes,
    class_id=class_id,
    confidence=confidence
)

print("检测数量:", len(detections))
print("边界框:", detections.xyxy)
print("类别ID:", detections.class_id)
print("置信度:", detections.confidence)

Detections 对象还支持通过参数添加类别名称,这在实际应用中非常有用:

# 定义类别名称映射
class_names = {0: "person", 1: "car", 2: "dog"}

# 为每个检测添加类别名称
detections.class_name = np.array([class_names[id] for id in detections.class_id])

print("类别名称:", detections.class_name)

Detections 对象的常用操作

Detections 类提供了丰富的方法来操作检测结果,让我们一一介绍这些实用功能。

筛选特定类别的检测结果:

# 筛选出所有 person 类别的检测结果
person_mask = detections.class_id == 0
person_detections = detections[person_mask]

print("Person 类别检测数:", len(person_detections))

根据置信度阈值筛选:

# 保留置信度大于 0.8 的检测结果
confidence_threshold = 0.8
high_conf_detections = detections[detections.confidence > confidence_threshold]

print("高置信度检测数:", len(high_conf_detections))

合并多个检测结果:

# 假设有两个 Detections 对象需要合并
detections1 = sv.Detections(
    xyxy=np.array([[100, 100, 200, 200]]),
    class_id=np.array([0]),
    confidence=np.array([0.9])
)

detections2 = sv.Detections(
    xyxy=np.array([[300, 300, 400, 400]]),
    class_id=np.array([1]),
    confidence=np.array([0.85])
)

# 使用 numpy 的 concatenate 或 Detections 的合并方法
combined = sv.Detections.merge([detections1, detections2])

print("合并后检测数:", len(combined))

计算检测结果的统计信息:

# 统计每个类别的检测数量
unique_classes, counts = np.unique(detections.class_id, return_counts=True)

for cls, count in zip(unique_classes, counts):
    class_name = class_names.get(cls, f"class_{cls}")
    print(f"{class_name}: {count} 个检测结果")

BoxAnnotator 和 LabelAnnotator:可视化利器

Supervision 提供了强大的标注工具,可以将检测结果直观地可视化到图像上。这些标注器支持高度自定义,包括颜色、字体、边框样式等。

基础图像标注:

import cv2

# 加载一张图像
image = cv2.imread("path/to/your/image.jpg")
# 如果没有测试图像,可以用以下方式创建一个空白图像
# image = np.zeros((640, 640, 3), dtype=np.uint8)

# 创建标注器
box_annotator = sv.BoxAnnotator()
label_annotator = sv.LabelAnnotator()

# 对图像进行标注
annotated_image = box_annotator.annotate(
    scene=image,
    detections=detections
)

# 添加标签标注(显示类别名称和置信度)
annotated_image = label_annotator.annotate(
    scene=annotated_image,
    detections=detections
)

# 保存或显示结果
cv2.imwrite("annotated_image.jpg", annotated_image)

自定义标注样式:

# 自定义标注颜色 - 为不同类别设置不同颜色
from supervision.draw.color import ColorPalette

# 使用预定义的颜色方案
color_palette = ColorPalette.from_hex(["#FF5733", "#33FF57", "#3357FF"])

# 自定义边框和文本样式
box_annotator = sv.BoxAnnotator(
    color=color_palette,
    thickness=2,
    text_opacity=0.8,
    text_scale=0.5
)

# 自定义标签显示格式
label_annotator = sv.LabelAnnotator(
    color=color_palette,
    text_opacity=0.9,
    text_scale=0.6,
    text_padding=4
)

# 创建自定义标签列表
labels = [
    f"{detections.class_name[i]}: {detections.confidence[i]:.2f}"
    for i in range(len(detections))
]

annotated_image = box_annotator.annotate(
    scene=image,
    detections=detections
)

annotated_image = label_annotator.annotate(
    scene=annotated_image,
    detections=detections,
    labels=labels
)

ByteTracker:视频目标跟踪

对于视频处理任务,Supervision 提供了强大的目标跟踪功能。ByteTracker 是一个高效的多目标跟踪器,能够在视频序列中保持对每个目标的持续追踪。

视频跟踪基本流程:

# 创建跟踪器实例
tracker = sv.ByteTrack()

# 假设我们处理视频的每一帧
# frame 是当前帧的图像
# detections 是当前帧的检测结果

# 更新跟踪器
tracked_detections = tracker.update_with_detections(detections)

# tracked_detections 包含额外的跟踪信息
print("跟踪ID:", tracked_detections.tracker_id)
print("边界框:", tracked_detections.xyxy)
print("类别ID:", tracked_detections.class_id)

完整视频处理示例:

# 定义视频处理函数
def process_video(
    source_video_path: str,
    target_video_path: str,
    model: object,
    tracker: object
):
    # 创建视频信息提取器
    video_info = sv.VideoInfo.from_video_path(source_video_path)

    # 创建视频读取器
    with sv.VideoSink(target_video_path, video_info) as sink:
        # 创建帧读取迭代器
        with sv.get_video_frames_generator(source_video_path) as frames:
            # 创建标注器
            box_annotator = sv.BoxAnnotator()
            label_annotator = sv.LabelAnnotator()

            # 逐帧处理
            for frame in frames:
                # 使用模型进行检测
                results = model(frame)

                # 转换为 Supervision 格式
                detections = sv.Detections.from_yolov8(results)

                # 更新跟踪器
                tracked_detections = tracker.update_with_detections(detections)

                # 创建标签(包含跟踪ID)
                labels = [
                    f"#{tracker_id} {class_name} {confidence:.2f}"
                    for tracker_id, class_name, confidence
                    in zip(
                        tracked_detections.tracker_id,
                        tracked_detections.class_name,
                        tracked_detections.confidence
                    )
                ]

                # 标注图像
                annotated_frame = box_annotator.annotate(
                    scene=frame,
                    detections=tracked_detections
                )
                annotated_frame = label_annotator.annotate(
                    scene=annotated_frame,
                    detections=tracked_detections,
                    labels=labels
                )

                # 写入输出视频
                sink.write_frame(annotated_frame)

视频信息与帧处理工具

Supervision 提供了便捷的视频处理工具,简化了视频 I/O 操作。

获取视频信息:

# 从视频文件获取信息
video_info = sv.VideoInfo.from_video_path("path/to/video.mp4")

print("视频宽度:", video_info.width)
print("视频高度:", video_info.height)
print("帧率:", video_info.fps)
print("总帧数:", video_info.total_frames)
print("视频时长(秒):", video_info.total_frames / video_info.fps)

逐帧读取视频:

# 方法一:使用 VideoSink 和 VideoInfo
video_info = sv.VideoInfo.from_video_path("input.mp4")

with sv.VideoSink("output.mp4", video_info) as sink:
    with sv.get_video_frames_generator("input.mp4") as frames:
        for frame in frames:
            # 在这里处理每一帧
            processed_frame = frame  # 替换为你的处理逻辑
            sink.write_frame(processed_frame)

# 方法二:使用 cv2.VideoCapture
import cv2

cap = cv2.VideoCapture("input.mp4")

while True:
    ret, frame = cap.read()
    if not ret:
        break

    # 处理帧
    # ...

cap.release()

从摄像头读取:

# 读取摄像头画面
with sv.VideoSink("webcam_output.mp4", sv.VideoInfo(width=640, height=480, fps=30)) as sink:
    with sv.get_video_frames_generator(0) as frames:
        for frame in frames:
            # 处理摄像头帧
            processed = frame

            # 写入视频
            sink.write_frame(processed)

            # 按 'q' 键退出
            if cv2.waitKey(1) & 0xFF == ord('q'):
                break

第三部分:实战教程 – 从模型到应用

教程一:YOLOv8 目标检测项目

让我们从头开始构建一个完整的 YOLOv8 目标检测项目。YOLOv8 是 Ultralytics 公司开发的最新一代 YOLO 模型,与 Supervision 配合使用非常便捷。

项目准备与依赖安装:

# 首先安装必要的依赖
# pip install ultralytics supervision opencv-python

import cv2
import numpy as np
from ultralytics import YOLO
import supervision as sv

加载模型并进行检测:

# 加载预训练的 YOLOv8 模型
# 这里使用 YOLOv8n(nano 版本,速度最快)
model = YOLO("yolov8n.pt")

# 读取图像
image = cv2.imread("path/to/image.jpg")

# 进行目标检测
results = model(image)[0]

# 将 YOLOv8 结果转换为 Supervision 格式
detections = sv.Detections.from_yolov8(results)

print("检测到的目标数量:", len(detections))
print("检测到的类别:", np.unique(detections.class_id))

完整的目标检测可视化流程:

def detect_and_visualize(image_path: str, output_path: str):
    """
    完整的目标检测和可视化流程

    参数:
        image_path: 输入图像路径
        output_path: 输出图像路径
    """
    # 加载图像
    image = cv2.imread(image_path)

    # 加载 YOLOv8 模型
    model = YOLO("yolov8n.pt")

    # 执行检测
    results = model(image)[0]

    # 转换为 Supervision Detections 格式
    detections = sv.Detections.from_yolov8(results)

    # 创建标注器 - 使用边界框
    box_annotator = sv.BoxAnnotator(
        thickness=2,
        text_thickness=2,
        text_scale=1.0
    )

    # 创建标签列表
    # 从 YOLOv8 结果中获取类别名称
    class_names = results.names

    labels = [
        f"{class_names[class_id]} {confidence:.2f}"
        for class_id, confidence
        in zip(detections.class_id, detections.confidence)
    ]

    # 进行标注
    annotated_image = box_annotator.annotate(
        scene=image.copy(),
        detections=detections,
        labels=labels
    )

    # 保存结果
    cv2.imwrite(output_path, annotated_image)

    print(f"处理完成!共检测到 {len(detections)} 个目标")

    return detections

批量处理目录中的图像:

import os
from pathlib import Path

def batch_detect(
    input_dir: str,
    output_dir: str,
    model_name: str = "yolov8n.pt"
):
    """
    批量处理目录中的所有图像

    参数:
        input_dir: 输入图像目录
        output_dir: 输出图像目录
        model_name: 模型名称或路径
    """
    # 创建输出目录
    os.makedirs(output_dir, exist_ok=True)

    # 加载模型
    model = YOLO(model_name)

    # 获取所有图像文件
    supported_formats = [".jpg", ".jpeg", ".png", ".bmp", ".webp"]
    image_files = [
        f for f in Path(input_dir).iterdir()
        if f.suffix.lower() in supported_formats
    ]

    # 创建标注器
    box_annotator = sv.BoxAnnotator(thickness=2)

    # 统计变量
    total_detections = 0

    # 逐个处理图像
    for image_path in image_files:
        print(f"处理: {image_path.name}")

        # 读取图像
        image = cv2.imread(str(image_path))

        # 执行检测
        results = model(image)[0]
        detections = sv.Detections.from_yolov8(results)

        # 标注
        annotated_image = box_annotator.annotate(
            scene=image.copy(),
            detections=detections
        )

        # 保存结果
        output_path = os.path.join(output_dir, image_path.name)
        cv2.imwrite(output_path, annotated_image)

        total_detections += len(detections)

    print(f"批量处理完成!共处理 {len(image_files)} 张图像,检测到 {total_detections} 个目标")

    return total_detections

# 使用示例
# batch_detect("input_images/", "output_images/")

教程二:实时视频目标检测与跟踪

这个教程将展示如何构建一个完整的实时目标检测与跟踪系统,能够处理视频文件或摄像头输入。

完整的视频分析系统:

import cv2
from ultralytics import YOLO
import supervision as sv
from typing import List, Optional

class VideoAnalyzer:
    """
    视频分析器:支持目标检测、跟踪和统计
    """

    def __init__(
        self,
        model_path: str = "yolov8n.pt",
        confidence_threshold: float = 0.3,
        iou_threshold: float = 0.5
    ):
        """
        初始化视频分析器

        参数:
            model_path: YOLO 模型路径
            confidence_threshold: 置信度阈值
            iou_threshold: IOU 阈值用于 NMS
        """
        # 加载模型
        self.model = YOLO(model_path)

        # 设置阈值
        self.confidence_threshold = confidence_threshold
        self.iou_threshold = iou_threshold

        # 创建跟踪器
        self.tracker = sv.ByteTrack()

        # 创建标注器
        self.box_annotator = sv.BoxAnnotator(
            thickness=2,
            text_thickness=1,
            text_scale=0.5
        )

        # 统计信息
        self.stats = {
            "frame_count": 0,
            "total_detections": 0,
            "class_counts": {},
            "tracked_objects": set()
        }

    def process_frame(self, frame: np.ndarray) -> tuple:
        """
        处理单帧图像

        参数:
            frame: 输入帧 (BGR 格式)

        返回:
            annotated_frame: 标注后的帧
            detections: 检测结果
        """
        # 运行检测
        results = self.model(
            frame,
            conf=self.confidence_threshold,
            iou=self.iou_threshold,
            verbose=False
        )[0]

        # 转换为 Supervision 格式
        detections = sv.Detections.from_yolov8(results)

        # 更新跟踪器
        tracked_detections = self.tracker.update_with_detections(detections)

        # 更新统计信息
        self._update_stats(tracked_detections)

        # 创建标注
        annotated_frame = self._annotate_frame(
            frame.copy(),
            tracked_detections
        )

        return annotated_frame, tracked_detections

    def _update_stats(self, detections: sv.Detections):
        """更新统计信息"""
        self.stats["frame_count"] += 1
        self.stats["total_detections"] += len(detections)

        # 统计每个类别的数量
        for class_id in detections.class_id:
            class_name = str(class_id)
            self.stats["class_counts"][class_name] = \
                self.stats["class_counts"].get(class_name, 0) + 1

        # 记录跟踪的物体ID
        if hasattr(detections, "tracker_id"):
            for tracker_id in detections.tracker_id:
                self.stats["tracked_objects"].add(tracker_id)

    def _annotate_frame(
        self,
        frame: np.ndarray,
        detections: sv.Detections
    ) -> np.ndarray:
        """为帧添加标注"""
        # 获取类别名称映射
        class_names = self.model.model.names

        # 创建标签
        labels = []
        if len(detections) > 0:
            for class_id, confidence, tracker_id in zip(
                detections.class_id,
                detections.confidence,
                detections.tracker_id
            ):
                class_name = class_names[int(class_id)]
                label = f"#{int(tracker_id)} {class_name} {confidence:.2f}"
                labels.append(label)

        # 标注
        annotated = self.box_annotator.annotate(
            scene=frame,
            detections=detections,
            labels=labels if labels else None
        )

        return annotated

    def process_video(
        self,
        source_path: str,
        output_path: Optional[str] = None,
        show_preview: bool = True
    ):
        """
        处理视频文件

        参数:
            source_path: 输入视频路径
            output_path: 输出视频路径(可选)
            show_preview: 是否显示预览窗口
        """
        # 获取视频信息
        video_info = sv.VideoInfo.from_video_path(source_path)

        # 创建视频写入器(如果指定了输出路径)
        sink = None
        if output_path:
            sink = sv.VideoSink(output_path, video_info)

        # 创建帧生成器
        frames_generator = sv.get_video_frames_generator(source_path)

        # 用于 FPS 计算
        fps_tracker = sv.FPS()

        try:
            for frame in frames_generator:
                # 处理帧
                annotated_frame, detections = self.process_frame(frame)

                # 显示预览
                if show_preview:
                    # 计算并显示 FPS
                    fps_tracker.tick()
                    fps = fps_tracker.fps()

                    # 在图像上显示 FPS
                    cv2.putText(
                        annotated_frame,
                        f"FPS: {fps:.1f}",
                        (10, 30),
                        cv2.FONT_HERSHEY_SIMPLEX,
                        1,
                        (0, 255, 0),
                        2
                    )

                    # 显示帧数和检测数
                    info_text = f"Frame: {self.stats['frame_count']} | Objects: {len(detections)}"
                    cv2.putText(
                        annotated_frame,
                        info_text,
                        (10, 70),
                        cv2.FONT_HERSHEY_SIMPLEX,
                        0.8,
                        (0, 255, 0),
                        2
                    )

                    cv2.imshow("Video Analysis", annotated_frame)

                    # 按 'q' 退出
                    if cv2.waitKey(1) & 0xFF == ord('q'):
                        break

                # 写入输出视频
                if sink:
                    sink.write_frame(annotated_frame)

        finally:
            # 清理资源
            if sink:
                sink.release()
            if show_preview:
                cv2.destroyAllWindows()

        # 打印统计信息
        self._print_stats()

    def _print_stats(self):
        """打印统计信息"""
        print("\n" + "=" * 50)
        print("视频分析统计")
        print("=" * 50)
        print(f"总帧数: {self.stats['frame_count']}")
        print(f"总检测数: {self.stats['total_detections']}")
        print(f"独立跟踪物体数: {len(self.stats['tracked_objects'])}")
        print("\n各类别检测统计:")
        for class_id, count in self.stats['class_counts'].items():
            print(f"  类别 {class_id}: {count} 次")
        print("=" * 50)

# 使用示例
# analyzer = VideoAnalyzer(model_path="yolov8n.pt")
# analyzer.process_video("input.mp4", "output.mp4", show_preview=True)

实时摄像头目标检测:

def real_time_detection(
    model_path: str = "yolov8n.pt",
    camera_id: int = 0,
    confidence: float = 0.5
):
    """
    实时摄像头目标检测

    参数:
        model_path: 模型路径
        camera_id: 摄像头ID
        confidence: 置信度阈值
    """
    # 加载模型
    model = YOLO(model_path)

    # 创建标注器
    box_annotator = sv.BoxAnnotator(thickness=2)

    # 打开摄像头
    cap = cv2.VideoCapture(camera_id)

    # 设置摄像头分辨率
    cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
    cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)

    # FPS 计算器
    fps = sv.FPS()

    print("按 'q' 键退出...")

    try:
        while True:
            # 读取帧
            ret, frame = cap.read()

            if not ret:
                print("无法读取摄像头画面")
                break

            # 开始 FPS 计时
            fps.tick()

            # 进行检测
            results = model(frame, conf=confidence, verbose=False)[0]
            detections = sv.Detections.from_yolov8(results)

            # 标注
            annotated_frame = box_annotator.annotate(
                scene=frame,
                detections=detections
            )

            # 显示 FPS
            cv2.putText(
                annotated_frame,
                f"FPS: {fps.fps():.1f}",
                (10, 30),
                cv2.FONT_HERSHEY_SIMPLEX,
                1,
                (0, 255, 0),
                2
            )

            # 显示检测数量
            cv2.putText(
                annotated_frame,
                f"Objects: {len(detections)}",
                (10, 70),
                cv2.FONT_HERSHEY_SIMPLEX,
                1,
                (0, 255, 0),
                2
            )

            # 显示画面
            cv2.imshow("Real-time Detection", annotated_frame)

            # 按 'q' 退出
            if cv2.waitKey(1) & 0xFF == ord('q'):
                break

    finally:
        # 清理资源
        cap.release()
        cv2.destroyAllWindows()
        print(f"平均 FPS: {fps.fps():.2f}")

# 使用示例
# real_time_detection(model_path="yolov8n.pt")

教程三:自定义目标检测与过滤

在实际应用中,我们经常需要对检测结果进行过滤和处理。Supervision 提供了灵活的方式来筛选和处理检测结果。

按类别过滤检测结果:

def filter_by_class(
    detections: sv.Detections,
    target_classes: List[int]
) -> sv.Detections:
    """
    按类别过滤检测结果

    参数:
        detections: 原始检测结果
        target_classes: 目标类别ID列表

    返回:
        过滤后的检测结果
    """
    # 创建类别掩码
    mask = np.isin(detections.class_id, target_classes)

    # 返回过滤后的结果
    return detections[mask]

# 使用示例
# 只保留 person (class_id=0) 和 car (class_id=2) 的检测
filtered = filter_by_class(detections, [0, 2])

按区域过滤检测结果:

def filter_by_region(
    detections: sv.Detections,
    region: tuple
) -> sv.Detections:
    """
    只保留在指定区域内的检测结果

    参数:
        detections: 原始检测结果
        region: 区域坐标 (x1, y1, x2, y2)

    返回:
        过滤后的检测结果
    """
    x1, y1, x2, y2 = region

    # 获取所有检测框的中心点
    centers = detections.get_anchor_point(anchor=sv.Position.CENTER)

    # 创建区域掩码
    mask = (
        (centers[:, 0] >= x1) &
        (centers[:, 0] <= x2) &
        (centers[:, 1] >= y1) &
        (centers[:, 1] <= y2)
    )

    return detections[mask]

# 使用示例
# 只保留图像左侧区域 (0, 0, 320, 480) 内的检测
left_region = filter_by_region(detections, (0, 0, 320, 480))

按检测框大小过滤:

def filter_by_size(
    detections: sv.Detections,
    min_area: float = 1000,
    max_area: float = float('inf')
) -> sv.Detections:
    """
    按检测框面积过滤

    参数:
        detections: 原始检测结果
        min_area: 最小面积阈值
        max_area: 最大面积阈值

    返回:
        过滤后的检测结果
    """
    # 计算每个检测框的面积
    xyxy = detections.xyxy
    areas = (xyxy[:, 2] - xyxy[:, 0]) * (xyxy[:, 3] - xyxy[:, 1])

    # 创建面积掩码
    mask = (areas >= min_area) & (areas <= max_area)

    return detections[mask]

# 使用示例
# 过滤掉太小的检测框(面积小于 2000 像素)
size_filtered = filter_by_size(detections, min_area=2000)

计算检测框之间的重叠(IOU):

def calculate_iou(box1: np.ndarray, box2: np.ndarray) -> float:
    """
    计算两个边界框之间的 IOU

    参数:
        box1: 第一个边界框 [x1, y1, x2, y2]
        box2: 第二个边界框 [x1, y1, x2, y2]

    返回:
        IOU 值
    """
    # 计算交集区域
    x1 = max(box1[0], box2[0])
    y1 = max(box1[1], box2[1])
    x2 = min(box1[2], box2[2])
    y2 = min(box1[3], box2[3])

    # 计算交集面积
    intersection = max(0, x2 - x1) * max(0, y2 - y1)

    # 计算各自的面积
    area1 = (box1[2] - box1[0]) * (box1[3] - box1[1])
    area2 = (box2[2] - box2[0]) * (box2[3] - box2[1])

    # 计算并集面积
    union = area1 + area2 - intersection

    # 返回 IOU
    return intersection / union if union > 0 else 0

# 使用示例
box_a = np.array([100, 100, 200, 200])
box_b = np.array([150, 150, 250, 250])
iou = calculate_iou(box_a, box_b)
print(f"IOU: {iou:.4f}")

非极大值抑制(NMS)实现:

def non_maximum_suppression(
    detections: sv.Detections,
    iou_threshold: float = 0.5
) -> sv.Detections:
    """
    应用非极大值抑制来去除重叠的检测框

    参数:
        detections: 检测结果
        iou_threshold: IOU 阈值,超过该值的框会被抑制

    返回:
        去重后的检测结果
    """
    if len(detections) == 0:
        return detections

    # 获取边界框和置信度
    boxes = detections.xyxy
    scores = detections.confidence

    # 按置信度排序
    indices = np.argsort(scores)[::-1]

    keep_indices = []

    while len(indices) > 0:
        # 保留置信度最高的框
        current_idx = indices[0]
        keep_indices.append(current_idx)

        if len(indices) == 1:
            break

        # 计算与最高置信度框的 IOU
        current_box = boxes[current_idx]
        remaining_boxes = boxes[indices[1:]]

        ious = np.array([
            calculate_iou(current_box, box)
            for box in remaining_boxes
        ])

        # 保留 IOU 小于阈值的框
        indices = indices[1:][ious < iou_threshold]

    return detections[np.array(keep_indices)]

# 使用示例
# 去除重叠的检测框
nms_detections = non_maximum_suppression(detections, iou_threshold=0.5)

教程四:图像分割与掩码可视化

Supervision 对图像分割任务提供了良好的支持,包括语义分割和实例分割。

SAM 分割模型集成:

“`python

安装 SAM 依赖

pip install segment Anything supervision

from segment_anything import sam_model_registry, SamPredictor
import supervision as sv
import cv2

def sam_segmentation(
image_path: str,
sam_checkpoint: str,
model_type: str = “vit_b”
):
“””
使用 SAM 模型进行分割

参数:
    image_path: 输入图像路径
    sam_checkpoint: SAM 模型检查点路径
    model_type: SAM 模型类型 (vit_b, vit_l, vit_h)
"""
# 加载图像
image = cv2.imread(image_path)
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

# 加载 SAM 模型
sam = sam_model_registry[model_type](checkpoint=sam_checkpoint)
predictor = SamPredictor(sam)

# 设置图像
predictor.set_image(image_rgb)

# 假设我们有一些检测框作为提示
# 这里使用预定义的检测框
boxes = np.array([
    [100, 100, 300, 400],
    [400, 200, 600, 500]
])

# 存储分割结果
all_masks = []

for box in boxes:
    # 使用边界框提示进行分割
    mask, score, _ = predictor.predict(
        box=box,
        multimask_output=True
    )

    # 选择最佳掩码
    best_mask = mask[np.argmax(score)]
    all_masks.append(best_mask)

# 合并所有掩码
combined_mask = np.zeros

如果内容对您有帮助,欢迎打赏

您的支持是我继续创作的动力

前往打赏页面

评论区

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注