iT邦幫忙

2026 iThome 鐵人賽

DAY 23
0
AI 自動化

我以為我保留了五道閘門系列 第 23

Day 23|五十張封面,只有兩張是 AI 畫的

  • 分享至 

  • xImage
  •  

模組五|發布與衍生物(Day 23–27)

我做的是一檔中文新聞 podcast,有些集會請來賓進來對談。錄音走線上多軌,主持人與來賓各自留下一條音軌,之後交給一條自動化管線組裝、上架。

剪輯收在一個沒有客觀標準的東西:好不好只能試聽。而節目做完之後是另一批東西——封面、影片、導流、文案。

而第一個問題是:視覺一致性能不能自動化?

我的答案有點反直覺:能,但不是用生圖模型

五十集,兩張

我把所有封面產生腳本翻了一遍。

只有兩集用了 AI 生圖。 走的是 image edit(影像編輯)模式——不是給一段文字讓模型從零畫,而是餵一張既有的圖進去讓它改:把前一集的封面當風格參考,讓模型在同一個調性下畫新的主視覺(一張封面裡最主要的那張圖,每集不同),輸出 1024×1024,之後用 LANCZOS(放大影像時用的重取樣演算法,邊緣比最近鄰、雙線性那些做法乾淨)升到 2048。

其餘的每一張,都是程式畫的。 Pillow(Python 的影像處理函式庫,可以開畫布、貼圖、寫字、存檔)開一張畫布,貼底色、貼主視覺、用指定字型畫標題與集數編號、輸出。沒有模型參與。

而更早期的做法更土:用 headless 瀏覽器(沒有視窗、由程式驅動的瀏覽器)截圖一份 HTML,3000×3000。

為什麼套版贏了

我一手轉軋輥,每次餵進去的花色都不一樣,壓出來的形狀完全一樣

因為一致性要的東西,剛好是生成模型最給不了的:

同一個字型、同一個位置、同一個比例、同一個留白。

生圖模型每次輸出都不一樣,這是它的特性,不是缺陷。用 image edit 模式加上前一集當參考,可以把「風格」壓在一個範圍內,但壓不住字的位置差 3 像素。

而人眼對「同一個節目的封面」的判斷,恰好非常依賴那 3 像素。風格接近但排版跳動,看起來比風格不同還糟。它會像同一個模板被人手動排過,而不是同一條產線出來的。

所以分工是這樣的:

誰做 為什麼
主視覺 生圖模型(少數集數)/素材庫 需要變化
排版 程式 需要不變

要變的交給模型,要不變的交給程式。

這條可以推廣:任何「每次都要有點不同、但骨架必須一樣」的產出,都適用這個切法。

輸出規格

同一集內容會落在三個不同版位——方形的、橫的、直的——每個版位都要有自己的一張圖。所以一集節目要出三種尺寸:

方形封面   2560 × 2560   (dpi 144)
橫式封面   1280 × 720
直式影片   1080 × 1920

三種比例,三套排版邏輯。而它們共用同一份主視覺與字型。

字型只有兩個檔:一個中文黑體、一個拉丁可變字重字型(variable font,同一個檔就能無段調粗細,不必再另外裝一套粗體檔;集數編號用它,加粗到 700——字重刻度上 400 是一般內文的粗細,700 大約等於粗體)。兩個字型檔,五十集。 這是一致性最便宜的來源:限制選項本身就是一種一致性機制。

但套版自己也在漂

講完優點,講難看的部分。

我把不同時期的腳本參數抓出來對照,箭頭代表同一個參數在不同集數之間換過值:

橫式封面的主視覺卡片   540×540  →  600×600
方形封面的品牌字級      150pt   →  144pt
主視覺區塊尺寸          2070    →  1900
主視覺頂部留白           240    →   330

沒有 design token(設計變數:把字級、間距、尺寸這些數字抽成一份具名的共用設定,所有產出都引用同一份)。 這些數字直接寫在各集的腳本裡,改了就往下傳,沒改的就留在舊值。

所以「同一個節目」這件事,實際上是分段的:某幾集是一組排版,另幾集是另一組。差異小到不容易發現,但它確實在。

而修法很簡單:把這些數字抽成一份共用設定,所有腳本引用它。我沒做,因為每一集寫腳本的時候,複製上一集再改幾個數字是最快的。

這句話你已經在這個系列裡看過好幾次了。今天它有一個更嚴重的版本。

三個 stale copy

我在比對腳本的時候,發現三個檔案有問題。

先說一下這些檔案放在哪:每一集在製作端都有自己的一個目錄,該集要用的腳本一集一份,就躺在那個目錄裡。

兩個集數目錄裡的橫式封面腳本,是 byte-identical(位元組完全相同,連一個字元都不差)的舊集腳本。

不是「很像」,是完全一樣:docstring(寫在 Python 檔案最上面、說明這支腳本在幹嘛的那段文字)寫著舊集號、讀取的檔名是舊集的主視覺、畫在圖上的字串也是舊集號。

還有四份直式圖腳本,md5(把一份檔案內容算成一串固定長度的指紋,內容差一個位元組指紋就完全不同)完全相同,全部指向同一個舊集的音檔。

也就是說:這些腳本現在跑起來,會產出錯的東西。

而它們躺在那裡沒有造成任何問題,因為沒有人跑它們。實際的封面是由另一支正確的腳本產的。

死碼不會報錯

我拉開一格抽屜,裡面躺著貼了別集標籤的東西,外面的牌子寫著本集

死碼(dead code,留在專案裡但實際上不會被執行到的程式)是怎麼長出來的?這件事的形狀值得看清楚。開新一集的時候,有一支建目錄的腳本負責替新集數開一個資料夾、把該集要用的檔案備好,而它的實作方式就是整包複製上一集:

複製上一集的整個目錄     ← 快
  ↓
改了「今天要用的」那幾支腳本
  ↓
沒改「今天沒用到的」那幾支
  ↓
它們留在那裡,內容是錯的,而且沒有任何徵兆

沒有錯誤訊息,因為它們沒有被執行。

而問題會在未來某一天發生:我(或任何人)打開那個目錄,看到一支叫「產出橫式封面」的腳本,跑下去,得到一張別集的封面。

死碼不會報錯,它只是躺在那裡等人踩

Day 27 那篇專門講「複製整個資料夾然後忘記改」這件事,會講這個複製機制造成的更大災難:集數撞號、素材放錯目錄,至少 5 組加 9 起。今天這三個 stale copy 是同一個病最輕微的症狀。

一個沒被利用的線索

有意思的是,這三個檔案其實留下了可偵測的痕跡:它們跟來源檔案的 md5 完全相同。

也就是說,一個很簡單的檢查就能抓到:

對每個集數目錄裡的腳本,算 md5
如果它跟另一個目錄的同名腳本 md5 相同
  → 這支腳本沒有被改過,可能是複製殘留
  → 檢查它的 docstring 與硬編路徑是否指向本集

十幾行程式碼。而我沒寫,直到寫這個系列做比對時,才發現有東西可以抓。

線索一直在磁碟上,只是沒有人去問那個問題

代價

視覺一致性我做到了八成,而剩下兩成沒人在管。

那些漂移的排版數字(卡片從 540 變 600、字級從 150 變 144),我是寫這篇的時候才發現的。它們沒有造成任何抱怨,但它們讓「同一條產線」這件事變成半真的。

第二個代價:兩集用了 AI 生圖,而我沒有記錄那次的成本與 prompt 演進。 那兩張封面的 prompt 直接寫死在腳本裡,沒有版本、沒有「試過什麼被否決」的註解,Day 20 講的是一個背景音樂音量參數改了七次的故事,那個參數旁邊的註解把現在的值、它跟人聲的相對關係、上一個值、為什麼改全寫了;跟那行註解比起來,這裡的紀錄品質差很多。

同一個人,在不同環節的紀錄紀律差這麼多,本身就是一個發現。 音訊我會留理由,圖像我不會,大概因為音訊我調得比較痛。

第三個:那三個 stale copy 我沒刪。 又一項。

帶走什麼

要變的交給模型,要不變的交給程式

視覺一致性不是「風格接近」,是排版完全相同。而排版是程式的強項、模型的弱項。把這兩件事切開,比要求模型「保持一致」有效得多。

第二條,關於一致性最便宜的來源:

限制選項。 兩個字型檔、三種輸出尺寸、一組固定的留白。選項越少,跑五十次的結果越像同一個東西。而這比任何 style guide(設計規範文件,寫明字型、顏色、間距該怎麼用)都有效,因為它不依賴人記得。

第三條,可以今天就做的:

複製出來的檔案,去比對它跟來源的 md5。 相同代表它沒被改過。如果它應該要被改(例如裡面有硬編的識別字串),那它就是一個等著被踩的地雷。

這個檢查十幾行,而它抓的是「複製貼上式初始化」必然產生的殘留。只要你的 scaffold(開新專案/新單位時用來架好初始檔案的那套腳手架)是用複製實作的,你就一定有這種東西。

明天講一個 12 分鐘裡修了六次的 bug,以及為什麼前四次都在猜。


上一篇
Day 22|細剪到 50 分鐘,然後被退回
下一篇
Day 24|12 分鐘六筆 commit,只為了把一行字置中
系列文
我以為我保留了五道閘門25
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言