SoM (Set-of-Mark) 是一种视觉提示(Visual Prompting)技术,通常用于增强多模态大模型(如 GPT-4V, LLaVA)的细粒度空间理解能力。
它的核心逻辑非常直接:
分割(Segmentation):使用 SAM (Segment Anything Model) 等模型将图像分割成不同区域。
标记(Marking):在这些区域上覆盖半透明掩码,并打上数字标签(ID)。
提示(Prompting):将处理后的图发给 GPT-4V,问它:“标签 [5] 的物体是什么?”
下面是一个基于 Python 的完整示例。
准备工作
你需要安装 segment-anything 库以及 PyTorch。
# 1. 安装 PyTorch (根据你的环境选择合适的 CUDA 版本) pip install torch torchvision # 2. 安装 SAM 和 OpenCV pip install opencv-python matplotlib segment-anything
重要提示:你需要下载 SAM 的权重文件(Model Checkpoint)。请下载 sam_vit_h_4b8939.pth (2.4GB) 并放在代码同级目录下。下载地址: Meta AI SAM Repository
1. 完整 SoM 生成代码
这段代码会自动分割图片,并为每个分割区域生成“彩色掩码 + 数字标签”。
import torch
import cv2
import numpy as np
import matplotlib.pyplot as plt
from segment_anything import sam_model_registry, SamAutomaticMaskGenerator
class SoMGenerator:
def __init__(self, checkpoint_path, model_type="vit_h", device="cuda"):
"""
初始化 SAM 模型
"""
print(f"正在加载 SAM 模型 ({device})...")
self.device = device
self.sam = sam_model_registry[model_type](checkpoint=checkpoint_path)
self.sam.to(device=device)
# 初始化自动掩码生成器
# points_per_side: 采样点数,越低速度越快但精度降低
# pred_iou_thresh: 过滤低质量掩码的阈值
self.mask_generator = SamAutomaticMaskGenerator(
model=self.sam,
points_per_side=32,
pred_iou_thresh=0.86,
stability_score_thresh=0.92,
crop_n_layers=0,
crop_n_points_downscale_factor=1,
min_mask_region_area=100, # 忽略太小的区域
)
def generate_som_image(self, image_path, output_path=None):
"""
核心流程:读取图片 -> 分割 -> 绘制标记
"""
# 1. 读取图片
image = cv2.imread(image_path)
if image is None:
raise ValueError("无法读取图片,请检查路径")
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
# 2. 生成掩码
print("正在生成分割掩码 (这可能需要几秒钟)...")
masks = self.mask_generator.generate(image)
print(f"检测到 {len(masks)} 个区域")
# 按面积从大到小排序,避免小标签被大标签覆盖
masks = sorted(masks, key=(lambda x: x['area']), reverse=True)
# 3. 绘制 SoM 效果 (Overlay)
som_image = image.copy()
# 创建一个带有透明度的图层
overlay = image.copy()
font_scale = 0.5
font_thickness = 1
for i, ann in enumerate(masks):
# 获取掩码的布尔矩阵
m = ann['segmentation']
# 生成随机颜色
color = np.random.randint(0, 255, (3,)).tolist()
# --- A. 绘制半透明掩码 ---
# 将掩码区域涂上颜色
overlay[m] = color
# --- B. 计算标签位置 (重心) ---
# 使用 OpenCV 寻找轮廓来确定中心点,比简单的 bbox 中心更准确
contours, _ = cv2.findContours(m.astype(np.uint8), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if contours:
# 获取最大的轮廓
c = max(contours, key=cv2.contourArea)
M = cv2.moments(c)
if M["m00"] != 0:
cX = int(M["m10"] / M["m00"])
cY = int(M["m01"] / M["m00"])
else:
cX, cY = 0, 0
# --- C. 绘制数字标签 (The Mark) ---
label_text = str(i + 1)
# 为了让文字清晰,先画一个黑色背景框或描边
(text_w, text_h), _ = cv2.getTextSize(label_text, cv2.FONT_HERSHEY_SIMPLEX, font_scale, font_thickness)
# 画一个小黑块背景
cv2.rectangle(som_image, (cX - 2, cY - text_h - 2), (cX + text_w + 2, cY + 2), (0, 0, 0), -1)
# 画白色文字
cv2.putText(som_image, label_text, (cX, cY),
cv2.FONT_HERSHEY_SIMPLEX, font_scale, (255, 255, 255), font_thickness, cv2.LINE_AA)
# 混合原图和彩色掩码 (alpha blending)
alpha = 0.5 # 透明度
cv2.addWeighted(overlay, alpha, som_image, 1 - alpha, 0, som_image)
# 4. 显示或保存结果
plt.figure(figsize=(12, 8))
plt.imshow(som_image)
plt.axis('off')
plt.title(f"SoM Result: {len(masks)} Regions")
plt.show()
if output_path:
# 转回 BGR 保存
cv2.imwrite(output_path, cv2.cvtColor(som_image, cv2.COLOR_RGB2BGR))
print(f"结果已保存至: {output_path}")
return som_image, masks
# --- 运行示例 ---
if __name__ == "__main__":
# 检查是否有 GPU,没有的话用 cpu (会很慢)
device = "cuda" if torch.cuda.is_available() else "cpu"
# 请确保路径正确
CHECKPOINT_PATH = "./sam_vit_h_4b8939.pth"
IMAGE_PATH = "./test.jpg" # 换成你的图片路径
try:
som = SoMGenerator(CHECKPOINT_PATH, device=device)
# 运行
som.generate_som_image(IMAGE_PATH, "som_output.jpg")
print("\n--- 构建 Prompt 示例 ---")
print("现在你可以把生成的 'som_output.jpg' 发给 GPT-4V 并提问:")
print("User: 'Look at the image. What object is located at mark [3]?'")
print("User: 'Describe the relationship between object [1] and object [5].'")
except FileNotFoundError:
print("错误:未找到模型权重文件或图片。")
print("请下载 'sam_vit_h_4b8939.pth' 并准备一张 'test.jpg'。")
except Exception as e:
print(f"发生错误: {e}") 2. 代码核心逻辑解析
加载 SAM:sam_model_registry 用于加载 Meta 预训练的 ViT-H 模型。这是目前效果最好的分割模型之一。
SamAutomaticMaskGenerator:这是 SAM 的全自动模式。它不需要你点选,而是会在整个图像上生成网格点,并在每个点上运行推理,最后合并重复的掩码。
min_mask_region_area=100:这行代码很重要,它过滤掉了那些只有几个像素的噪点,保证生成的 Mark 清晰。
Visual Marking (打标):SoM 的精髓在于如何把数字画上去。
颜色叠加:代码使用了 cv2.addWeighted 实现 50% 的透明度,这样即使盖住了物体,模型依然能看到物体原本的纹理。
重心计算:使用 cv2.moments 计算不规则形状的重心(Centroid),确保数字标签位于物体的视觉中心,而不是简单的边界框中心(那样如果是“甜甜圈”形状,数字会打在空心处)。
对比度增强:在白色文字下面画了一个黑色小矩形背景,确保无论掩码颜色多亮,数字都清晰可见。
3. 如何配合大模型使用 (Prompting)
生成完这种带数字的图片后,你的 Prompt 结构应该是这样的:
User:
(上传生成的 som_output.jpg)I have overlaid a Set-of-Mark on this image. Please answer the following questions:Identify the object labeled [12].What action is the person in [5] doing?Is the object [3] closer to the camera than object [8]?GPT-4V / LLaVA Response:The object labeled [12] is a red apple.The person in [5] is holding a cup of coffee. ...
这种技术极大地解决了多模态模型容易产生的幻觉(Hallucination)和空间定位不准的问题。
网友回复
fbx、obj、glb三维格式模型如何在浏览器中通过three相互转换格式?
python如何实现基于http隧道加密的正向代理服务?
有没有有专门针对 UI 界面截图进行智能标记(Set-of-Mark, SoM) 的开源库和工具?
如何用python实现Set-of-Mark (SoM) 技术?
python如何截取windows指定应用的窗口截图,不用管窗口是不是在最前面?
linux能不能给rm删除命令增加回收站功能,可恢复被删文件?
bfwsoa如何在命令行中执行控制器动作器方法?
RAG(检索增强生成)和 KG(知识图谱)有啥不同?
KVM硬件是啥?
ai大模型对于大型项目源码上下文不够是如何解决进行开发与修改功能的?


