使用 Node.js 在深度學習中作圖片預處理

背景

最近在作一個和對象識別相關的項目,因爲團隊內技術棧偏向 JavaScript,在已經用 PythonTensorflow 搭建好了對象識別服務器後,爲了避免再增長團隊成員維護成本,因此儘量將訓練和識別以外的任務交給 Node.js 來作,今天要講到的圖片預處理就是其中之一。html

這裏對還不瞭解深度學習的人就幾個概念作個簡單的解釋。前端

  1. 對象識別:對象識別可理解爲計算機在一張圖片中發現某個或某些指定的物體,好比找到裏面全部的狗。
  2. 訓練:計算機學會對象識別這個本領就像人類學會說話同樣,須要不斷地練習,深度學習中管這個過程叫作 「訓練」。
  3. 訓練集:人類學會說話須要看別人怎麼說,聽別人的聲音等等,這些可以讓本身學會說話的信息在深度學習中稱爲訓練集,只不過對象識別中須要的訓練集只有圖片。

作圖片預處理的目的是爲了解決對象識別中訓練集不足的問題。當對象識別應用於某個專用領域的時候,就會遇到這個問題。若是你是識別一隻狗,這樣的圖片一大把,並且有人已經訓練好了,而且能夠提供服務給你們使用了。若是你是識別團隊內的文化衫,這樣的圖片就太少了,費了老半天勁拍 100 張,這樣的數據量依然少得可憐。要知道網上那些成熟的 AI 服務,訓練集隨隨便便就成千上萬,甚至以億爲單位。固然,專用領域通常需求也比較簡單,須要識別出來的東西種類很少,特徵也比較明顯,可是仍然會但願訓練集越大越好,這時候就能夠對所擁有的圖片作一些處理,來生成新的圖片,從而擴充當前的訓練集,這個過程就叫圖片預處理了。python

常見的圖片預處理方式有如下幾種:git

  1. 旋轉。因爲旋轉的角度能夠是任意值,因此須要隨機生成一些角度來旋轉,這又稱爲隨機旋轉。
  2. 翻轉。至關於在圖片旁邊放面鏡子,新圖片就是鏡子內的圖片,通常有水平翻轉和豎直翻轉兩種。
  3. 調節亮度。調節過手機的亮度就能體會這個意思。
  4. 調節飽和度。調節過傳統電視就能體會到這個意思,飽和度越高,色彩顯示越鮮豔,反之給人一種冷色的感受。
  5. 調節色相。這個至關於給整個圖片變顏色同樣,想象一下之前調出來的綠色電視。
  6. 調節對比度。這個會讓圖片亮的地方更亮,暗的地方更暗。也能夠想象一下電視上的對比度調節,不得不說電視機啓蒙了這些專業名詞。

上述每項操做都須要視場景而選擇,目前適用於咱們團隊的處理方式主要也就是上面這些。還有一些白化、Gamma 處理等操做,因爲不是那麼直觀,有興趣的人能夠本身去了解。github

安裝 gm

gm 是一個圖片處理的 npm 庫,性能在 Node.js 庫中應該算佼佼者了,它底層默認使用的是 GraphicsMagick,因此你須要先安裝 GraphicsMagick,在 Mac 系統中直接用 Homebrew 安裝:npm

brew install graphicsmagick
複製代碼

其餘系統的安裝方式能夠直接前往官網查看。api

若是你須要在圖片上添加文字,還須要安裝 ghostscript,在 Mac 上能夠用 brew install ghostscript 安裝。因爲本文沒涉及到這一個功能,因此能夠不用安裝。服務器

同時,須要將 gm 安裝在你的項目下:微信

npm i gm -S
複製代碼

預處理

爲了直觀,我選了一張圖片做爲預處理對象:函數

示例圖片

另外,在本文的示例代碼中,每種預處理方法的函數名都是參照 TensorflowImage 模塊的同名方法而定,更多處理圖片的方法能夠前往 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);
}
複製代碼

翻轉後的效果以下圖所示:

Flip

你還能夠把 .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 還支持對圖片進行其餘處理方式,你能夠本身去發掘,每種方式在特定場景下都有本身的侷限性,須要你去甄選。但願你們都有一個本身滿意的訓練集。

宣傳

歡迎你們 Star 筆者的 Github,另外,也歡迎你們關注筆者的公衆號,獲取最新文章的推送:

微信公衆號二維碼
相關文章
相關標籤/搜索