FiftyOne初体验(三)


前言

刚刚入门 YOLO 系列模型,尝试了 YOLOv5 和 YOLOv10,但在处理数据集时遇到了不少麻烦。为了更好地可视化和管理数据,我发现了 FiftyOne 这个工具。FiftyOne 是一个强大的数据探索和可视化平台,它能有效地帮助你处理目标检测任务中的数据。使用 FiftyOne,你可以轻松地可视化图像和标注框,迅速筛选出需要分析的数据子集,无论是按类别、标签还是注释等条件。这让数据集的管理和分析变得更加高效,极大地简化了我在 YOLO 模型训练过程中的工作。


一、FiftyOne 是什么?

FiftyOne 是一个开源的数据探索和可视化工具,专为机器学习和计算机视觉任务设计。它提供了强大的功能来帮助用户:

可视化和探索图像、视频及其标注数据。
以交互式方式查看和管理数据集。
快速筛选、搜索和分析数据子集,根据类别、标签、注释等条件进行过滤。
生成数据集的统计信息和报告,以帮助理解和优化模型性能。
FiftyOne 支持多种数据格式,包括 COCO、Pascal VOC 和自定义格式,旨在简化数据管理和提高工作效率

二、代码

1、2、3 ----->:FiftyOne初体验(一)

4、5、6 ----->:FiftyOne初体验(二)

7.使用txt文件筛选Fiftyone可视化图

通过上次代码中已经可以可视化出具有筛选预测框置信度的功能了,但是当我们将读取预测错误样本的时候,需要单独重新创建错误样本的数据集,这样显得很繁琐。
进一步修改代码,将使用预测错误的样本txt导入代码中进行筛选。

代码如下(示例):

import fiftyone as fo
import os
import xml.etree.ElementTree as ET
from PIL import Image
from pathlib import Path

def read_error_img_list(file_path):
    """
    读取 error_img_test 文件中的图像列表。
    """
    with open(file_path, 'r') as f:
        error_images = f.read().splitlines()
    # 提取文件名(不包括路径)以便于后续匹配
    error_images = [Path(img_path).stem for img_path in error_images]
    return error_images

def read_predictions_from_xml(xml_dir):
    """
    从 XML 文件中读取预测数据。
    """
    predictions = {}
    for xml_file in os.listdir(xml_dir):
        if xml_file.endswith('.xml'):
            tree = ET.parse(os.path.join(xml_dir, xml_file))
            root = tree.getroot()
            filename = root.find('filename').text
            detections = []
            for obj in root.findall('object'):
                name = obj.find('name').text
                bbox = obj.find('bndbox')
                xmin = float(bbox.find('xmin').text)
                ymin = float(bbox.find('ymin').text)
                xmax = float(bbox.find('xmax').text)
                ymax = float(bbox.find('ymax').text)
                confidence = float(obj.find('confidence').text)  # 假设XML文件中有这个字段
                detections.append({
                    'label': name,
                    'bbox': [xmin, ymin, xmax, ymax],
                    'confidence': confidence  # 添加confidence值
                })
            predictions[filename] = detections
    return predictions

def read_labels_from_xml(xml_dir):
    """
    从 XML 文件中读取标签数据。
    """
    labels = {}
    for xml_file in os.listdir(xml_dir):
        if xml_file.endswith('.xml'):
            tree = ET.parse(os.path.join(xml_dir, xml_file))
            root = tree.getroot()
            filename = root.find('filename').text
            labels[filename] = []
            for obj in root.findall('object'):
                name = obj.find('name').text
                bbox = obj.find('bndbox')
                xmin = float(bbox.find('xmin').text)
                ymin = float(bbox.find('ymin').text)
                xmax = float(bbox.find('xmax').text)
                ymax = float(bbox.find('ymax').text)
                labels[filename].append({
                    'label': name,
                    'bbox': [xmin, ymin, xmax, ymax]
                })
    return labels

def create_fiftyone_dataset(image_dir, predictions, labels, error_img_list):
    """
    创建 FiftyOne 数据集,筛选出需要的图像。
    """
    samples = []
    for filename, detection_list in predictions.items():
        # 检查文件名是否在 error_img_list 中
        if Path(filename).stem not in error_img_list:
            continue

        img_path = os.path.join(image_dir, filename)
        if not os.path.exists(img_path):
            continue

        img = Image.open(img_path)
        width, height = img.size
        detections = []
        for detection in detection_list:
            label = detection['label']
            bbox = detection['bbox']
            confidence = detection['confidence']  # 获取confidence值
            xmin, ymin, xmax, ymax = bbox
            detections.append(
                fo.Detection(
                    label=label,
                    bounding_box=[xmin / width, ymin / height, (xmax - xmin) / width, (ymax - ymin) / height],
                    confidence=confidence  # 添加confidence值
                )
            )
        sample = fo.Sample(filepath=img_path)
        sample.metadata = fo.ImageMetadata(width=width, height=height)
        sample["predictions"] = fo.Detections(detections=detections)
        samples.append(sample)
    
    dataset = fo.Dataset(name="thyroid_dataset_v3")
    dataset.add_samples(samples)
    return dataset

def add_label_data_to_dataset(dataset, labels):
    """
    将标签数据添加到 FiftyOne 数据集中。
    """
    for sample in dataset:
        filename = Path(sample.filepath).name
        if filename in labels:
            metadata = sample.metadata
            width = metadata.width
            height = metadata.height
            label_list = labels[filename]
            detections = []
            for label in label_list:
                label_name = label['label']
                bbox = label['bbox']
                xmin, ymin, xmax, ymax = bbox
                detections.append(
                    fo.Detection(
                        label=label_name,
                        bounding_box=[xmin / width, ymin / height, (xmax - xmin) / width, (ymax - ymin) / height]
                    )
                )
            sample["ground_truth"] = fo.Detections(detections=detections)  # 保持为 "ground_truth"
            sample.save()

# 文件和目录路径
error_img_test_path = '/home/jovyan/work/xxx/yolov5-5.0/runs/thyroid_error_train/thyroid_images/error_img_H00/漏检图像列表2.txt'
prediction_dir = '/home/jovyan/work/xxx/yolov5-5.0/runs/thyroid_error_train/thyroid_images/error_img_H00/3_error_pred_xml'
label_dir = '/home/jovyan/work/xxx/yolov5-5.0/runs/thyroid_error_train/thyroid_images/error_img_H00/xml'
image_dir = '/home/jovyan/work/xxx/yolov5-5.0/runs/thyroid_error_train/thyroid_images/error_img_H00/images'

# 读取 error_img_test 文件中的图像列表
error_img_list = read_error_img_list(error_img_test_path)

# 读取预测和标签数据
predictions = read_predictions_from_xml(prediction_dir)
labels = read_labels_from_xml(label_dir)

# 创建 FiftyOne 数据集,筛选出需要的图像
dataset = create_fiftyone_dataset(image_dir, predictions, labels, error_img_list)

# 添加标签数据到数据集中
add_label_data_to_dataset(dataset, labels)

# 启动 FiftyOne 可视化
session = fo.launch_app(dataset)
session.wait()  # 官网给的示例没有这一句,记得加上,不然程序不会等待,在网页中看不到我们要的效果

经过实际使用后还是存在不方便的情况,于是进一步修改代码在上述基础上添加tag标签用来区分漏检和误检(漏检、误检等代码后续在yolov5小工具分类中更新)

8.使用txt文件创建tag进行分类—>Fiftyone可视化图

import fiftyone as fo
import os
import xml.etree.ElementTree as ET
from PIL import Image
from pathlib import Path

def read_error_img_list(file_path):
    """
    读取 error_img_test 文件中的图像列表。
    """
    with open(file_path, 'r') as f:
        error_images = f.read().splitlines()
    # 提取文件名(不包括路径)以便于后续匹配
    error_images = [Path(img_path).stem for img_path in error_images]
    return error_images

def read_predictions_from_xml(xml_dir):
    """
    从 XML 文件中读取预测数据。
    """
    predictions = {}
    for xml_file in os.listdir(xml_dir):
        if xml_file.endswith('.xml'):
            tree = ET.parse(os.path.join(xml_dir, xml_file))
            root = tree.getroot()
            filename = root.find('filename').text
            detections = []
            for obj in root.findall('object'):
                name = obj.find('name').text
                bbox = obj.find('bndbox')
                xmin = float(bbox.find('xmin').text)
                ymin = float(bbox.find('ymin').text)
                xmax = float(bbox.find('xmax').text)
                ymax = float(bbox.find('ymax').text)
                confidence = float(obj.find('confidence').text)  # 假设XML文件中有这个字段
                detections.append({
                    'label': name,
                    'bbox': [xmin, ymin, xmax, ymax],
                    'confidence': confidence  # 添加confidence值
                })
            predictions[filename] = detections
    return predictions

def read_labels_from_xml(xml_dir):
    """
    从 XML 文件中读取标签数据。
    """
    labels = {}
    for xml_file in os.listdir(xml_dir):
        if xml_file.endswith('.xml'):
            tree = ET.parse(os.path.join(xml_dir, xml_file))
            root = tree.getroot()
            filename = root.find('filename').text
            labels[filename] = []
            for obj in root.findall('object'):
                name = obj.find('name').text
                bbox = obj.find('bndbox')
                xmin = float(bbox.find('xmin').text)
                ymin = float(bbox.find('ymin').text)
                xmax = float(bbox.find('xmax').text)
                ymax = float(bbox.find('ymax').text)
                labels[filename].append({
                    'label': name,
                    'bbox': [xmin, ymin, xmax, ymax]
                })
    return labels

def create_fiftyone_dataset(image_dir, predictions, labels, error_img_list, dataset_name, tag):
    """
    创建 FiftyOne 数据集,筛选出需要的图像,并添加标签。
    """
    samples = []
    for filename, detection_list in predictions.items():
        # 检查文件名是否在 error_img_list 中
        if Path(filename).stem not in error_img_list:
            continue

        img_path = os.path.join(image_dir, filename)
        if not os.path.exists(img_path):
            continue

        img = Image.open(img_path)
        width, height = img.size
        detections = []
        for detection in detection_list:
            label = detection['label']
            bbox = detection['bbox']
            confidence = detection['confidence']  # 获取confidence值
            xmin, ymin, xmax, ymax = bbox
            detections.append(
                fo.Detection(
                    label=label,
                    bounding_box=[xmin / width, ymin / height, (xmax - xmin) / width, (ymax - ymin) / height],
                    confidence=confidence  # 添加confidence值
                )
            )
        sample = fo.Sample(filepath=img_path)
        sample.metadata = fo.ImageMetadata(width=width, height=height)
        sample["predictions"] = fo.Detections(detections=detections)
        sample["tag"] = tag  # 添加标签用于区分数据集
        samples.append(sample)
    
    dataset = fo.Dataset(name=dataset_name)
    dataset.add_samples(samples)
    return dataset

def add_label_data_to_dataset(dataset, labels):
    """
    将标签数据添加到 FiftyOne 数据集中。
    """
    for sample in dataset:
        filename = Path(sample.filepath).name
        if filename in labels:
            metadata = sample.metadata
            width = metadata.width
            height = metadata.height
            label_list = labels[filename]
            detections = []
            for label in label_list:
                label_name = label['label']
                bbox = label['bbox']
                xmin, ymin, xmax, ymax = bbox
                detections.append(
                    fo.Detection(
                        label=label_name,
                        bounding_box=[xmin / width, ymin / height, (xmax - xmin) / width, (ymax - ymin) / height]
                    )
                )
            sample["ground_truth"] = fo.Detections(detections=detections)  # 保持为 "ground_truth"
            sample.save()

# 文件和目录路径
error_img_test_path1 = '/error_img_H00/误检图像列表2.txt'
error_img_test_path = '/error_img_H00/漏检图像列表2.txt'
prediction_dir = '/error_img_H00/3_error_pred_xml'
label_dir = '/error_img_H00/xml'
image_dir = '/error_img_H00/images'

# 读取误检和漏检图像列表
error_img_list_missed = read_error_img_list(error_img_test_path)
error_img_list_false_positive = read_error_img_list(error_img_test_path1)

# 读取预测和标签数据
predictions = read_predictions_from_xml(prediction_dir)
labels = read_labels_from_xml(label_dir)

# 创建 FiftyOne 数据集
missed_dataset = create_fiftyone_dataset(image_dir, predictions, labels, error_img_list_missed, "missed_dataset", "missed")
false_positive_dataset = create_fiftyone_dataset(image_dir, predictions, labels, error_img_list_false_positive, "false_positive_dataset", "false_positive")

# 添加标签数据到数据集中
add_label_data_to_dataset(missed_dataset, labels)
add_label_data_to_dataset(false_positive_dataset, labels)

# 合并数据集
combined_dataset = fo.Dataset(name="combined_dataset")
combined_dataset.add_samples(missed_dataset)
combined_dataset.add_samples(false_positive_dataset)

# 启动 FiftyOne 可视化
session = fo.launch_app(combined_dataset)
session.wait()  # 等待 FiftyOne 界面加载

还具有一定不足,比如在fiftyone页面中进行模糊筛选功能,目前只能筛选出单个图像的情况


总结

以上就是今天要讲的内容,本文进一步介绍了FiftyOne的使用,并给出筛选错误样本的格式。(后续会更新使用FiftyOne遇到的问题)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值