洋紅幕配深紅披風:整件衣服被吃成半透明,腳本一聲都沒吭
去背分四步:選底色、生圖、key 掉、驗收。9 月 1 日撞的是第一步:幕色跟主體撞色,那一塊會跟背景一起被吃掉,而且不報錯。更早之前為了一頂帽子連生四輪,全都沒用,因為問題不在生圖那一端。

chroma key 不會告訴你底色挑錯了。它照分數做事,主體身上哪一塊顏色跟幕色夠近,那一塊就跟背景一起走,全程一行錯誤訊息也沒有。
9 月 1 日下午在重生視覺小說《咒泉鄉桃園三結義》裡關羽的一張立繪,她身上裹著一件深紅色的短披風。幕色用慣用的洋紅 #FF00FF,key 完貼上去一看,整件披風是半透明的:換青色底就透出青色,換洋紅底就透出洋紅,像模型把它畫成了一層紗。紅跟洋紅在色相上只差一點,夠近就會中。
只要素材最後要疊到別的圖上,就會走到這一關:角色立繪、表情與服裝差分、道具、貼圖都一樣。整條流程四步,選底色、生圖、key 掉、驗收。第一步挑錯,後面三步就得一直替它擦屁股。
這條線上的工具是 cutout-skill:SKILL.md 寫做法,cutout.py 做事,兩個檔案都是 AI 寫的。剩下那一半是我的:要它做什麼、東西交回來的時候看出哪裡不對、要它改。
下面那頂帽子的四輪,就是挑錯底色的學費。
灰的不是那頂帽子
黑洞先生頭上那頂卡其色禮帽,配的是黃幕 #ffff00,出圖每一次都灰灰的。AI 連續四輪重生,全押在生圖提示詞上:把顏色講得更白話、把 hex 直接塞進提示詞、補一句 NEVER grey。四輪都沒用。
帽子的顏色一次都沒畫錯過。卡其跟黃幕靠得太近,帽子被 key 走一大半,剩下那層半透明貼在白底上,看起來就是灰的。去背成品習慣貼白底檢查,透明處在白底上就是白或淺灰,跟「模型把它畫成灰色」看起來一模一樣。換洋紅 #ff00ff,一次就過。黑洞先生的來歷寫在七日創作者挑戰那篇。
後來多認一個數字
肉眼那一關還是要過,但只靠「看起來像不像」不夠,所以判準換成一個數得出來的東西:不透明區內部(不算邊緣那圈環帶)還剩幾顆半透明像素。健康的圖只有幾百顆,上萬就代表有整片被吃進背景了。
披風那張換綠幕重生之後,這個數字從 99,254 掉到 717。這種量級的差別,縮圖上一點都看不出來。
負控制要一起配。棋盤格跟白底都藏得住「整片被吃掉」,要把成品貼到一個跟主體、跟幕色都離很遠的顏色上,透明區才會現形。所以 cutout.py check 每次都多吐一張貼在對照底色上的圖,預設是洋紅。
也可以數不透明像素,修好前後的差值就是被吃掉的量,帽子那次是 30k 變 51k。
這條判準是那晚幾輪重生之間寫進 SKILL.md 的。這篇裡的像素數多半是腳本印出來的,帽子那組跟百分比是概數。
挑幕色的三條
一、不要叫模型出透明圖或無背景圖。它回的是烤死的棋盤格、一層灰霧,或一圈光暈,疊到真的背景上就顯髒。要它畫的是主體單獨站在一片平塗的純色前面。
二、幕色從主體的顏色清單反推。先把主體身上所有顏色攤開,色相離得最遠的那一支才是候選。手邊常備兩支,綠 #00FF00 與洋紅 #FF00FF,主體偏綠就走洋紅,主體偏粉紫就走綠。
三、一個角色的素材從頭到尾綁死同一支幕色。基本立繪、表情、服裝、姿勢差分全部同一支,切出來才是同一組參數,疊上去也才對得齊。
底色選對之後,才輪到演算法
9 月 1 日 cutout-skill 進了四筆 commit,撞色那條是其中一筆。另外三筆都在處理演算法:
- 一筆改 matte 的演算法,改走線性斜坡。柔邊回來了,半透明像素從 0 變成數千,鑲邊從 4 到 7% 掉到 0%。
- 一筆管縮放:一定要在 premultiplied alpha 下縮。透明區的 RGB 還是幕色,重採樣會把它拉回邊緣。單張 key 完的殘留色邊只有 0.07%,縮完貼進統一畫布就變 2 到 5%,每一張都中。
- 一筆管「被主體包住的區塊要不要補回不透明」。眼白跟瞳孔反光該保護,可是演算法把披風縫隙漏出來的 1647px 純綠,當成「不連邊界又夠小」補了回去,成品上留了一塊沒去乾淨的綠斑。那天晚上我丟了一句「你的批風有一塊綠色沒去背喔???」退件,之後改成先看那一塊的 key 分數,夠純的幕色一律不補,多小都不補。
這三條每一條都有差。可是它們站在同一個前提上:幕色沒有撞到主體。撞色的時候,這三條照樣在做事,救不了選錯的那一步。
接下來的九天
9 月 3 日,第三色從「責任在你」變成做得起來。 skill 原本寫的是綠幕洋紅幕以外的顏色後果自負。把分數的算法攤開來看,它只是在看 key 色佔哪幾個通道:綠幕 g - max(r,b)、洋紅幕 min(r,b) - g、藍幕 b - max(r,g),同一個式子套到藍、青、黃都成立。
同一天也把黑、白、灰擋死。它們沒有色度,chroma key 在數學上無從下手,傳進去會直接報錯停住,不會安靜地什麼都不做。自動判幕還是只認綠與洋紅,判到別的顏色就擋下來要求重生,第三色得自己傳 --key #RRGGBB。
9 月 4 日,劉關張整組換藍幕。 綠幕救了披風,可是同一個人換回正裝就換一個問題:袍子是深綠織金,看起來會撞上綠幕。三個人的配色是米白配墨綠、深綠織金加紅披風、黑配紅。綠幕吃綠,洋紅幕吃關張身上的紅,常備的兩支看起來都不保險,整組改成純藍 #0000FF。
這支常數現在散在 larch-taoyuan 的 7 支腳本裡:常服、表情與姿勢差分、可收集 CG、配角,還有番外白帝城的立繪。封面上排那三張就是它們去背之前的樣子。
9 月 10 日,防線推到生圖那一步,還是漏掉一個。 做四個練習小故事的素材包時,八個角色的立繪用洋紅幕,提示詞裡寫死:衣服與頭髮不可以用紅色、粉紅色、紫色。理論上撞色在生圖那一刻就切掉了,等於用配色的自由度換產線的穩定,記錄裡沒寫這筆交換划不划算。
老闆娘那張沒切掉。她穿深綠圍裙,裡面是米白上衣配深色長裙,那條長裙帶紫,洋紅幕照樣把它吃成半透明。
拿 raw/ 裡的原圖重跑一次 key,那張的半透明像素是 118,672,出貨版是 58,543,中間差的六萬顆是後來補回不透明的。修法沒有回第一步換幕色,是在 build.py 裡加一支 refill():用原圖重判哪裡才是真的背景(純幕色分數夠高,或者從畫面邊界連得到),主體內部那些本身不太像幕色的補回不透明,邊緣留 3px 抗鋸齒帶。
所以生圖那端的禁色只是把撞色的機率壓低,沒有關掉。最後守門的還是驗收那個數字。
自己拿去用
skill 掛 MIT,repo 在 https://github.com/yazelin/cutout-skill。它不綁 Claude,README 寫的是任何 agent 讀 SKILL.md 就能照用法跑 cutout.py,依賴只有 numpy、Pillow、scipy。想先確認環境沒問題就跑 python3 cutout.py demo,它會印四角 alpha、alpha 分佈、殘留色邊,還有「背景沒去乾淨」那一欄。
同一套 chroma 判定也在 LINE 貼圖工作室 那條線上跑,那邊的上架防呆後來被移植回這支 skill 的驗收。自家貼圖產線的 README 把背景沒去乾淨列為上架防呆的第一條,整包出貨前逐張跑一次 check,別等退件通知回來才找是哪一張。