222 lines
6.5 KiB
Python
222 lines
6.5 KiB
Python
"""
|
|
SegFormer-B0 最小化Demo
|
|
用于快速上手图像语义分割
|
|
"""
|
|
|
|
import os
|
|
import torch
|
|
import numpy as np
|
|
from PIL import Image
|
|
import matplotlib.pyplot as plt
|
|
from transformers import SegformerImageProcessor, SegformerForSemanticSegmentation
|
|
|
|
|
|
class SegFormerDemo:
|
|
def __init__(self, model_name="nvidia/segformer-b0-finetuned-ade-512-512"):
|
|
"""
|
|
初始化SegFormer-B0模型
|
|
|
|
Args:
|
|
model_name: 预训练模型名称
|
|
- "nvidia/segformer-b0-finetuned-ade-512-512" (ADE20K数据集,150类)
|
|
- "nvidia/segformer-b0-finetuned-cityscapes-1024-1024" (城市街景)
|
|
"""
|
|
print(f"加载SegFormer模型: {model_name}")
|
|
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
|
print(f"使用设备: {self.device}")
|
|
|
|
# 加载处理器和模型
|
|
self.processor = SegformerImageProcessor.from_pretrained(model_name)
|
|
self.model = SegformerForSemanticSegmentation.from_pretrained(model_name)
|
|
self.model.to(self.device)
|
|
self.model.eval()
|
|
|
|
# ADE20K数据集的类别数
|
|
self.num_classes = self.model.config.num_labels
|
|
print(f"模型加载完成! 支持 {self.num_classes} 个类别")
|
|
|
|
def segment_image(self, image_path):
|
|
"""
|
|
对图像进行语义分割
|
|
|
|
Args:
|
|
image_path: 图像路径
|
|
|
|
Returns:
|
|
pred_seg: 分割结果 (H, W) 每个像素的类别ID
|
|
original_image: 原始图像
|
|
"""
|
|
# 读取图像
|
|
image = Image.open(image_path).convert("RGB")
|
|
|
|
# 预处理
|
|
inputs = self.processor(images=image, return_tensors="pt")
|
|
inputs = {k: v.to(self.device) for k, v in inputs.items()}
|
|
|
|
# 推理
|
|
with torch.no_grad():
|
|
outputs = self.model(**inputs)
|
|
logits = outputs.logits
|
|
|
|
# 上采样到原始尺寸
|
|
upsampled_logits = torch.nn.functional.interpolate(
|
|
logits,
|
|
size=image.size[::-1], # (height, width)
|
|
mode="bilinear",
|
|
align_corners=False
|
|
)
|
|
|
|
# 获取每个像素的类别
|
|
pred_seg = upsampled_logits.argmax(dim=1)[0].cpu().numpy()
|
|
|
|
return pred_seg, np.array(image)
|
|
|
|
def visualize_segmentation(self, image, pred_seg, save_path=None):
|
|
"""
|
|
可视化分割结果
|
|
|
|
Args:
|
|
image: 原始图像 (H, W, 3)
|
|
pred_seg: 分割结果 (H, W)
|
|
save_path: 保存路径
|
|
"""
|
|
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
|
|
|
|
# 原始图像
|
|
axes[0].imshow(image)
|
|
axes[0].set_title("Original Image")
|
|
axes[0].axis("off")
|
|
|
|
# 分割mask(彩色)
|
|
axes[1].imshow(pred_seg, cmap='tab20b')
|
|
axes[1].set_title("Segmentation Mask")
|
|
axes[1].axis("off")
|
|
|
|
# 叠加显示
|
|
axes[2].imshow(image)
|
|
axes[2].imshow(pred_seg, alpha=0.5, cmap='tab20b')
|
|
axes[2].set_title("Overlay")
|
|
axes[2].axis("off")
|
|
|
|
plt.tight_layout()
|
|
|
|
if save_path:
|
|
plt.savefig(save_path, dpi=150, bbox_inches='tight')
|
|
print(f"结果已保存到: {save_path}")
|
|
|
|
plt.show()
|
|
|
|
def extract_object_mask(self, pred_seg, target_classes):
|
|
"""
|
|
提取特定类别的mask
|
|
|
|
Args:
|
|
pred_seg: 分割结果 (H, W)
|
|
target_classes: 目标类别ID列表
|
|
|
|
Returns:
|
|
mask: 二值mask (H, W)
|
|
"""
|
|
mask = np.isin(pred_seg, target_classes)
|
|
return mask.astype(np.uint8) * 255
|
|
|
|
def extract_food_region(self, image, pred_seg):
|
|
"""
|
|
提取食物区域(基于ADE20K数据集)
|
|
|
|
ADE20K中食物相关类别(部分):
|
|
- 53: food, 54: table, 55: chair, 56: car, 57: person
|
|
- 116: plate, 117: banana, 118: apple, 119: sandwich, 120: orange
|
|
|
|
Args:
|
|
image: 原始图像 (H, W, 3)
|
|
pred_seg: 分割结果 (H, W)
|
|
|
|
Returns:
|
|
food_image: 提取的食物区域图像
|
|
food_mask: 食物区域mask
|
|
"""
|
|
# 食物相关类别(需要根据实际情况调整)
|
|
food_classes = [53, 116, 117, 118, 119, 120, 121, 122, 123]
|
|
|
|
# 提取mask
|
|
food_mask = self.extract_object_mask(pred_seg, food_classes)
|
|
|
|
# 应用mask
|
|
food_image = image.copy()
|
|
food_image[food_mask == 0] = 255 # 白色背景
|
|
|
|
return food_image, food_mask
|
|
|
|
|
|
def main():
|
|
"""
|
|
主函数:演示SegFormer-B0的基本用法
|
|
"""
|
|
print("=" * 50)
|
|
print("SegFormer-B0 快速上手Demo")
|
|
print("=" * 50)
|
|
|
|
# 1. 创建Demo实例
|
|
demo = SegFormerDemo()
|
|
|
|
# 2. 测试图像路径(请替换为您的图像路径)
|
|
test_image = "test_food3.jpg"
|
|
|
|
# 如果没有测试图像,创建一个示例
|
|
if not os.path.exists(test_image):
|
|
print(f"\n警告: 未找到测试图像 '{test_image}'")
|
|
print("请将您的食物图片重命名为 'test_food.jpg' 或修改代码中的路径")
|
|
print("\n提示: 您可以从以下位置获取测试图像:")
|
|
print(" - dataset目录中的训练图像")
|
|
print(" - 任何食物图片")
|
|
return
|
|
|
|
# 3. 进行分割
|
|
print(f"\n正在处理图像: {test_image}")
|
|
pred_seg, original_image = demo.segment_image(test_image)
|
|
|
|
# 4. 显示统计信息
|
|
unique_classes = np.unique(pred_seg)
|
|
print(f"\n检测到的类别数: {len(unique_classes)}")
|
|
print(f"类别ID: {unique_classes[:20]}...") # 显示前20个
|
|
|
|
# 5. 可视化结果
|
|
print("\n可视化分割结果...")
|
|
demo.visualize_segmentation(
|
|
original_image,
|
|
pred_seg,
|
|
save_path="segmentation_result.png"
|
|
)
|
|
|
|
# 6. 提取食物区域
|
|
print("\n提取食物区域...")
|
|
food_image, food_mask = demo.extract_food_region(original_image, pred_seg)
|
|
|
|
# 可视化食物区域
|
|
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
|
|
axes[0].imshow(original_image)
|
|
axes[0].set_title("Original")
|
|
axes[0].axis("off")
|
|
|
|
axes[1].imshow(food_mask, cmap='gray')
|
|
axes[1].set_title("Food Mask")
|
|
axes[1].axis("off")
|
|
|
|
axes[2].imshow(food_image)
|
|
axes[2].set_title("Extracted Food Region")
|
|
axes[2].axis("off")
|
|
|
|
plt.tight_layout()
|
|
plt.savefig("food_extraction_result.png", dpi=150)
|
|
print("食物提取结果已保存到: food_extraction_result.png")
|
|
plt.show()
|
|
|
|
print("\n" + "=" * 50)
|
|
print("Demo运行完成!")
|
|
print("=" * 50)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|