Communitygithub.com

qiwen-bot/gif-background-removal

從動畫 GIF、影像序列和短片中移除背景,匯出透明 GIF + WebP + APNG。當使用者要求「把背景透明」、「摳圖」、「去背景」、「去白底」、「透明背景 GIF/動圖」或抱怨去背後仍有殘留/雜點/白邊/倒影/沒摳乾淨時使用此技能。

gif-background-removal 是什麼?

這是一個專門用於從動畫 GIF、PNG 序列和短片中移除背景的 AI 技能,並能匯出透明背景的 GIF、WebP 和 APNG 格式。它不僅是簡單的摳圖工具,更內建了嚴謹的可解性判斷機制,能在運算前主動識別主體淺色部件與背景過於接近的案例,避免無意義的長時間運算。此技能支援 Windows 平台,使用 rembg (BiRefNet 模型) 和 scipy 進行背景分離與引導濾波,並透過自動化腳本 (cutout.py, verify.py) 實現管線化處理。其獨特的驗收流程會在不同的底色(灰、深色、白色)上逐幀檢查殘留、雜點與邊緣品質,確保輸出的透明動畫在各種顯示場景下都乾淨無瑕。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add qiwen-bot/gif-background-removal

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

GIF / 動圖 透明背景摳圖

需要收集的輸入

  • 原始檔案:GIF、PNG 序列還是影片。影片走 ffmpeg 抽幀(腳本已內建)
  • 觀看底色:使用者是在什麼背景上看這張圖的?深色介面還是白底頁面會暴露完全不同的殘留。 不知道就預設做全套底色驗證(腳本已內建),不要跳過
  • 主體是否有接近背景色的淺色部件(白手套、白靴、白邊)? 這是能力天花板,見下方「先做可解性判斷」

先做可解性判斷(跳過這步會白幹幾十分鐘)

摳圖是資訊恢復問題,不是演算法調優問題。當主體淺色部件與背景在顏色上接近時,後處理 無法把它們分開 —— 色度、平滑度、時間中位數、背景曲面擬合全會誤傷主體。

別靠肉眼估顏色,跑一下:

.venv-bg\Scripts\python scripts\cutout.py --input <源檔案> --work work --stage solvability

它用「相機靜止 + 主體會動」分離出背景板和主體,輸出三個數:

  • 背景板中位色 / 主體最亮色調 / 兩者差值
  • 落在背景色 ±25/255 以內的主體像素佔比 —— 這個數最可解釋

實測這份機器人素材:背景 (212,207,210),主體最亮 (250,251,250),差值 40.7, 但32.6% 的主體像素落在背景色 25 以內 → 打印 AMBIGUOUS。

看到 AMBIGUOUS 就要先告訴使用者:

這份素材的淺色部件和背景顏色非常接近,純摳圖做不到像素級乾淨,頂多做到「放大後有極細殘留」。 有原始工程的話,換高飽和純綠/品紅幕布重渲一次,我就能做到完全無殘留; 只有成品檔案的話我只能做到逼近乾淨。

然後問使用者要不要繼續,別悶頭跑 20 分鐘。

注意:這個檢查是預警訊號,不是判決。最終以跑完之後的殘留量化為準 —— 「能分」也可能因為模型邊緣不準而出殘留,「不能分」也可能實際摳得很乾淨。

Procedure

  1. 建工作區並裝環境 python -m venv .venv-bg && .venv-bg\Scripts\python -m pip install rembg onnxruntime pillow scipy (scipy 來自 rembg 依賴鏈,引導濾波要用它 —— 見下)

  2. 跑管線

    .venv-bg\Scripts\python scripts\cutout.py --input <源檔案> --outdir out --scale 2
    

    --input 接受影片檔案、GIF/PNG 檔案,或一個已抽好幀的 PNG 序列目錄。 --scale 2 不是可選項:肢體與軀幹之間的縫只有 5–15px,720p 下分割模型根本看不見。 個別幀還頑固就 --stage matte 單獨重跑那幾幀、--scale 3。

    純 CPU 下 70 幀 720p 跑 2× 約 20 分鐘(約 17.6 秒/幀,onnxruntime 無 GPU)。 提前告訴使用者這個量級,別讓人以為卡死。

  3. 驗收(見下節),不通過就定位階段重跑,不要整體重跑。

  4. 交付:GIF + WebP + APNG 三份都給,並說明各自適用場景。

硬規則(違反過、代價都很大)

  • 拓撲處理只用 opening,絕不用 closing。binary_closing 會填掉 ≤2px 的背景縫, 把手臂和軀幹之間的窄縫焊死,之後再多的處理也救不回來。實測這一條比其它所有問題加起來 造成的返工都多。
  • 空洞填充閾值取小值(60px 量級,不是幾百)。真正的肢體縫隙有幾千像素,針孔只有幾十。
  • 引導濾波半徑 ≤2,且預設不要中值濾波。3×3 中值和半徑 4 的引導濾波都會抹掉幾像素寬的縫。
  • 不要對 alpha 做非線性壓縮。曾經寫過 clip((clip((a-0.04)/0.92)-0.06)/0.88) 這種對映, 意圖是收緊邊緣,實際在 alpha≈0.7 處反而抬高了 alpha,等於親手把殘留補得更實。
  • GIF 不要用有序抖動(Bayer)。靜幀上邊緣更柔,但那個網點鎖在螢幕座標系裡, 輪廓一動穿過去就「沸騰」閃爍 —— 動畫裡反而更糟。GIF 走清理後的硬閾值, 柔邊 alpha 留給 WebP / APNG。
  • 模型選 BiRefNet。isnet / u2net 會把地面鏡面倒影判成實體摳進去;換成 birefnet-general-lite 後 alpha 在靴底處直接歸零。
  • 不要為了「更乾淨」去擦背景色。用背景曲面/色度判據大面積擦殘留,會連主體淺色部件一起擦掉。 殘留的正確解法是提高 matte 解析度,不是改閾值。

Output contract

  • out/<name>.gif —— 透明索引 255、transparency=255、disposal=2,1-bit 硬邊。 拓撲清理(去散點)只作用於 GIF
  • out/<name>.webp —— 保留連續 alpha,不做拓撲清理,所以會留一層柔邊暖色毛邊。 疊複雜背景優先用它,這個差異是刻意的,別當成 bug
  • out/<name>.png —— APNG,瀏覽器相容性最好
  • 保持原始檔案的畫布尺寸、幀數、每幀時長、循環設定不變;在回覆裡列出這幾項的實測值 (動畫 WebP 的逐幀時長 PIL 讀不出來,遇到這種情況照實說「以 GIF 實測為準」,別編)
  • 用 <media> 標籤交付檔案,並說明 GIF 與 WebP 各自的適用場景

Failure handling

  • GIF 在瀏覽器裡顯示成灰底:先別改檔案。查 VP8X 的 ALPHA 位與 ANIM 背景色 (verify.py 會打印),兩者都正常的話是檢視器給透明區鋪了底色。 若 verify.py 報 ANMF ... blend=0 ,那才是真問題 —— cutout.py 匯出時會自動改成 blend=1,如果這份檔案的 blend 是 0,說明它不是本腳本產的,或走了外部工具。
  • 主體被啃掉:幾乎都是背景判據太寬。回退到 final/ 那一層重新匯出,不要在擦除邏輯上繼續調參。
  • 某幾幀縫隙沒摳開:--stage matte --scale 3 只重跑那幾幀,單幀成本約 20 秒。
  • rembg 模型下載慢:rembg 2.x 把模型放在 ~/.rembg/models/<model>/<model>.onnx (不是老版的 ~/.u2net),birefnet-general-lite 約 214MB。首次執行要等下載。

驗收(這一步不能省)

必須在使用者真實觀看的那塊底色上逐幀看,否則等於沒驗:

.venv-bg\Scripts\python scripts\verify.py out\<name>.gif --backdrop grey --ref work\final

它會輸出:散點數、逐幀不透明佔比(判斷有無閃爍)、最低不透明行、GIF 相對原始 matte 的 顏色量化誤差、WebP 容器標誌,並生成四底色對比圖 + 全幀接觸表 + 指定區域放大圖。

判斷標準:

指標通過
散點數0
不透明佔比逐幀極差< 0.05
最低不透明行逐幀一致(反射殘留會讓它忽高忽低或頂到畫面底)
顏色量化平均誤差< 3/255(縮圖看不出丟色相,只能靠這個數)
四底色對比每塊底色都能透出來,沒有統一淺灰
接觸表逐幀無殘影、無雜點、肢體縫隙通透

肉眼只看單幀、或只在黑底上看,都會漏掉問題 —— 暖色殘留疊在深灰底上才明顯。

Windows (win32) platform notes

  • 直譯器用 py 或 python,python3 在 Windows 上不存在;別用 PATH 上的 python(可能指向無 pip 的 hermes venv),優先使用 venv 裡的 .venv-bg\Scripts\python.exe
  • 路徑含中文時,用 Write/Read 工具寫腳本檔案,不要用 Set-Content 拼內容
  • PIL 的 ImageFilter.GaussianBlur 不支援 mode "F" 和 float RGB;對浮點圖做模糊用 scipy.ndimage.gaussian_filter(rembg 已經裝了 scipy)
  • Image.fromarray(float32_array, "RGB") 會直接報錯,先 .astype(np.float32) 再按通道處理
  • 不要用 Remove-Item 清理;cutout.py --clean 已經封裝了目錄清理

Examples

輸入:「把這段 GIF 的背景摳透明」 → cutout.py --input 動作.gif --outdir out --scale 2,再 verify.py, → 交付 GIF/WebP/APNG 三份,列出 720×720 / 70 幀 / 每幀 100ms / 總 7000ms。

輸入:「摳乾淨點,邊緣還有雜點,手臂和身體之間還有沒摳的地方」 → 不要去調閾值。先跑 verify.py --zoom <框選座標> 確認缺陷位置, → 散點多就加 --median 3,縫隙不通用就 --stage matte --scale 3 重跑對應幀, → 再驗收。回覆裡說清是哪一類缺陷、用了哪條修復,別只說「已優化」。

相關技能