sam3的代码、问题备注

目录

安装

前提条件

下载sam3模型:

一堆报错:

[重大坑位一:Windows 下缺少 triton 包](#重大坑位一:Windows 下缺少 triton 包)

图片识别代码:

问题2:识别不出目标

问题3:胡乱识别,识别结果200个

prompt="person":​编辑

prompt="shoe":

问题4:如何在pycharm中使用conda环境,

视频识别代码:


配置sam3环境:GitHub - facebookresearch/sam3:该仓库提供用于使用Meta Segment Anything Model 3(SAM 3)进行推理和微调的代码,下载训练模型检查点的链接,以及展示如何使用模型的示例笔记本。·GitHub

一定要切到sam3环境,conda activate sam3,Y:

cd Y:\miniconda_\sam3

pip install -e .

pip install -e ".[notebooks]"

git clone https://gitclone.com/github.com/facebookresearch/sam3.git

安装

前提条件

  • Python 3.12 或更高版本
  • PyTorch 2.7 或更高版本
  • 支持CUDA 12.6及以上的CUDA兼容显卡
  1. 创建新的Conda环境:
复制代码
conda create -n sam3 python=3.12
conda deactivate
conda activate sam3
  1. 安装支持 CUDA 的 PyTorch:
复制代码
pip install torch==2.10.0 torchvision --index-url https://download.pytorch.org/whl/cu128
  1. 克隆仓库并安装包:
复制代码
git clone https://github.com/facebookresearch/sam3.git
cd sam3
pip install -e .
  1. 安装额外的依赖,比如笔记本或开发:
复制代码
# For running example notebooks
pip install -e ".[notebooks]"

# For development
pip install -e ".[train,dev]"
  1. 可选择的依赖以加快推理
复制代码
pip install einops ninja && pip install flash-attn-3 --no-deps --index-url https://download.pytorch.org/whl/cu128
pip install git+https://github.com/ronghanghu/cc_torch.git

下载sam3模型:

直接访问 SAM3 在 ModelScope 上的模型页面进行下载

  • 访问地址https://modelscope.cn/models/facebook/sam3

一堆报错:

(1)No module named 'pkg_resources'

pip install setuptools==70.0.0

(2)

重大坑位一:Windows 下缺少 triton 包

直接运行会报 No module named 'triton' 官方 triton 不支持 Windows,但社区大佬已编译好替代版:

复制代码
pip install triton-windows==3.3.0.post19

装完这步基本解决 90% 人的卡死问题

(3)

(sam3) Y:\miniconda_\sam3>python -c "import torch; print('CUDA可用:', torch.cuda.is_available()); print('显卡:', torch.cuda.get_device_name(0) if torch.cuda.is_available() else '无')"

CUDA可用: True

显卡: NVIDIA GeForce RTX 2060

(sam3) Y:\miniconda_\sam3>python -c "from sam3.model_builder import build_sam3_image_model; model = build_sam3_image_model(); print('文本编码器存在:', hasattr(model, 'text_encoder'))"

文本编码器存在: False

明确问题:文本编辑器不存在,模型从modelscope下载

图片识别代码:

python 复制代码
import torch
import matplotlib.pyplot as plt
from PIL import Image

from sam3.model_builder import build_sam3_image_model
from sam3.model.sam3_image_processor import Sam3Processor
from sam3.visualization_utils import plot_results

# 加载模型(会自动读取本地 sam3.pt)
model = build_sam3_image_model()
print(" 模型加载成功!模型类型:", type(model))
processor = Sam3Processor(model,
    confidence_threshold=0.1)

# 加载测试图片
image = Image.open("assets/images/test_image.jpg")

# 设置图像(这一步会做全图编码)
inference_state = processor.set_image(image)
print("设置图片成功")

# 文本提示分割(换成你想要的词)
inference_state = processor.set_text_prompt(state=inference_state, prompt="person")

# 或者分割鞋子:prompt="shoe"
# 或者试试:prompt="foot" / "sock" / "person" / "hat" 都好使
print("Text prompt set successfully.")

masks = inference_state["masks"]

print("\n===== 结果 =====")
print(f"检测到目标数量:{len(masks)}")

if len(masks) > 0:
    plot_results(image, inference_state)
    plt.show()
else:
    print(" 未检测到目标")
    # 自动重试其他词

问题2:识别不出目标

复制代码
processor = Sam3Processor(model,
    confidence_threshold=0.1)这里手动把置信度门槛挑到0.1

问题3:胡乱识别,识别结果200个

原因:checkpoint 根本没有正确加载,不知道什么时候被注释了

STEP 1:修 model_builder.py(必须)

找到:

复制代码
if checkpoint_path is not None:
    with open(checkpoint_path, "rb") as f:
        checkpoint = torch.load(f, map_location="cpu")
    #_load_checkpoint(model, checkpoint_path)

改成👇:

复制代码
if checkpoint_path is not None:
    _load_checkpoint(model, checkpoint_path)

取消注释

prompt="person":

prompt="shoe":

问题4:如何在pycharm中使用conda环境,

直接在终端:Y:\miniconda\Scripts\activate sam3

视频识别代码:

get_mask_hash,calculate_iou,fuse_match,filter_invalid_target这几个函数是为了防止掩码颜色乱跳,当然还有待优化

python 复制代码
import os
os.environ['ULTRALYTICS_AUTOUPDATE'] = '0'

import torch
import cv2
import numpy as np
from pathlib import Path
from ultralytics.models.sam import SAM3SemanticPredictor
import hashlib

# 【字典】存储 目标ID → 固定颜色,全程只赋值一次,永不修改
id2color = {}
# 【字典】存储上一帧的所有目标:{目标ID: 目标边框坐标}
prev_boxes = {}
# 【字典】存储上一帧所有目标的中心点:{目标ID: (中心x, 中心y)}
prev_centers = {}
# 【最大允许匹配距离】单位:像素
# 两个目标中心点超过200像素,直接判定不是同一个物体
MAX_DIST = 200

# ========== 配置参数 ==========
VIDEO_PATH = r"Y:/miniconda_/sam3/assets/videos/bedroom.mp4"
TEXT_PROMPT = "person"  # 分割目标
CONF_THRESHOLD = 0.25
OUTPUT_PATH = "Y:/miniconda_/sam3/assets/out/bedroom_person_segmented2.mp4"
MODEL_PATH = "Y:/miniconda_/sam3/weights/sam3.pt"  # 请确保sam3.pt文件在当前目录或指定路径

# [剔除错误id]掩码最小面积阈值(根据视频分辨率调整,720P设500,1080P设1000)
MIN_MASK_AREA = 500

#------------------------------------------------新增颜色跟踪的三个函数
def get_mask_hash(mask):#(1)给每个分割目标生成唯一 ID
    # 1. 将掩码转为布尔值:>0.5 = 属于目标,否则=背景
    mask_bool = mask > 0.5
    # 2. 取出所有属于目标的像素坐标 (y坐标, x坐标)
    coords = np.where(mask_bool)
    # 3. 如果掩码为空(没有目标),直接返回None
    if len(coords[0]) == 0:
        return None
    # 4. 找到目标的上下左右边界(最小/最大坐标)
    y_min, y_max = np.min(coords[0]), np.max(coords[0])
    x_min, x_max = np.min(coords[1]), np.max(coords[1])
    # 5. 计算目标中心点坐标 (cx, cy)
    cx, cy = (x_min + x_max) / 2, (y_min + y_max) / 2
    # 6. 计算目标宽度 w 和高度 h
    w, h = x_max - x_min, y_max - y_min
    # 7. 把中心点、宽高拼成唯一字符串(作为目标特征)
    feat = f"{cx:.1f}_{cy:.1f}_{w:.1f}_{h:.1f}"
    # 8. 用MD5哈希把特征转为8位唯一ID(相当于身份证号)
    return hashlib.md5(feat.encode()).hexdigest()[:8]

def calculate_iou(box1, box2):#(2)计算重叠度,两帧的重叠面积占总面积占比
    # 1. 拆解box1:左上角(x1_1,y1_1),右下角(x2_1,y2_1)
    x1_1, y1_1, x2_1, y2_1 = box1
    # 2. 拆解box2:左上角(x1_2,y1_2),右下角(x2_2,y2_2)
    x1_2, y1_2, x2_2, y2_2 = box2
    # 3. 计算两个框重叠区域的左上角坐标(取最靠右、最靠下)
    inter_x1 = max(x1_1, x1_2)
    inter_y1 = max(y1_1, y1_2)
    # 4. 计算两个框重叠区域的右下角坐标(取最靠左、最靠上)
    inter_x2 = min(x2_1, x2_2)
    inter_y2 = min(y2_1, y2_2)
    # 5. 计算重叠区域面积(max(0,...)防止负数,不重叠=0)
    inter_area = max(0, inter_x2 - inter_x1) * max(0, inter_y2 - inter_y1)
    # 6. 计算box1自身面积
    area1 = (x2_1 - x1_1) * (y2_1 - y1_1)
    # 7. 计算box2自身面积
    area2 = (x2_2 - x1_2) * (y2_2 - y1_2)
    # 8. 计算并集面积(两个框合起来的面积,避免重复计算重叠区)
    union = area1 + area2 - inter_area
    # 9. 返回 IoU = 重叠面积 / 并集面积(范围0~1,越大越重合)
    return inter_area / union if union > 0 else 0

def fuse_match(box):#(3)IoU + 距离 综合匹配,
    # 1. 计算当前目标框的中心点坐标 (cx, cy)
    cx = (box[0] + box[2]) / 2
    cy = (box[1] + box[3]) / 2
    # 2. 初始化:最佳匹配ID、最高得分
    best_id = None
    best_score = -1.0
    # 3. 遍历上一帧所有目标,找最可能匹配的同一个物体
    for pid, pbox in prev_boxes.items():
        # 3.1 计算当前框 与 上一帧某个框 的 IoU
        iou = calculate_iou(box, pbox)
        # 3.2 获取上一帧目标的中心点
        pcx, pcy = prev_centers.get(pid, ((pbox[0]+pbox[2])/2, (pbox[1]+pbox[3])/2))
        # 3.3 计算两个中心点的欧几里得距离(像素距离)
        dist = ((cx - pcx)**2 + (cy - pcy)**2)**0.5
        print(f"中心距离={dist}")
        # 3.4 距离超过最大允许值,直接跳过(不可能是同一个物体)
        if dist > MAX_DIST:
            continue
        # 3.5 归一化距离:0=很近,1=最远
        norm_dist = dist / MAX_DIST
        # 3.6 综合打分:IoU占70%,距离占30%(兼顾重叠+移动)
        score = 0.7 * iou + 0.3 * (1 - norm_dist)
        # 3.7 保留得分最高的目标ID(最可能是同一个)
        if score > best_score:
            best_score = score
            best_id = pid
    # 4. 返回最匹配的目标ID,没找到则返回None
    return best_id

# [剔除错误id]过滤误分割的无效目标(置信度+掩码面积双重校验)
def filter_invalid_target(mask, conf):
    # 条件1:置信度低于阈值,直接判定为无效目标
    if conf < CONF_THRESHOLD:
        return False
    # 条件2:掩码二值化后计算面积,像素数过少判定为无效
    mask_bool = mask > 0.5
    mask_area = np.sum(mask_bool)
    if mask_area < MIN_MASK_AREA:
        print(f"【过滤】掩码面积{mask_area} < {MIN_MASK_AREA},判定为误分割")
        return False
    # 双重校验通过,判定为有效目标
    return True

#------------------------------------------------------------------------------------------------------------------------------------------------
# ========== 初始化SAM 3预测器 ==========
print("正在加载SAM 3模型...")
overrides = dict(
    conf=CONF_THRESHOLD,
    task="segment",
    mode="predict",
    model=MODEL_PATH,
    half=True,  # 使用FP16加速推理
    save=False,
)
predictor = SAM3SemanticPredictor(overrides=overrides)
print("模型加载成功!")

# ========== 打开视频 ==========创建视频捕获对象
cap = cv2.VideoCapture(VIDEO_PATH)
if not cap.isOpened():
    print(f"无法打开视频文件: {VIDEO_PATH}")
    exit()

# 获取视频信息
fps = int(cap.get(cv2.CAP_PROP_FPS))
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))

print(f"视频信息:")
print(f"  帧率: {fps} fps")
print(f"  分辨率: {width} x {height}")
print(f"  总帧数: {total_frames}")

# ========== 创建输出视频写入器 ==========
fourcc = cv2.VideoWriter_fourcc(*'mp4v')#视频编码格式,*将字符串 'mp4v' 拆解为四个独立的字符
#函数返回一个整数fourcc,该整数代表指定的编码格式
out = cv2.VideoWriter(OUTPUT_PATH, fourcc, fps, (width, height))
#创建输出视频对象

# ========== 逐帧处理视频 ==========
frame_idx = 0       #初始化计数器,提示开始处理。
print("开始处理视频...")

while True:         #逐帧读取视频
# frame:三维NumPy数组,表示读取到的这一帧图像。形状为(height, width, 3),其中3代表BGR三个颜色通道
    ret, frame = cap.read() #ret(return的缩写):布尔值,表示是否成功读取到一帧。
    print(f"ret={ret}")
    if not ret :
        break


    print(f"处理进度!!: {frame_idx}/{total_frames} 帧 ({frame_idx / total_frames * 100:.1f}%)")

    # 设置当前帧进行分割
    #①将输入的帧调整到模型期望的尺寸(SAM3期望输入尺寸为1024×1024),并进行归一化等操作
    #②将预处理后的图像送入SAM3的image_encoder神经网络(Vision Transformer架构),提取出高维特征向量
    #③将生成的图像嵌入存储在预测器内部的features属性中,供后续的掩码解码器使用
    #(这里的predictor.set_image=SAM3SemanticPredictor.set_image
    #   与图像分割中的Sam3Processor.set_image()都是编码图像,区别是:前者属于ultralytics库,后者属于原生sam3库)
    predictor.set_image(frame)

    # 使用文本提示进行分割,返回的results包含单帧检测到的目标掩码、边界框、置信度等信息
    results = predictor(text=[TEXT_PROMPT])
    print("11111111111111111")
    # 绘制分割结果(len(results) > 0确保有检测结果;results[0].masks确保有掩码生成)
    if len(results) > 0 and results[0].masks is not None:
        # 获取掩码(data获得掩码张量,再将数据从GPU内存移动到CPU内存,转成numpy数组)
        #masks形状是(目标数量,图像高度,图像宽度)(N, H, W),表示每个像素属于目标的概率,就是目标的轮廓图
        masks = results[0].masks.data.cpu().numpy()

        boxes = results[0].boxes.data.cpu().numpy() if results[0].boxes is not None else []
        # 创建空白掩码叠加层,用于绘制掩码的彩色区域。
        overlay = np.zeros((height, width, 3), dtype=np.uint8)

        current_boxes = {}
        current_centers = {}

        # 为每个检测到的对象绘制不同颜色的掩码
        for i, mask in enumerate(masks):
            print(f"正在处理第{frame_idx}帧的第{i}张掩码")
            #print(f"处理----: {frame_idx}/{total_frames} 帧 ({frame_idx / total_frames * 100:.1f}%)")

            # [剔除错误id]1. 获取当前目标的置信度(用于过滤)
            current_conf = 0.0
            if results[0].boxes is not None and len(results[0].boxes) > i:
                current_conf = results[0].boxes[i].conf.cpu().numpy()[0]
            #[剔除错误id]2. 过滤无效目标(误分割),直接跳过不处理
            if not filter_invalid_target(mask, current_conf):
                continue

            mask_bool = mask > 0.5  # 二值化掩码(mask是每个像素属于目标的概率)
            # 随机生成颜色

            # 绘制边界框(如果有)
            if results[0].boxes is not None and len(results[0].boxes) > i:
#results[0]获取第一张图像的检测结果;boxes[i]获取第 i 个边界框的完整信息;xyxy获取该框的坐标属性,
# 它返回一个形状为 [1, 4] 的张量,包含边界框的左上角和右下角坐标 [x1, y1, x2, y2]
#将GPU张量转为CPU上的NumPy数组,将浮点数坐标转为整数像素坐标
                box = results[0].boxes[i].xyxy[0].cpu().numpy().astype(int)

                # 1. 用融合匹配函数,找当前目标对应上一帧的ID(同一个物体)
                target_id = fuse_match(box)
                # 2. 如果没找到匹配 → 判定为新目标 → 生成唯一ID
                if target_id is None:
                    print("target_id=None")
                    target_id = get_mask_hash(mask)
                # 3. 如果这个ID还没有颜色 → 分配一次随机颜色(终身不变)
# 生成3个0~255之间的随机整数,分别代表R、G、B值。.tolist():将NumPy数组转换为Python列表,格式为[R, G, B]
                if target_id not in id2color:
                    print("新目标给新颜色")
                    id2color[target_id] = np.random.randint(0, 255, 3).tolist()
                # 4. 取出该目标固定颜色
                color = id2color[target_id]
                overlay[mask_bool] = color
#绘制边框rectangle参数:图像 + 左上角坐标 + 右下角坐标 + 颜色 + 粗细
                cv2.rectangle(frame, (box[0], box[1]), (box[2], box[3]), color, 2)

                # 记录当前帧信息
                cx = (box[0] + box[2]) / 2
                cy = (box[1] + box[3]) / 2
                current_boxes[target_id] = box
                current_centers[target_id] = (cx, cy)

                # 半透明叠加,第一张图(原始帧)70%透明度,overlay:第二张图(绘制内容),权重0.3(30%透明度
                frame = cv2.addWeighted(frame, 0.7, overlay, 0.3, 0)

                # 显示检测数量,参数(目标图像,要显示的文本内容,文本左下角坐标,字体类型,字体大小,字体颜色BGR格式,字体线条粗细)
                cv2.putText(frame, f"Person count: {len(masks)}", (10, 30),
                            cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
        # 更新上一帧数据
        prev_boxes = current_boxes
        prev_centers = current_centers
    # 写入输出视频
    out.write(frame)
    frame_idx += 1
    print(f"frame_idx={frame_idx}")
    print(f"total_frames={total_frames}")
    if frame_idx > total_frames:
        break

                # ========== 释放资源 ==========
cap.release()
out.release()
cv2.destroyAllWindows()
print(f"\n视频处理完成!")
print(f"输出文件: {OUTPUT_PATH}")
print(f"处理帧数: {frame_idx}")

分割效果:

相关推荐
2601_956906213 小时前
机器学习固态电解质!
机器学习·固态电解质‘
一切皆是因缘际会4 小时前
AI技术落地全景解析:从智能体到具身智能
大数据·人工智能·深度学习·机器学习·架构
无敌昊哥战神4 小时前
【机器学习扫盲】从预测 Score 到ACC、 Precision、Recall、ROC 曲线的白话全解
python·深度学习·算法·机器学习
Mark White5 小时前
奇异值分解(SVD):从几何直觉理解矩阵的本质
线性代数·机器学习·矩阵
迦南的迦 亚索的索5 小时前
机器学习_05_k-means算法
算法·机器学习·kmeans
大模型最新论文速读5 小时前
利用异步编程的 future 思想,让 LLM Agent 快 1.44 倍
人工智能·深度学习·算法·机器学习·自然语言处理
Bingorl5 小时前
机器学习之线性回归算法
算法·机器学习·线性回归
一个王同学13 小时前
从零到一 | CV转多模态大模型 | week09 | Minillava Refactor结合手搓和llava源码深入理解多模态大模型原理
人工智能·深度学习·机器学习·计算机视觉·改行学it
赢乐15 小时前
大模型学习笔记:检索增强生成(RAG)架构
人工智能·python·深度学习·机器学习·智能体·幻觉·检索增强生成(rag)