使用Segment Anything(SAM)模型进行自动标注

该文详细介绍了如何下载和使用两个GitHub项目,通过PyTorch模型提取图像嵌入,将模型转换为ONNX格式,然后进行图像对象标注。过程中涉及SAM模型、COCO和VOC格式的标注文件转换。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.下载项目

项目1:https://github.com/zhouayi/SAM-Tool

项目2:https://github.com/facebookresearch/segment-anything

git clone https://github.com/zhouayi/SAM-Tool.git

git clone https://github.com/facebookresearch/segment-anything.git
cd segment-anything
pip install -e .

下载SAM模型:https://dl.fbaipublicfiles.com/segment_anything/sam_vit_h_4b8939.pth

2. 处理数据

把数据放置在<dataset_path>/images/*这样的路径中,并创建空文件夹<dataset_path>/embeddings

3. 提取图片的embedding文件和生成onnx模型

将项目1中的helpers文件夹复制到项目2的主目录下

  • 运行extract_embeddings.py文件来提取图片的embedding
# cd到项目2的主目录下
python helpers\extract_embeddings.py --checkpoint-path sam_vit_h_4b8939.pth --dataset-folder <dataset_path> --device cpu
  • checkpoint-path:上面下载好的SAM模型路径
  • dataset-folder:数据路径
  • device:默认cuda,没有GPUcpu也行的,就是速度挺慢的

运行完毕后,<dataset_path>/embeddings下会生成相应的npy文件

  • 运行generate_onnx.pypth文件转换为onnx模型文件
# cd到项目2的主目录下
python helpers\generate_onnx.py --checkpoint-path sam_vit_h_4b8939.pth --onnx-model-path ./sam_onnx.onnx --orig-im-size 1080 1920
  • checkpoint-path:同样的SAM模型路径
  • onnx-model-path:得到的onnx模型保存路径
  • orig-im-size:数据中图片的尺寸大小(height, width)

注意:提供给的代码转换得到的onnx模型并不支持动态输入大小,所以如果你的数据集中图片尺寸不一,那么可选方案是以不同的orig-im-size参数导出不同的onnx模型供后续使用

这里可能会报错ValueError: Unsupported ONNX opset version: 15,原因是因为pytorch或者onnx版本太低了,解决方法是安装更高版本的pytorch和onnx,可供参考,我的环境是
onnx == 1.13.1
onnxruntime == 1.14.1
torch == 1.13.1
torchaudio== 0.13.1
torchvision==0.14.1
我同样也尝试了另一种方法:将opset-version数值设低一点,但是会报其他的错误

4. 开始标注

  • 将生成的sam_onnx.onnx模型复制到项目1的主目录下,运行segment_anything_annotator.py进行标注
# cd到项目1的主目录下
python segment_anything_annotator.py --onnx-model-path sam_onnx.onnx --dataset-path <dataset_path> --categories cat,dog
  • onnx-model-path:导出的onnx模型路径
  • dataset-path:数据路径
  • categories:数据集的类别(每个类别以,分割,不要有空格)

在对象位置处点击鼠标左键为增加掩码,点击右键为去掉该位置掩码。

其他使用快捷键有:

Esc:退出appa:前一张图片d:下一张图片
k:调低透明度l:调高透明度n:添加对象
r:重置Ctrl+s:保存Ctrl+z:撤销上一个对象

在这里插入图片描述

最后生成的标注文件为coco格式,保存在<dataset_path>/annotations.json

5. 查看全部标注结果

运行cocoviewer.py查看全部的标注结果

# cd到项目1的主目录下
python cocoviewer.py -i <dataset_path> -a <dataset_path>\annotations.json

在这里插入图片描述

6.将保存的json格式转换为voc格式

该工具保存的标注文件格式为COCO标准格式,如有需求可自行编写标注文件格式转换脚本,下面提供转换为VOC格式的脚本例子

import json
import xml.etree.ElementTree as ET
import os

jsonPath = "F:/vsCode/segment-anything/CatDog/annotations.json"
vocPath = "F:/vsCode/segment-anything/CatDog/VOC"
with open(jsonPath, 'r') as f:
    data = json.load(f)

info = data["info"]
images = data["images"]
annotations = data["annotations"]
categories = data["categories"]

# 对每个图像处理
for img_data in images:
    # 创建 VOC XML 文件
    xml_file = ET.Element('annotation')
    ET.SubElement(xml_file, 'folder').text = 'VOC'
    ET.SubElement(xml_file, 'filename').text = os.path.basename(img_data["file_name"])
    source = ET.SubElement(xml_file, 'source')
    ET.SubElement(source, 'database').text = 'My Database'
    ET.SubElement(source, 'annotation').text = 'COCO'
    ET.SubElement(source, 'image').text = 'flickr'
    size = ET.SubElement(xml_file, 'size')
    ET.SubElement(size, 'width').text = str(img_data['width'])
    ET.SubElement(size, 'height').text = str(img_data['height'])
    ET.SubElement(size, 'depth').text = '3'
    ET.SubElement(xml_file, 'segmented').text = '0'

    # 查找该图像的所有标注框
    bbox_list = []
    category_ids = []
    for ann_data in annotations:
        if ann_data['image_id'] == img_data['id']:
            bbox = ann_data['bbox']
            bbox_list.append(bbox)
            category_ids.append(ann_data['category_id'])

    # 对每个标注框处理
    for i in range(len(bbox_list)):
        bbox = bbox_list[i]
        category_id = category_ids[i]
        # 转换 COCO 格式到 VOC 格式
        x_min = bbox[0]
        y_min = bbox[1]
        x_max = bbox[0] + bbox[2]
        y_max = bbox[1] + bbox[3]
        class_name = categories[category_id]['name']
        # 创建 VOC XML 标注
        obj = ET.SubElement(xml_file, 'object')
        ET.SubElement(obj, 'name').text = class_name
        ET.SubElement(obj, 'pose').text = 'Unspecified'
        ET.SubElement(obj, 'truncated').text = '0'
        ET.SubElement(obj, 'difficult').text = '0'
        bndbox = ET.SubElement(obj, 'bndbox')
        ET.SubElement(bndbox, 'xmin').text = str(int(x_min))
        ET.SubElement(bndbox, 'ymin').text = str(int(y_min))
        ET.SubElement(bndbox, 'xmax').text = str(int(x_max))
        ET.SubElement(bndbox, 'ymax').text = str(int(y_max))

    # 将 XML 文件保存到 VOC 目标文件夹中
    xml_str = ET.tostring(xml_file)
    with open(os.path.join(vocPath, os.path.basename(img_data["file_name"]).replace('.jpg', '.xml')), 'wb') as f:
        f.write(xml_str)

VOC格式:
在这里插入图片描述

7.其他

  • bug:TypeError: Argument 'bb' has incorrect type (expected numpy.ndarray, got list)已修正,可以重新拉取项目一,或者直接在自己的项目一代码修改下面一处即可,由于要构成一个polygon,所以segmentation中的轮廓点列表的长度必须是偶数且大于4。

    # salt/dataset_explorer.py
    def parse_mask_to_coco(image_id, anno_id, image_mask, category_id, poly=False):
    	# ...
    	if poly == True:
            for contour in contours:
                contour = np.flip(contour, axis=1)
                segmentation = contour.ravel().tolist()
                sc = bunch_coords(segmentation)
                sc = simplify_coords_vwp(sc, 2)
                sc = unbunch_coords(sc)
                # 增加判断条件
                if len(sc) > 4:
                    annotation["segmentation"].append(sc)
    
  • 修改标注框线条的宽度的代码位置

    # salt/displat_utils.py
    class DisplayUtils:
        def __init__(self):
            self.transparency = 0.65 # 默认的掩码透明度
            self.box_width = 2 # 默认的边界框线条宽度
    
  • 修改标注文本的格式的代码位置

    # salt/displat_utils.py
    def draw_box_on_image(self, image, categories, ann, color):
        x, y, w, h = ann["bbox"]
        x, y, w, h = int(x), int(y), int(w), int(h)
        image = cv2.rectangle(image, (x, y), (x + w, y + h), color, self.box_width)
    
        text = '{} {}'.format(ann["id"],categories[ann["category_id"]])
        txt_color = (0, 0, 0) if np.mean(color) > 127 else (255, 255, 255)
        font = cv2.FONT_HERSHEY_SIMPLEX
        txt_size = cv2.getTextSize(text, font, 1.5, 1)[0]
        cv2.rectangle(image, (x, y + 1), (x + txt_size[0] + 1, y + int(1.5*txt_size[1])), color, -1)
        cv2.putText(image, text, (x, y + txt_size[1]), font, 1.5, txt_color, thickness=5)
        return image
    
使用SAM自动标注工具制作数据集是一个高效的选择,特别是当你准备使用YOLO算法进行目标检测的训练时。SAM标注工具可以辅助你快速创建标注信息,而YOLO算法需要的边界框和类别信息,正好可以通过半自动标注来加速和优化。以下是结合YOLO算法特点的标注过程说明: 参考资源链接:[SAM自动标注工具:目标检测与语义分割数据集制作](https://wenku.csdn.net/doc/3k13g39dur?spm=1055.2569.3001.10343) 首先,你需要安装并配置SAM标注工具,确保所有必要的依赖项都已安装,并且环境变量正确设置。在准备开始标注之前,你可能需要根据YOLO格式要求调整标注模板,YOLO通常要求使用txt文件存储标注信息,每个图像对应一个txt文件,文件内包含所有该图像中目标的类别和位置信息。 其次,打开SAM标注工具,导入你的图像数据集。SAM工具通常会提供基本的图像浏览功能,并允许你设置不同的标注类别和颜色。开始标注前,你可以预设一些标注快捷键,以便于快速切换类别和执行标注标注时,SAM工具可能会提供自动跟踪目标的功能,这样你可以更快速地绘制边界框。尽管如此,你仍然需要仔细检查并调整标注框的位置,确保它们准确地框住目标对象。对于YOLO算法,标注精度至关重要,因为算法性能直接受到标注质量的影响。 标注完成后,你需要将标注信息导出为YOLO算法所要求的格式。在SAM工具中,你可以选择导出为文本文件,每个图像对应一个文本文件,文件中列出所有标注目标的类别和位置坐标。位置坐标应该是相对于图像宽度和高度的比例值,而不是像素值。 最后,将导出的标注文件和对应的图像文件组织成YOLO算法要求的目录结构,并进行数据集的分割,通常分为训练集和验证集。这样,你就可以开始使用这些数据集来训练YOLO模型了。 在整个标注过程中,始终要关注标注的准确性和一致性,这对于训练出高性能的目标检测模型至关重要。为了深入理解YOLO算法的特点和数据集制作的最佳实践,建议参考《SAM自动标注工具:目标检测与语义分割数据集制作》这一资源,它不仅提供了工具使用说明,还包含了数据集制作和目标检测算法的详细指南,有助于你更全面地掌握整个流程。 参考资源链接:[SAM自动标注工具:目标检测与语义分割数据集制作](https://wenku.csdn.net/doc/3k13g39dur?spm=1055.2569.3001.10343)
评论 236
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值