Communitygithub.com

Objekt-Trennung

Trennt eine Person, ein Produkt oder eine Hand vom Hintergrund in einem Video – alles läuft lokal auf dem Computer des Benutzers, nichts wird hochgeladen.

Was ist Objekt-Trennung?

Diese Fähigkeit des KI-Agenten ermöglicht die objektbasierte Videosegmentierung auf dem lokalen Rechner mithilfe des SAM 2 Video-Modells von Facebook. Sie führt eine vollständige Pipeline aus: Auswahl des richtigen Modells basierend auf der Hardware (NVIDIA GPU, Apple Silicon oder CPU), Verarbeitung des Videos in Blöcken, Speicherung von Binärmasken jeder Szene und Erzeugung eines Ausgabevideos mit grün hervorgehobenem Motiv. Der Nutzer muss lediglich Python mit torch und transformers installieren, ein Modell anhand der Gerätespezifikationen auswählen, Ankerpunkte auf dem Startframe setzen und die Segmentierung ausführen. Die Verarbeitung erfolgt in 60-Frame-Blöcken, um den Speicherverbrauch niedrig zu halten, und die Ausgabe umfasst sowohl das farbcodierte Video als auch einzelne Masken-Grafiken, die für weitere Nachbearbeitungsschritte verwendet werden können. Diese Fähigkeit ist ideal für Produktvideos, Tutorials und kreative Projekte, bei denen ein Motiv ohne Aufwändige Greenscreen-Setups isoliert werden muss.

Funktioniert mit~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Barty-Bart/motion-graphics/tree/main/skills/object-separation

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Was macht Objekt-Trennung?

Trennen Sie ein Motiv vom Hintergrund in einem Video. Alles läuft auf dem Computer des Benutzers und es wird nichts hochgeladen. Das Ergebnis ist dasselbe Video mit dem in Grün hervorgehobenen Motiv, sodass der Benutzer genau sehen kann, was getrennt wurde, plus eine Maske für jedes Bild, die auf der Festplatte gespeichert wird.

Sie schreiben und führen das Python während des Vorgangs aus. Die folgenden Codeblöcke sind getestet; passen Sie sie an, anstatt von Grund auf neu zu beginnen.

1. Einrichtung in einem Ordner, der gelöscht werden kann

Benötigt Python 3.10+ und ffmpeg. Installieren Sie alles in einer virtuellen Umgebung im Projektordner, sodass das Entfernen dieses einen Ordners alles entfernt:

python3 -m venv .object-separation
.object-separation/bin/pip install torch transformers opencv-python-headless pillow numpy

Unter Windows verwenden Sie .object-separation\Scripts\pip. Wenn Sie eine NVIDIA-Karte unter Windows haben, installieren Sie zuerst den CUDA-Build von torch von pytorch.org, da einfaches pip die langsamere CPU-Version liefert. Führen Sie das gesamte folgende Python mit dem Python der virtuellen Umgebung aus.

2. Computer durchsuchen und Modell auswählen

Führen Sie zuerst den Scan durch und teilen Sie dem Benutzer mit, was gefunden wurde: das Gerät, das ausgewählte Modell, die Downloadgröße und die zu erwartende Geschwindigkeit.

MaschineModellDownload
NVIDIA GPU mit 12 GB+facebook/sam2.1-hiera-large898 MB
NVIDIA GPU unter 12 GB, oder Apple Siliconfacebook/sam2.1-hiera-base-plus323 MB
Keine GPU (nur CPU)facebook/sam2.1-hiera-tiny156 MB

Auf einer CPU funktioniert es, ist aber langsam: etwa 5 Sekunden pro Bild, also dauert ein 10-Sekunden-Clip bei 30 fps etwa eine halbe Stunde. Es verbraucht unter 2 GB RAM. Eine GPU ist viel schneller. Wenn der Clip lang ist und keine GPU vorhanden ist, schlagen Sie vor, nur den Teil zu trennen, den der Benutzer benötigt.

# Legen Sie diese Werte aus der Benutzeranfrage fest. Bildnummern beginnen bei 0; das Video muss eine konstante Bildrate haben.
VIDEO = "clip.mp4"
START, END = 0, 89              # zu trennende Bilder. Klickpunkte werden auf dem START-Bild ausgewählt.
PTS = [[480, 270], [100, 60]]   # Klickpunkte in 960 Pixel Breite: zuerst auf das Motiv, dann auf Dinge, die ausgelassen werden sollen
LBL = [1, 0]                    # 1 = das ist das Motiv, 0 = das ist NICHT das Motiv
import os, platform, subprocess, numpy as np, torch, cv2
from transformers import Sam2VideoModel, Sam2VideoProcessor

print("system:", platform.system(), platform.machine())
if torch.cuda.is_available():
    gb = torch.cuda.get_device_properties(0).total_memory / 1e9
    print("gpu:", torch.cuda.get_device_name(0), f"{gb:.0f} GB")
    dev, MODEL = "cuda", "facebook/sam2.1-hiera-" + ("large" if gb >= 12 else "base-plus")
elif getattr(torch.backends, "mps", None) and torch.backends.mps.is_available():
    dev, MODEL = "mps", "facebook/sam2.1-hiera-base-plus"
else:
    dev, MODEL = "cpu", "facebook/sam2.1-hiera-tiny"
print("device:", dev, "| model:", MODEL)
model = Sam2VideoModel.from_pretrained(MODEL).to(dev).eval()     # wird beim ersten Ausführen heruntergeladen
proc = Sam2VideoProcessor.from_pretrained(MODEL)
W = 960   # Arbeitsbreite. SAM 2 skaliert selbst auf 1024, daher ist eine kleinere Breite NICHT schneller.

3. Klickpunkte auswählen

Speichern Sie das Bild START mit einem Koordinatengitter und betrachten Sie es. Wählen Sie zwei oder drei Punkte auf dem Motiv und fügen Sie Punkte mit der Bezeichnung 0 auf allem hinzu, das ausgelassen werden muss (Finger, die es halten, Verpackung, der Tisch). Zeigen Sie dem Benutzer, was Sie ausgewählt haben, wenn es nicht offensichtlich ist.

def read_frames(video, a, b):
    cap = cv2.VideoCapture(video); cap.set(cv2.CAP_PROP_POS_FRAMES, a); out = []
    for _ in range(a, b + 1):
        ok, im = cap.read()
        if not ok: break
        out.append(cv2.cvtColor(cv2.resize(im, (W, int(im.shape[0] * W / im.shape[1]))), cv2.COLOR_BGR2RGB))
    return out


def preview(frame, path="work/preview.jpg"):       # Bild mit beschriftetem Gitter zum Auswählen von Klickpunkten
    os.makedirs("work", exist_ok=True)
    im = cv2.cvtColor(read_frames(VIDEO, frame, frame)[0], cv2.COLOR_RGB2BGR)
    for x in range(0, im.shape[1], 80):
        cv2.line(im, (x, 0), (x, im.shape[0]), (0, 255, 255), 1); cv2.putText(im, str(x), (x + 2, 12), 0, 0.4, (0, 255, 255), 1)
    for y in range(0, im.shape[0], 60):
        cv2.line(im, (0, y), (im.shape[1], y), (0, 255, 255), 1); cv2.putText(im, str(y), (2, y - 2), 0, 0.4, (0, 255, 255), 1)
    cv2.imwrite(path, im); return path

4. Das Motiv trennen

Das Video wird in Blöcken von 60 Bildern verarbeitet, damit der Speicher niedrig bleibt. Jeder Block beginnt mit der letzten Maske des vorherigen. Versuchen Sie zuerst einen kurzen Bereich (ca. 20 Bilder), überprüfen Sie ihn und führen Sie dann den vollständigen Bereich aus.

def separate_chunk(frames, points=None, labels=None, mask=None):
    H = frames[0].shape[0]
    s = proc.init_video_session(video=frames, inference_device=dev, video_storage_device="cpu",
                                inference_state_device="cpu", dtype=torch.float32)
    if mask is None:
        proc.add_inputs_to_inference_session(inference_session=s, frame_idx=0, obj_ids=1,
                                             input_points=[[points]], input_labels=[[labels]])
    else:
        proc.add_inputs_to_inference_session(inference_session=s, frame_idx=0, obj_ids=1,
                                             input_masks=mask.astype(np.uint8))
    res = {}
    with torch.inference_mode():
        model(inference_session=s, frame_idx=0)
        for o in model.propagate_in_video_iterator(s, start_frame_idx=0):
            m = proc.post_process_masks([o.pred_masks], original_sizes=[[H, W]], binarize=True)[0]
            res[o.frame_idx] = m.squeeze().cpu().numpy().astype(bool)
    return res


def separate(start, end, pts, lbl, ch=60):
    out, a, seed = {}, start, None
    while a < end:
        b = min(end, a + ch)
        fr = read_frames(VIDEO, a, b)
        r = separate_chunk(fr, points=pts, labels=lbl) if seed is None else separate_chunk(fr, mask=seed)
        for k, m in r.items():
            out[a + k] = m
        seed, a = r[len(fr) - 1], b
    return out


masks = separate(START, END, PTS, LBL)
os.makedirs("work/masks/subject", exist_ok=True)
for f, m in masks.items():
    cv2.imwrite(f"work/masks/subject/{f:05d}.png", m.astype(np.uint8) * 255)

5. Das Video mit dem grün hervorgehobenen Motiv ausgeben

def green(f, img, fill=0.35):                       # grüne Tönung, Leuchten und eine helle Kante am Motiv
    m = cv2.GaussianBlur(cv2.resize(masks[f].astype(np.float32), (img.shape[1], img.shape[0])), (5, 5), 0)
    edge = cv2.morphologyEx((m > 0.5).astype(np.uint8), cv2.MORPH_GRADIENT, np.ones((7, 7), np.uint8)).astype(np.float32)
    glow = np.clip(cv2.GaussianBlur(edge, (37, 37), 0) * 2.5, 0, 1)[..., None]
    col = np.array([20, 255, 57], np.float32)          # BGR grün
    img = img * (1 - m[..., None] * fill) + col * m[..., None] * fill + col * glow * 0.9
    return img * (1 - edge[..., None]) + np.array([220, 255, 200], np.float32) * edge[..., None]


def render(a, b, out, fn):                          # schreibt die Bilder a..b mit dem Original-Audio
    cap = cv2.VideoCapture(VIDEO); cap.set(cv2.CAP_PROP_POS_FRAMES, a)
    fps = cap.get(cv2.CAP_PROP_FPS)
    ff = subprocess.Popen(["ffmpeg", "-v", "error", "-y", "-f", "rawvideo", "-pix_fmt", "bgr24",
                           "-s", f"{int(cap.get(3))}x{int(cap.get(4))}", "-r", str(fps), "-i", "-",
                           "-ss", f"{a / fps:.4f}", "-t", f"{(b - a + 1) / fps:.4f}", "-i", VIDEO,
                           "-map", "0:v", "-map", "1:a?", "-c:v", "libx264", "-crf", "18", "-pix_fmt", "yuv420p",
                           "-c:a", "aac", "-shortest", out], stdin=subprocess.PIPE)
    for f in range(a, b + 1):
        ok, img = cap.read()
        if not ok: break
        ff.stdin.write(np.clip(fn(f, img.astype(np.float32)), 0, 255).astype(np.uint8).tobytes())
    ff.stdin.close(); ff.wait()


render(START, END, "work/separated.mp4", green)

Sehen Sie sich einige Bilder des Ergebnisses an, bevor Sie es übergeben: den Anfang, die Mitte und jeden Moment, in dem eine Hand oder ein Objekt davor vorbeikommt. Wenn Grün auf etwas übergeht, das nicht das Motiv ist, fügen Sie einen Punkt mit der Bezeichnung 0 darauf hinzu und führen Sie Schritt 4 erneut aus. Wenn ein Teil des Motivs fehlt, fügen Sie dort einen Punkt mit der Bezeichnung 1 hinzu.

Teilen Sie dem Benutzer mit, wo sich die Dateien befinden: work/separated.mp4 (das Video mit dem grünen Motiv) und work/masks/subject/ (eine Maske pro Bild, weiß = Motiv). Die Masken bleiben auf der Festplatte, sodass alles, was der Benutzer als Nächstes mit dem getrennten Motiv tun möchte, von ihnen ausgehen kann, ohne die Trennung erneut ausführen zu müssen.

6. Aufräumen (optional)

Wenn der Benutzer fertig ist, kann alles entfernt werden:

  • die virtuelle Umgebung: löschen Sie den Ordner .object-separation
  • das Modell: löschen Sie ~/.cache/huggingface/hub/models--facebook--sam2.1-hiera-* (unter Windows: C:\Users\<Name>\.cache\huggingface\hub)

Hinweise

  • Masken haben harte Kanten: sehr sauber bei Objekten und Händen, weniger bei weichem Haar.
  • Alles, was am Motiv befestigt ist, gilt als Teil davon (Verpackung, Abziehfolie). Fügen Sie einen Punkt mit der Bezeichnung 0 darauf hinzu.
  • Wenn das Video eine variable Bildrate hat, konvertieren Sie es zuerst: ffmpeg -i in.mp4 -vsync cfr -r 30 out.mp4.
  • Nur der erste Durchlauf benötigt das Internet, um das Modell herunterzuladen.

Individual skills in this repo

This repo contains 5 individual skills — each has its own dedicated page.

Verwandte Skills