前言:這並不是那個現在幾乎每天都有新話題的AI根據關鍵字自動繪圖。
一來那種研究已經有很多人做了,我手上的資源也不可能把它做好。
二來我對AI無法通靈的問題也不是很滿意。我還是寧願主動給予較明確的資訊。
我的目標是給予一張低解析度的黑白草稿圖片(非黑即白,沒有中間灰階值),
輸出一張較高解析度,且與特定主題相符的彩色圖片。
目前的版本中,草稿圖片的解析度為64x64,遠低於一般描繪草稿的尺寸。
輸出彩色圖片的解析度則為256x256,老實說這有點太小了。
但受限於手上的資源與花費時間,目前只能達到這個尺寸。
至於「特定主題」的限制,則與這個問題本身的難度有關。
即使是給予「二次元人物大頭照」這麼明確的設定,也有各種不同的配色。
無論AI再怎麼厲害,也絕不可能在無額外資訊的情況下通靈出你想要的配色。
當然已經存在許多額外輸入顏色的應用範例,但終究還是要提供額外資訊。
一種只需要給草稿,不需要額外資訊的應用方法,就是請AI畫某個特定角色。
而這種吃力不討好的研究,當然就要選自己最喜歡的角色了:
https://dic.pixiv.net/a/%E6%98%9F%E7%86%8A%E5%8B%87%E5%84%80
示範影片:
https://www.youtube.com/watch?v=_3o_YFQoWDo
在目前的成果中,可以看到AI確實認得髮色、瞳色、以及那根明顯的角。
而在許多可見的問題中,最嚴重的就是那有如核廢料般的背景。
這其實是可想而知的結果,在下面的細節中會特別解釋。
而它其實也有解決方法,但目前的版本還無法處理,應該會是下一個研究主題。
訓練出這麼一個只能畫特定角色的模型有什麼用?老實說,沒什麼用。
現在都2202年了,一個只能畫一顆頭的玩具最多就只是個玩具。
雖然還是好像有點東西出來,但筆觸方面仍舊不太自然,顏色變化也單調。
與處理訓練資料所花的時間相比(準備草稿非常耗時,後續細節部分有說明),
這個種程度的結果相當相當成比例,直接去練繪圖似乎還比較好。
所以我才在一開始就選定這個角色,至少這樣我就絕對不會後悔。
以下是一些與人工智慧研究直接相關的細節,沒興趣的讀者們可以忽略。
訓練資料
訓練AI需要收集大量的資料,而我總共收集了2400張圖片。
雖然這個角色的人氣很低,不過在Pixiv上也有接近兩萬張圖片。
(其他繪圖網站當然也有不少,但還是Pixiv數量最多。)
由於主題是臉部特寫,所以我需要排除以下的圖片:
1. 有一隻眼睛完全被擋住的側臉(這會產生過大的變化度,使AI訓練困難)
2. 一隻或兩隻眼睛閉上(比1.簡單一點,但還是有影響,先排除)
3. 臉部被太過誇張的裝飾或前景遮擋
4. 臉部被角色自己的CP貼上來遮擋
5. 畫風過於特殊,與一般的二次元繪圖相去太遠
6. 上車圖(我不希望未來哪一天時在展示資料時突然就手滑了)
7. 主推繪師所產出的大量圖片(資料重複性太高對訓練不妥,我只會從中挑幾張)
過濾完後的可用圖片只剩下2400張(當然每一兩天會有新的,不過緩不濟急)。
以AI繪圖的主題來說,這個量其實根本不夠,不過更多的我也生不出來。
要標示原始圖片的臉部區域,並獲得旋轉與裁切後的內容並不困難。
如果你要玩的是GAN或Diffusion Model,那你已經可以拿去用了。
不過我的主題是給予對應的草稿,將這些圖片做為輸出的標準答案。
由於AI無法通靈出沒有給的資訊,例如圖片環境的明暗,以及作者習慣的配色。
因此我以臉部附近的顏色分布為參考,將所有圖片的明暗與顏色調整至一致。
(實作上我只是分別調整RGB三色頻道的平均值與標準差而已。)
接下來的重點是準備這些圖片的輸入草稿,而這就是另一個惡夢的開始。
草稿輸入
我將每張圖片的臉部旋轉至水平方向,以產生2400張256x256的臉部特寫。
對於每一張圖片,我需要給予一張對應的64x64草稿,像這樣:
https://i.imgur.com/qQEvoHP.png
右邊是256x256的圖片,左邊是64x64的草稿放大成256x256的尺寸。
很明顯地,草稿絕對不可能表達出所有的完整的細節,一定要做大幅度的簡化。
當然如果是256x256的草稿就可以描繪更多細節,但這與我一開始的目標不符。
雖然我願意給AI輸入資料,但還是希望它能用非常精簡的內容產生複雜結果。
怎樣的內容該用怎樣的草稿代表?這完全沒有標準答案。
我只能用自己的一套標準,並盡量從頭到尾保持一致。
例如,背景的內容與角色本身無關,所以無論細節為何都保持空白。
角色臉部以下自然延伸的脖子、肩膀部分會提供對應的草稿。
但如果是舉起的手腳或不相干的背景,則只標示出邊界,以外則保持空白。
雖然草稿只有64x64,但如果是逐個像素慢慢點,點個幾張就要出人命了。
所以我用了一個簡單的邊緣偵測演算法來產生一個作為起始的自動草稿:
https://i.imgur.com/JAsJMNK.png
當然這種自動草稿的可靠性還是很差,每一張圖都有許多地方需要手動修改。
而來源不同的圖片有各種不同的畫風,有時候會出現差異明顯較大者:
https://i.imgur.com/rYM7MDd.png
這種畫風與線稿上色明顯不同,在訓練時有引發混亂的風險。
然而,在訓練資料缺乏的狀況下,我還是有使用這類型的資料。
(當然更極端的案例,例如水墨畫風格,則還是捨棄不用。)
另一個比較嚴重的問題是位於圖片邊界的臉部:
https://i.imgur.com/EAJ8mnH.png
如果想像頭型畫出完整的草稿,則AI會看到正常的草稿以及缺乏部分內容的圖片。
(圖片外的區域我以隨機顏色填補,所以才會出現亮綠色,這似乎不是好做法)
所以我只把草稿畫在有效區域內,外部區域以及圖片邊界上都不標示,
然後祈禱AI至少能在有效區域內學到有用的東西(AI訓練師常有的信仰)。
在現實上,這個邊界問題,以及圖片中不固定的背景內容,
就是造成了範例影片中背景顏色有如核廢料一般的原因。
一種可能的解決辦法是在標示草稿時,也額外標示一個背景與無效區域。
然後在訓練時要求AI把該區域填成白色,這樣至少可以得到一致的結果。
更進一步的策略是,在AI訓練小有成果後,
把這些有邊界問題的圖片之草稿畫成完整的樣子,然後請AI重新畫一張。
這樣或許可以在本人還沒有原作者繪圖功力的前提下補出完整的頭型。
不過這個做法目前還在想像階段,無法保證能得到堪用的結果。
Augmentation(資料增補)
用圖片進行AI訓練時不可能只使用固定內容的一份訓練資料,
必須對其進行小幅隨機變化,使AI能看到較有多樣性的內容。
最簡單的做法就是圖片的縮放與旋轉,很遺憾地,這非常不適用於本主題。
一張256x256彩色圖片的對應輸入資料為64x64黑白草稿圖。
每一個草稿的黑色像素都對應到圖片中4x4的區域。
如果將圖片進行例如1.10倍的放大,或5度的旋轉,
則草稿也要作相同的轉換,使得絕大部分的像素移動到非整數座標的位置上。
這時候有兩種策略:
(1) 用Bilinear Interpolation讓草稿變成有灰階漸層的內容
由於本應用希望輸入的是非黑即白的草稿(如範例影片),這樣作並不適合。
(2) 用Nearest Neighbor Interpolation讓草稿保持非黑即白狀態
可想而知草稿會變得破破爛爛,結果應該很不堪用
不過從後面提到的其他資料增補方法看來,或許可以考慮這樣做。
總之,目前的資料增補方式排除了圖片的縮放與旋轉。
當然也不能就這樣什麼都不做,至少平移還是可以做的。
草稿圖每移動一像素,原始圖片就要移動四個像素,否則會有同樣的問題。
因此我在裁剪臉部圖片時,將256x256的範圍往上下左右各放大八格。
所產生的272x272圖片就可以供草稿圖片上下左右隨機平移最多兩格。
下圖為一範例,上半部是原本位置,下半部隨機平移:
https://i.imgur.com/nO2x2iR.png
草稿圖片平移產生的空隙目前就先留白,但其實有另一種更好的處理方法。
如果在標示圖片時,就已經根據272x272的範圍標好68x68的草稿,
那麼只要是在限定的範圍內隨機平移,都可以得到完整的64x64草稿。
不過我在做到一半時才想到這點,所以目前只有用留白的簡單方法。
另外,對於草稿本身也需要做一點隨機變化。
當你使用一隻滑鼠當畫筆,假裝這種東西叫做繪圖板時,
畫出的線段一定會破破爛爛,或是在曲線上有不自然的轉折。
收集資料時產生的草稿圖片都經過仔細修補,實際上不可能一口氣畫成。
所以在訓練時,必須在草稿圖片上隨機增加像素或移除像素。
以模擬實際畫草稿時斷斷續續的結果,或是曲線上如同階梯一般不自然的連接。
同時還加上另一種更激進的做法,擦除草稿內隨機數量與位置的小塊區域。
下圖為一範例,上半部是原本的完整草稿,下半部是擾動後的結果:
https://i.imgur.com/YJk0SNb.png
擾動後的殘破草稿,在訓練時仍然對應到內容完整的彩色圖片。
這當然是期望AI能在輸入尚未完成時,就產生接近完整的結果。
實際看到的結果當然沒有這麼理想,但也有大致的趨勢。
例如眼睛的草稿還沒有畫完時,會出現一團顏色偏黑帶紅(瞳色)的物體。
一般來說,對於輸出圖片的內容也需要進行位置以外的隨機調整。
常見作法有亮度(整體或三色頻道分開)隨機變化,或將整張圖片模糊/清晰化。
在本應用中,這個做法並不太合理,它會使同一張草稿對應到不同的輸出圖片。
這在訓練上除了使AI混亂外沒有好處,因此我沒有採用這種做法。
最後,最簡單也不用擔心技術上問題的做法,是隨機將圖片左右翻轉。
只要將草稿圖片也跟著左右翻轉,就能輕鬆滿足對應關係。
然而這其實有與研究「主題」高度相關的限制:
以這個角色來說,沒有任何因素會阻礙左右翻轉的策略。
但如果是像凱留這種挑染固定挑在左邊的角色,翻轉後就會發生問題:
https://i.imgur.com/AFgxajC.png
要讓AI同時學會「只有一條挑染,但可能出現在左邊或右邊」其實並非難事。
但一個挑染在相反方向的角色還有靈魂嗎?這種哲學問題就會產生許多爭議了。
模型架構
輸入一張圖片,輸出一張圖片,這怎麼看都是U-net。
所以我使用的模型也跟教科書上隨處可見的U-net完全一樣,沒什麼新奇的。
唯一的小變化是原本Concatenate的地方,我用1x1 Convolution+Add取代。
因為實際測起來沒什麼差別,我就選比較省計算量的版本了。
(Concatenate下一次的Convolution複雜度會變大,比替代的做法還耗時)
至於什麼Resnet block, Bottleneck的小變化也都試過,
但是並沒有在訓練/測試速度都已經變得更慢的情況下得到更好的結果。
前面提供的範例圖片中,輸入的草稿圖片都是白底黑線。
但在圖片資料處理中,黑色代表的數字是0.0,白色則是1.0。
白底黑線的圖片中絕大多數都是非零的值,有筆畫的地方才是0。
黑白顛倒後的黑底白線圖片則絕大多數都是0,有筆畫的地方才有非零的值。
後者觀感上明顯比較合理(數學上我不敢肯定),
所以草稿圖片會進行黑白顛倒,以黑底白線的內容做為模型的輸入。
令我比較好奇的是,一般即使是輸入單頻道圖片,也會有不同的灰階值。
而這個應用的輸入格式比較特殊,只有0.0和1.0兩種數值。
比起一般U-net接上的3x3 Convolution,是否需要一些特殊的前端Layer,
才能讓模型更有效率地從這個內容相對比較單調的輸入抽取資訊。
不過這邊能搞怪的地方我也稍微玩過,目前還沒發現什麼值得一提的做法。