本文將示範如何在 Ubuntu Linux 系統下,只使用一張 2D 照片,透過 SAM 3D Objects 工具建立物體的 3D 的網格模型。
這一張照片是我在路上用手機隨手拍的街景,照片中有一台白色轎車,以及後方路邊的大樹,我們希望可以只靠著這一張 2D 的照片,建立出白色轎車與大樹的 3D 網格(mesh)模型,以及高斯潑濺場景模型。

隨手拍的街景
我們使用的方法主要是 facebook 開發的 SAM 3D Objects,他可以從 2D 的照片直接生成 3D 的網格模型,屬於生成式人工智慧(Generative AI),而這種 3D 模型生成方式並不是非常真實,只是創造一個跟照片中很相似的模型而已。
本文所使用的環境為 Ubuntu Linux 24.04.4 LTS,Python 採用 3.12 版。
使用 SAM 產生遮罩
由於 SAM 3D 的輸入除了原始 2D 影像之外,還需要物體的 2D 影像遮罩,這部份我們隨意挑了 facebook 的 SAM 模型來產生,若要更換成其他的來源也可以,只要是 2D 遮罩就可以使用。
先建立一個專案目錄與獨立的 Python 虛擬環境:
# 建立一個專案目錄
mkdir part1-segment-anything-masks
cd part1-segment-anything-masks
# 用 python3.12 建立 venv 虛擬環境
python3.12 -m venv .venv
# 載入 venv 環境
source .venv/bin/activate
# 啟動 venv 後,先升級 pip
pip install --upgrade pip
安裝 SAM 與其所需要的必要套件:
# 安裝 SAM 與其所需要的必要套件
pip install torch torchvision segment_anything numpy pillow
確認安裝是否成功:
# 測試安裝是否成功
python -c "import torch, torchvision, segment_anything; print(torch.__version__, torch.cuda.is_available())"
若沒出現錯誤,且有顯示 Torch 的版本,就表示沒問題。
2.13.0+cu130 True
接著使用 SAM 進行圖像分割,SAM 模型有三種不同的版本,分別是 ViT-H、ViT-L、ViT-B,這裡我們採用預設的 ViT-H 作為示範,參考 SAM 的說明文件下載權重檔案:
# 下載 SAM ViT-H 模型權重(約 2.5 GB)
curl -O https://dl.fbaipublicfiles.com/segment_anything/sam_vit_h_4b8939.pth
開始使用 SAM 模型進行圖像分割,這裡我們撰寫一個 make_masks.py 指令稿,主要做以下四件事:
- 載入 SAM ViT-H 模型權重。
- 以自動模式掃描整張影像,分割影像中所有物件。
- 將每一個分割出來的物件依照面積排序,挑出最大的
TOP_N個「互不重疊」物體(與已選候選 IoU 過高者剔除)。 - 匯出
TOP_N張灰階的影像遮罩,加上一張套疊遮罩的影像(除錯用)。
以下是 make_masks.py 指令稿內容:
import os
import numpy as np
import torch
from PIL import Image
from segment_anything import SamAutomaticMaskGenerator, sam_model_registry
# 輸入影像檔案
inputImage = "street-20260816-01.webp"
# SAM ViT-H 模型權重檔案
SAM_VIT_H_CKPT = "sam_vit_h_4b8939.pth"
# 挑最大的幾個物體
TOP_N = 5
# 輸出目錄
OUTPUT_DIR = "output"
# 使用 GPU 或 CPU
device = "cuda" if torch.cuda.is_available() else "cpu"
# 載入 SAM ViT-H 模型權重
model = sam_model_registry["vit_h"](checkpoint=SAM_VIT_H_CKPT).to(device=device)
generator = SamAutomaticMaskGenerator(model)
# 載入輸入的影像
image = np.array(Image.open(inputImage).convert("RGB"))
H, W = image.shape[:2]
# 自動產生影像分割遮罩
masks = generator.generate(image)
print(f"已產生 {len(masks)} 個影像分割遮罩")
# 計算基本統計量
def stats(m):
seg = m["segmentation"]
area = int(seg.sum())
ys, xs = np.nonzero(seg)
return {
"seg": seg,
"area": area,
"centroid": (int(xs.mean()), int(ys.mean())),
"bbox": m["bbox"],
"rgb": [int(v) for v in image[seg].mean(axis=0)],
}
cands = [stats(m) for m in masks]
# 挑最大的 TOP_N 個「互不重疊」物體(NMS:與已選候選 IoU 過高者剔除)
IOU_MIN = 0.3
def iou(a, b):
inter = (a["seg"] & b["seg"]).sum()
union = (a["seg"] | b["seg"]).sum()
return inter / union if union else 0.0
top = []
for c in sorted(cands, key=lambda c: c["area"], reverse=True):
if all(iou(c, t) < IOU_MIN for t in top):
top.append(c)
if len(top) == TOP_N:
break
for i, c in enumerate(top):
print(f"top {i}: 中心點", c["centroid"], "面積", c["area"], "RGB", c["rgb"])
# 匯出只有 0 與 255 的遮罩灰階 PNG 遮罩
os.makedirs(OUTPUT_DIR, exist_ok=True)
for i, c in enumerate(top):
Image.fromarray((c["seg"].astype(np.uint8) * 255), mode="L").save(os.path.join(OUTPUT_DIR, f"{i}.png"))
# 套疊遮罩的影像(除錯用)
PALETTE = [(255, 0, 0), (0, 200, 0), (0, 120, 255), (255, 190, 0), (200, 0, 255)]
overlay = image.copy()
for i, c in enumerate(top):
overlay[c["seg"]] = (overlay[c["seg"]] * 0.4 + np.array(PALETTE[i % len(PALETTE)], np.uint8) * 0.6).astype(np.uint8)
Image.fromarray(overlay, mode="RGB").save(os.path.join(OUTPUT_DIR, "overlay.png"))
執行 make_masks.py 指令稿:
# 執行 make_masks.py 指令稿
python make_masks.py
執行完之後,我們就可以得到 2D 影像分割的結果:
top 0: 中心點 (952, 1219) 面積 804424 RGB [119, 125, 133] top 1: 中心點 (1264, 346) 面積 669482 RGB [52, 59, 44] top 2: 中心點 (381, 215) 面積 356494 RGB [171, 189, 212] top 3: 中心點 (441, 936) 面積 170260 RGB [119, 128, 134] top 4: 中心點 (540, 659) 面積 146488 RGB [151, 110, 104]
輸出的檔案中,包含各個物件的灰階遮罩,以及一張開發與除錯用的遮罩套疊影像。

遮罩套疊影像
這裡我直接用人工的方式挑出白色車子(編號 3)與後方樹木(編號 1)的分割,手動把這兩個分割拿出來給後面的 SAM 3D Objects 建立 3D 模型,如果要完全自動判斷,可以考慮加入 YOLO 模型 來處理。
使用 SAM 3D Objects 建立 3D 網格模型
有了原始影像與對應的物件遮罩之後,就可以使用 SAM 3D Objects 建立 3D 模型了。
這裡有一些工具我們要自行編譯,所以要先安裝 CUDA toolkit:
# 安裝 CUDA toolkit
sudo apt update
sudo apt install -y nvidia-cuda-toolkit g++
# 確認 nvcc 正常可用
nvcc --version
為了讓工作環境單純,這裡另外建立一個新的工作目錄與 Python 環境:
# 建立一個專案目錄
mkdir part2-sam3d-reconstruction
cd part2-sam3d-reconstruction
# 用 python3.12 建立 venv 虛擬環境
python3.12 -m venv .venv
# 載入 venv 環境
source .venv/bin/activate
# 啟動 venv 後,先升級 pip
pip install --upgrade pip
facebook 的 SAM 3D Objects 模型權重要在 Hugging Face 上面註冊,提出下載申請通並過審核之後,再把 token 放在 ~/.cache/huggingface/token,才能進行以下的模型權重下載。
# 下載 SAM 3D Objects 專案原始碼
git clone https://github.com/facebookresearch/sam-3d-objects.git sam-3d-objects
# 下載權重:先在 https://huggingface.co/facebook/sam-3d-objects 網頁核准
# 將 token 放在 ~/.cache/huggingface/token 才能下載
pip install 'huggingface-hub[cli]'
hf download --repo-type model \
--local-dir checkpoints-hf-download \
--max-workers 1 \
facebook/sam-3d-objects
mv checkpoints-hf-download/checkpoints sam-3d-objects/checkpoints/hf
rm -rf checkpoints-hf-download
安裝 SAM 3D Objects 與其所需要的必要套件:
# 安裝 torch(cu121 wheel,從 pytorch 官方來源)
pip install torch==2.5.1+cu121 torchvision==0.20.1+cu121 \
--index-url https://download.pytorch.org/whl/cu121
# 安裝 numpy 對齊 1.26.4(pytorch3d/gsplat 編譯要對齊 C ABI)
pip install numpy==1.26.4 --index-url https://pypi.org/simple \
--extra-index-url https://pypi.org/simple --trusted-host pypi.org
# 安裝必要套件
pip install \
hydra-core==1.3.2 omegaconf==2.3.0 optree==0.14.1 einops==0.8.2 \
einops-exts==0.0.4 easydict==1.13 loguru==0.7.2 astor==0.8.1 \
timm==0.9.16 trimesh==5.0.0 plyfile==1.1.3 pymeshfix==0.17.0 \
xatlas==0.0.9 safetensors==0.8.0 python-igraph==0.11.8 \
matplotlib==3.11.1 plotly==6.9.0 seaborn==0.13.2 pyvista==0.48.4 \
scipy==1.17.1 opencv-python==4.9.0.80 imageio==2.37.4 \
imageio-ffmpeg==0.6.0 av==12.0.0 gradio==5.49.0 \
lightning==2.3.3 lovely-numpy==0.2.24 fvcore==0.1.5.post20221221 \
iopath==0.1.10 wheel setuptools ninja \
--index-url https://pypi.org/simple --extra-index-url https://pypi.org/simple --trusted-host pypi.org
pip install spconv-cu121==2.3.8 xformers==0.0.28.post3 open3d==0.19.0 \
--index-url https://pypi.org/simple --extra-index-url https://download.pytorch.org/whl/cu121 --trusted-host pypi.org
pip install kaolin==0.17.0 \
--index-url https://pypi.org/simple --trusted-host pypi.org \
--find-links https://nvidia-kaolin.s3.us-east-2.amazonaws.com/torch-2.5.1_cu121.html
pip install git+https://github.com/microsoft/MoGe.git@a8c37341bc0325ca99b9d57981cc3bb2bd3e255b \
--index-url https://pypi.org/simple --extra-index-url https://pypi.org/simple --trusted-host pypi.org
# 編譯 flash_attn、pytorch3d、gsplat
IDX="--index-url https://pypi.org/simple --extra-index-url https://pypi.org/simple --trusted-host pypi.org"
# flash_attn
pip install --no-build-isolation $IDX 'flash-attn==2.8.3'
# pytorch3d
pip install --no-build-isolation $IDX \
'git+https://github.com/facebookresearch/pytorch3d.git@75ebeeaea0908c5527e7b1e305fbc7681382db47'
# gsplat
pip install --no-build-isolation $IDX \
'git+https://github.com/nerfstudio-project/gsplat.git@2323de5905d5e90e035f792fe65bad0fedd413e7'
# hydra 補丁(官方 doc/setup.md 最後要求,修 1.3.2 的 utils.py)
(cd sam-3d-objects && python patching/hydra)
# 檢查環境是否安裝成功
cd sam-3d-objects && python - <<'PY'
import os, sys
root = os.path.abspath(os.curdir)
sys.path.insert(0, root); sys.path.insert(0, os.path.join(root, "notebook"))
os.environ["CONDA_PREFIX"] = os.path.abspath(os.path.join(root, "..", ".venv"))
for m in ["torch","pytorch3d","kaolin","gsplat","flash_attn","hydra","omegaconf",
"utils3d","moge","trimesh","seaborn","gradio","imageio","xformers",
"open3d","spconv","lightning","optree","xatlas","igraph"]:
__import__(m)
import torch
print("ALL_IMPORTS_OK", torch.__version__, torch.cuda.is_available())
PY
cd ..
正常的話,會出現以下訊息,沒有出現明顯錯誤就代表安裝成功了。
Warp 1.16.0 initialized:
CUDA Toolkit 12.9, Driver 13.3
Devices:
"cpu" : "x86_64"
"cuda:0" : "NVIDIA H100 PCIe" (79 GiB, sm_90, mempool enabled)
Kernel cache:
/home/ubuntu/.cache/warp/1.16.0
ALL_IMPORTS_OK 2.5.1+cu121 True建立一個 make_3d.py 指令搞,主要做以下幾件事:
- 載入原始影像,以及白色轎車與大樹的灰階遮罩。
- 利用 SAM 3D 推論管線,建立白色轎車與大樹的 3D 模型。
- 將 3D 模型的網格與材質等匯出成
.glb檔案。 - 輸出高斯潑濺的
.ply檔案。 - 將 3D 模型渲染後,輸出
.mp4環繞影片檔案。
指令搞內容如下:
import os, sys
HERE = os.path.dirname(os.path.abspath(__file__))
# SAM 3D Objects 專案目錄
SAM_3D_REPO = os.path.join(HERE, "sam-3d-objects")
# 讓 sam3d_objects 與 inference 可正常引入
sys.path.insert(0, SAM_3D_REPO)
sys.path.insert(0, os.path.join(SAM_3D_REPO, "notebook"))
# inference.py 第 5 行會 `CUDA_HOME = CONDA_PREFIX`;venv 下 CONDA_PREFIX 無意義,
# 真實 nvcc 在 /usr,所以直接將 CONDA_PREFIX 指定為 /usr。
os.environ.setdefault("CONDA_PREFIX", "/usr")
os.environ.setdefault("LIDRA_SKIP_INIT", "1")
import imageio
from inference import (Inference, load_image, load_masks, make_scene,
ready_gaussian_for_video_rendering, render_video)
INP, OUT = os.path.join(HERE, "input"), os.path.join(HERE, "output")
os.makedirs(OUT, exist_ok=True)
# 輸入影像與遮罩
image = load_image(os.path.join(INP, "street-20260816-01.webp"))
car_mask = load_masks(INP, indices_list=[3])[0]
tree_mask = load_masks(INP, indices_list=[1])[0]
# 載入 SAM 3D 推論管線
inference = Inference(os.path.join(SAM_3D_REPO, "checkpoints/hf/pipeline.yaml"), compile=False)
def save_object(mask, name):
# 利用 SAM 3D 推論管線建立模型
out = inference(image, mask, seed=42)
# 3D 模型的網格與材質等
out["glb"].export(f"{OUT}/{name}_mesh.glb")
# 3D 高斯潑濺
out["gs"].save_ply(f"{OUT}/{name}_splat.ply")
scene = ready_gaussian_for_video_rendering(make_scene(out))
# 渲染畫面,輸出環繞影片
frames = render_video(scene, r=1.0, fov=60, pitch_deg=15,
yaw_start_deg=-45, resolution=512)["color"]
imageio.mimsave(f"{OUT}/{name}_orbit.mp4", frames, fps=30)
print(f"[{name}] mesh+splat+video 完成, glb verts={len(out['glb'].vertices)}")
return out
# 建立白色轎車與大樹的 3D 模型
out_car = save_object(car_mask, "white_car")
out_tree = save_object(tree_mask, "tree")
# 建立車與樹的場景
scene = make_scene(out_car, out_tree, in_place=False)
scene.save_ply(f"{OUT}/scene_car_tree.ply")
scene = ready_gaussian_for_video_rendering(scene, fix_alignment=True)
frames = render_video(scene, r=2.0, fov=50, resolution=512)["color"]
imageio.mimsave(f"{OUT}/scene_car_tree.mp4", frames, fps=30)
print(f"[scene] gaussians={int(scene._xyz.shape[0])}")
將原始影像與上面 SAM 模型產生的 2D 遮罩放進來 input 目錄,再執行 make_3d.py 指令稿:
# 建立 input 目錄
mkdir -p input
# 將原始影像與上面 SAM 模型產生的 2D 遮罩放進來
cp ../part1-segment-anything-masks/output/[13].png input/
cp ../part1-segment-anything-masks/street-20260816-01.webp input/
# 執行 make_3d.py 指令稿
python make_3d.py
執行完畢之後,會產生出以下幾種輸出檔案:
.ply高斯潑濺檔案,可以使用 SuperSplat 這類的工具開啟觀看。.glb3D 模型檔案,包含 3D 網格與材質,可用 Blender 這類的專業 3D 軟體進行編輯。.mp4影片檔,方便快速確認 3D 模型。
.glb 檔案是最主要的 3D 模型檔,用 Blender 打開即可進行模型的編輯。

使用 Blender 編輯 3D 模型
