SAM(Segment Anything)自动分割部署实战
·

自动标注工具可以集成SAM实现自动标注的功能。附源码和使用说明
主要有三个模型,一般用的B,L比较多一点
L模型是1.2G 兼顾精度和速度 大概75ms以内。
下载地址:https://huggingface.co/datasets/Gourieff/ReActor/blob/main/models/sams/sam_vit_l_0b3195.pth

我是直接暴露端口的,然后提供给标注平台使用。
启动之前记得导入环境变量:
export SAM_CHECKPOINT="/home/sam_vit_l_0b3195.pth"
import base64
import io
import os
from typing import List, Optional, Tuple
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from PIL import Image
def try_import_numpy():
try:
import numpy as np # type: ignore
return np
except Exception:
return None
def try_import_cv2():
try:
import cv2 # type: ignore
return cv2
except Exception:
return None
def try_import_torch():
try:
import torch # type: ignore
return torch
except Exception:
return None
def try_import_segment_anything():
try:
from segment_anything import SamPredictor, sam_model_registry # type: ignore
return SamPredictor, sam_model_registry
except Exception:
return None, None
np = try_import_numpy()
cv2 = try_import_cv2()
torch = try_import_torch()
SamPredictor, sam_model_registry = try_import_segment_anything()
class SegmentRequest(BaseModel):
image: str
x: int
y: int
multi: bool = True
class SamService:
def __init__(self) -> None:
self.backend = "unavailable"
self.device = "cpu"
self.predictor = None
self.reason = ""
self.sam_model_type = os.getenv("SAM_MODEL_TYPE", "vit_l")
self.sam_checkpoint = os.getenv(
"SAM_CHECKPOINT",
"/home/sam_vit_l_0b3195.pth"
).strip()
self.max_results = int(os.getenv("SAM_MAX_RESULTS", "3"))
self.polygon_epsilon_ratio = float(os.getenv("SAM_POLYGON_EPSILON_RATIO", "0.005"))
self.min_area = int(os.getenv("SAM_MIN_AREA", "80"))
self._init_backend()
def _init_backend(self) -> None:
if np is None or cv2 is None:
self.backend = "unavailable"
self.device = "missing_numpy_or_cv2"
self.reason = "numpy/cv2 not installed"
return
if SamPredictor is None or sam_model_registry is None:
self.backend = "unavailable"
self.device = "missing_segment_anything"
self.reason = "segment_anything is not installed"
return
if torch is None:
self.backend = "unavailable"
self.device = "missing_torch"
self.reason = "torch is not installed"
return
if not self.sam_checkpoint:
self.backend = "unavailable"
self.device = "missing_checkpoint"
self.reason = "SAM_CHECKPOINT is not set"
return
if not os.path.exists(self.sam_checkpoint):
self.backend = "unavailable"
self.device = "missing_checkpoint_file"
self.reason = f"SAM checkpoint not found: {self.sam_checkpoint}"
return
try:
self.device = "cuda" if torch.cuda.is_available() else "cpu"
sam = sam_model_registry[self.sam_model_type](checkpoint=self.sam_checkpoint)
sam.to(device=self.device)
self.predictor = SamPredictor(sam)
self.backend = "sam"
self.reason = ""
except Exception as exc:
self.backend = "unavailable"
self.device = "init_failed"
self.predictor = None
self.reason = f"failed to initialize SAM: {exc}"
def health(self) -> dict:
if self.backend == "unavailable":
return {
"ok": False,
"backend": self.backend,
"device": self.device,
"message": self.reason or "SAM is unavailable",
"model_type": self.sam_model_type,
"checkpoint": self.sam_checkpoint or None
}
return {
"ok": True,
"backend": self.backend,
"device": self.device,
"model_type": self.sam_model_type if self.backend == "sam" else None,
"checkpoint": self.sam_checkpoint if self.backend == "sam" else None
}
def segment(self, image_data_url: str, x: int, y: int, multi: bool = True) -> List[dict]:
image_bgr = self._decode_image(image_data_url)
height, width = image_bgr.shape[:2]
if not (0 <= x < width and 0 <= y < height):
raise HTTPException(status_code=400, detail="Click point is outside the image")
if self.backend == "sam" and self.predictor is not None:
return self._segment_with_sam(image_bgr, x, y, multi)
raise HTTPException(status_code=503, detail=self.reason or "SAM service backend is unavailable")
def _decode_image(self, image_data_url: str):
if np is None or cv2 is None:
raise HTTPException(status_code=503, detail="numpy/cv2 not installed")
if "," not in image_data_url:
raise HTTPException(status_code=400, detail="Invalid image data URL")
try:
_, encoded = image_data_url.split(",", 1)
image_bytes = base64.b64decode(encoded)
image = Image.open(io.BytesIO(image_bytes)).convert("RGB")
image_np = np.array(image)
return cv2.cvtColor(image_np, cv2.COLOR_RGB2BGR)
except Exception as exc:
raise HTTPException(status_code=400, detail=f"Failed to decode image: {exc}")
def _segment_with_sam(self, image_bgr, x: int, y: int, multi: bool) -> List[dict]:
image_rgb = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2RGB)
self.predictor.set_image(image_rgb)
point_coords = np.array([[x, y]])
point_labels = np.array([1])
masks, scores, _ = self.predictor.predict(
point_coords=point_coords,
point_labels=point_labels,
multimask_output=bool(multi),
)
results = []
for mask, score in zip(masks, scores):
polygon = self._mask_to_polygon(mask.astype("uint8") * 255, x, y)
if polygon is None:
continue
results.append({
"polygon": polygon,
"score": float(score),
})
results.sort(key=lambda item: item["score"], reverse=True)
return results[: self.max_results]
def _mask_to_polygon(self, binary_mask, click_x: int, click_y: int) -> Optional[List[List[int]]]:
contours, _ = cv2.findContours(binary_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if not contours:
return None
selected = None
selected_area = 0.0
for contour in contours:
area = cv2.contourArea(contour)
if area < self.min_area:
continue
inside = cv2.pointPolygonTest(contour, (float(click_x), float(click_y)), False) >= 0
if inside and area > selected_area:
selected = contour
selected_area = area
if selected is None:
selected = max(contours, key=cv2.contourArea)
if cv2.contourArea(selected) < self.min_area:
return None
perimeter = cv2.arcLength(selected, True)
epsilon = max(1.0, perimeter * self.polygon_epsilon_ratio)
approx = cv2.approxPolyDP(selected, epsilon, True)
polygon = [[int(point[0][0]), int(point[0][1])] for point in approx]
if len(polygon) < 3:
return None
return polygon
def _dedupe_results(self, results: List[dict]) -> List[dict]:
deduped = []
seen = set()
for item in results:
polygon = item["polygon"]
key = tuple((int(x), int(y)) for x, y in polygon)
if key in seen:
continue
seen.add(key)
deduped.append(item)
return deduped
service = SamService()
app = FastAPI(title="SAM Helper Service")
@app.get("/health")
def health():
return service.health()
@app.post("/segment")
def segment(request: SegmentRequest):
results = service.segment(request.image, request.x, request.y, request.multi)
return {
"success": len(results) > 0,
"backend": service.backend,
"device": service.device,
"results": results,
}
if __name__ == "__main__":
import uvicorn
host = os.getenv("SAM_HOST", "127.0.0.1")
port = int(os.getenv("SAM_PORT", "8002"))
uvicorn.run(app, host=host, port=port)
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)