背景
最近在做一個和對象識别相關的項目,由于團隊内技術棧偏向
JavaScript
,在已經用
Python
和
Tensorflow
搭建好了對象識别伺服器後,為了不再增加團隊成員維護成本,是以盡可能将訓練和識别之外的任務交給
Node.js
來做,今天要講到的圖檔預處理就是其中之一。
這裡對還不了解深度學習的人就幾個概念做個簡單的解釋。
對象識别:對象識别可了解為計算機在一張圖檔中發現某個或某些指定的物體,比如找到裡面所有的狗。 訓練:計算機學會對象識别這個本領就像人類學會說話一樣,需要不斷地練習,深度學習中管這個過程叫做 “訓練”。 訓練集:人類學會說話需要看别人怎麼說,聽别人的聲音等等,這些能夠讓自己學會說話的資訊在深度學習中稱為訓練集,隻不過對象識别中需要的訓練集隻有圖檔。
做圖檔預處理的目的是為了解決對象識别中訓練集不足的問題。當對象識别應用于某個專用領域的時候,就會遇到這個問題。如果你是識别一隻狗,這樣的圖檔一大把,而且有人已經訓練好了,并且可以提供服務給大家使用了。如果你是識别團隊内的文化衫,這樣的圖檔就太少了,費了老半天勁拍 100 張,這樣的資料量依然少得可憐。要知道網上那些成熟的 AI 服務,訓練集随随便便就成千上萬,甚至以億為機關。當然,專用領域一般需求也比較簡單,需要識别出來的東西種類不多,特征也比較明顯,但是仍然會希望訓練集越大越好,這時候就可以對所擁有的圖檔做一些處理,來生成新的圖檔,進而擴充目前的訓練集,這個過程就叫圖檔預處理了。
常見的圖檔預處理方式有以下幾種:
- 旋轉。由于旋轉的角度可以是任意值,是以需要随機生成一些角度來旋轉,這又稱為随機旋轉。
- 翻轉。相當于在圖檔旁邊放面鏡子,新圖檔就是鏡子内的圖檔,一般有水準翻轉和豎直翻轉兩種。
- 調節亮度。調節過手機的亮度就能體會這個意思。
- 調節飽和度。調節過傳統電視就能體會到這個意思,飽和度越高,色彩顯示越鮮豔,反之給人一種冷色的感覺。
- 調節色相。這個相當于給整個圖檔變顔色一樣,想象一下以前調出來的綠色電視。
- 調節對比度。這個會讓圖檔亮的地方更亮,暗的地方更暗。也可以想象一下電視上的對比度調節,不得不說電視機啟蒙了這些專業名詞。
上述每項操作都需要視場景而選擇,目前适用于我們團隊的處理方式主要也就是上面這些。還有一些白化、Gamma 處理等操作,由于不是那麼直覺,有興趣的人可以自己去了解。
AI-Image-Creator
為了實作上述預處理,我特地開發了一款工具 —— AI Image Creator。AI Image Creator 是一款可以将 1 張圖檔一鍵生成 40+ 張衍生圖檔的工具,它也可以深度周遊一個檔案夾,将裡面的所有圖檔都産生 40+ 張衍生圖檔,進而達到對一個圖庫擴充 40+ 倍的效果。
安裝
安裝依賴
AI Image Creator 依賴于 GraphicsMagick ,是以首先需要下載下傳和安裝 GraphicsMagick。如果是 macOS 系統,可以輕松地用 Homebrew 安裝:
brew install graphicsmagick
複制
Windows 系統可以前往 官網下載下傳頁面 下載下傳安裝包,如果下載下傳連結打不開可以前往 SourceForge 下載下傳。 Linux 系統一般可以使用包管理器安裝。
全局安裝
AI Image Creator 可以通過 npm 全局安裝:
npm i ai-image-creator -g
複制
本地安裝
AI Image Creator 也可以通過 npm 安裝在項目内:
npm i ai-image-creator -S
複制
使用
指令行
AI Image Creator 可以用過指令行的方式調用。如果是安裝在全局,則直接在終端運作
aiimg [options]
即可。如果是安裝在項目内,則在項目根目錄下通過
npx aiimg [options]
運作。
其中,
options
支援以下幾種參數:
-
,-i --input <path>
為源圖檔路徑或者包含源圖檔的目錄路徑,預設是目前目錄。path
-
,-o --output <path>
為生成的圖檔目錄路徑,預設是目前目錄下的path
檔案夾。output
-
,--output <paths>
為周遊時忽略的檔案夾或檔案名,多個檔案或檔案夾名用paths
分隔,暫時不支援 Glob 比對。,
例如,下面的指令就是将
images
目錄下除去
dog
和
cat
兩個目錄的所有圖檔都生成出衍生圖檔至
outputs
檔案夾:
aiimg -i images/ -o outputs --ignore images/dog,images/cat
複制
NodeAPI
AI Image Creator 可以作為
Node
子產品被調用:
import { start } from 'ai-image-creator'
/**
* @param {string} input 源圖檔路徑或者包含源圖檔的目錄路徑,預設是目前目錄
* @param {string} output 生成的圖檔目錄路徑,預設是目前目錄下的 output 檔案夾
* @param {object} options 參數配置
* @param {string|string[]} options.ignore 周遊時忽略的檔案夾或檔案名,暫時不支援 Glob 比對
* @return {Promise} 生成完畢後會傳回一個 Promise
*/
start(input, output, options);
複制
原理和效果
為了直覺,我選了一張圖檔作為預處理對象:
示例圖檔
另外,在本文的示例代碼中,每種預處理方法的函數名都是參照
Tensorflow
中
Image
子產品的同名方法而定,更多處理圖檔的方法可以前往 Tensorflow 文檔官網自行檢視,同時去 gm 官方文檔 中尋找相同作用的方法。
翻轉
沿 Y 軸翻轉用到了
gm
的
.flip
方法:
import gm from 'gm';
/**
* 沿 Y 軸翻轉,即上下颠倒
* @param inputPath 輸入的圖像檔案路徑
* @param outputPath 輸出的圖像檔案路徑
* @param callback 處理後的回調函數
*/
function flip(inputPath, outputPath, callback) {
gm(inputPath)
.flip()
.write(outputPath, callback);
}
複制
翻轉後的效果如下圖所示:
Flip
沿 X 軸翻轉用到了
gm
的
.flop
方法:
import gm from 'gm';
/**
* 沿 X 軸翻轉,即上下颠倒
* @param inputPath 輸入的圖像檔案路徑
* @param outputPath 輸出的圖像檔案路徑
* @param callback 處理後的回調函數
*/
function flop(inputPath, outputPath, callback) {
gm(inputPath)
.flop()
.write(outputPath, callback);
}
複制
翻轉後的效果如下圖所示:
Flop
你還可以把
.flip
和
.flop
組合起來使用,形成對角線翻轉的效果:
Flip and flop
如果把原圖看成一個前端元件,即一個購物按鈕組,裡面每個按鈕的背景可以自定義,按鈕裡面由文字、分隔線、文字三種元素組成,那麼上面翻轉後的圖檔是可以看成同一個元件的,即可以拿來作為訓練集。
有時候,翻轉帶來的效果并不是自己想要的,可能翻轉後,和原來的圖檔就不應該視作同一個東西了,這時候這種方法就有局限性了。
調整亮度
相比之後,調整亮度就顯得更加普适了,無論是什麼圖檔,調整亮度後,裡面的東西依然還是原來的那個東西。
調整亮度用到了
gm
的
.modulate
方法:
/**
* 調整亮度
* @param inputPath 輸入的圖像檔案路徑
* @param outputPath 輸出的圖像檔案路徑
* @param brightness 圖像亮度的值,基準值是 100,比 100 高則是增加亮度,比 100 低則是減少亮度
* @param callback 處理後的回調函數
*/
function adjustBrightness(inputPath, outputPath, brightness, callback) {
gm(inputPath)
.modulate(brightness, 100, 100)
.write(outputPath, callback);
}
複制
.modulate
方法是一個多功能的方法,可以同時調整圖檔的亮度、飽和度和色相三種特性,這三種特性分别對應着該方法的三個參數,這裡隻調整亮度,是以隻改變第一個參數(比 100 高則是增加亮度,比 100 低則是減少亮度),其他保持 100 基準值不變。
我把亮度從 0 - 200 的圖檔都生成了出來,并進行了對比,選出了一個亮度處理較為合适的區間。可以看看 0 - 200 之間相鄰亮度相差為 10 的圖檔之間的差别(提示:每張圖檔的左上角辨別出了該圖檔的亮度):
亮度
可以看到亮度為 60 以下的圖檔,都太暗了,細節不夠明顯,亮度為 150 以上的圖檔,都太亮了,也是細節不夠明顯。而經過多張圖檔綜合對比之後,我認為 60, 140 這個區間的圖檔品質比較好,與原圖相比不會丢失太多細節。
再來看看亮度為 50 和 60 的兩張圖檔,其實看起來像是一張圖檔一樣,不符合訓練集多樣性的原則,更何況是相鄰亮度相差為 1 的兩張圖檔。是以最終決定作為訓練集的相鄰兩張圖檔亮度差為 20,這樣差異就比較明顯,比如亮度為 80 和亮度為 100 的兩張圖檔。
最終,調節亮度産生的新圖檔将會是 4 張。從亮度為 60 的圖檔開始,每增加 20 亮度就選出來加入訓練集,直到亮度為 140 的圖檔,其中亮度為 100 的圖檔不算。
調節飽和度
調節飽和度也是用
.modulate
方法,隻不過是調節第二個參數:
/**
* 調整飽和度
* @param inputPath 輸入的圖像檔案路徑
* @param outputPath 輸出的圖像檔案路徑
* @param saturation 圖像飽和度的值,基準值是 100,比 100 高則是增加飽和度,比 100 低則是減少飽和度
* @param callback 處理後的回調函數
*/
function adjustSaturation(inputPath, outputPath, saturation, callback) {
gm(inputPath)
.modulate(100, saturation, 100)
.write(outputPath, callback);
}
複制
同樣按調節亮度的方法來确定飽和度的範圍以及訓練集中相鄰兩張圖檔的飽和度相差多少。可以看看相鄰飽和度相差為 10 的圖檔之間的差别(提示:每張圖檔的左上角辨別出了該圖檔的飽和度):
飽和度
調節飽和度的産生的圖檔細節沒有丢,大多都能夠用作訓練集中的圖檔,與亮度一樣,飽和度相差 20 的兩張圖檔差異性明顯。另外,飽和度大于 140 的時候,圖檔改變就不明顯了。是以調節飽和度産生的新圖檔将會是 6 張。從飽和度為 0 的圖檔開始,每增加 20 飽和度就選出來加入訓練集,直到飽和度為 140 的圖檔,其中飽和度為 100 的圖檔不算。
調節色相
調節色相的方法在此場景下是最有用的方法,産生的訓練集最多,率先來看下色相相鄰為 10 的圖檔之間的差距吧(提示:每張圖檔的左上角辨別出了該圖檔的色相):
色相
幾乎每個圖檔都能作為新的訓練集,由于色相調節範圍隻能在 0 - 200 之間,是以從色相為 0 的圖檔開始,每增加 10 色相就選出來加入訓練集,直到色相為 190 的圖檔,其中色相為 100 的圖檔不算。 這樣就能夠産生 20 張圖檔作為訓練集。
至于調節色相的代碼則和亮度、飽和度一樣,隻是改變了第三個參數:
/**
* 調整色相
* @param inputPath 輸入的圖像檔案路徑
* @param outputPath 輸出的圖像檔案路徑
* @param hue 圖像色相的值,基準值是 100,比 100 高則是增加色相,比 100 低則是減少色相
* @param callback 處理後的回調函數
*/
function adjustHue(inputPath, outputPath, hue, callback) {
gm(inputPath)
.modulate(100, 100, hue)
.write(outputPath, callback);
}
複制
調節色相并不是萬能的,隻是适用于這個場景,當然,我們團隊的需求都是類似這個場景的。但是,如果你要訓練識别梨的人工智能,告訴它有個藍色的梨顯然是不合适的。
調節對比度
調整對比度用到了
gm
的
.contrast
方法:
/**
* 調整對比度
* @param inputPath 輸入的圖像檔案路徑
* @param outputPath 輸出的圖像檔案路徑
* @param multiplier 調節對比度的因子,預設是 0,可以為負值,n 表示增加 n 次對比度,-n 表示降低 n 次對比度
* @param callback 處理後的回調函數
*/
function adjustContrast(inputPath, outputPath, multiplier, callback) {
gm(inputPath)
.contrast(multiplier)
.write(outputPath, callback);
}
複制
下面是對比度因子從 -10 到 10 之間的圖像,可以看到圖檔品質較好的區間是 -5, 2,其他都會丢失一些細節。另外相鄰對比度因子的圖檔之間的差異也比較明顯,是以每張圖檔都可作為訓練集,這樣又多出 7 張圖檔。
對比度
綜合效果展示
綜合效果展示
總結
通過上述 5 種方法,可以在一張圖檔的基礎上額外獲得 40 張圖檔,即訓練集是原來的 40 倍。這還是在沒有多種方法混合使用的情況下,如果混合使用,恐怕幾百倍都不止。
gm
還支援對圖檔進行其他處理方式,你可以自己去發掘,每種方式在特定場景下都有自己的局限性,需要你去甄選。希望大家都有一個自己滿意的訓練集。