画像分類する際によく行う画像カサマシ方法

Pocket

画像分類を行うサービスや、転移学習(finetuning)を行って独自の画像分類モデルを作成してたりすると、画像をwebスクレイピングして、集めたりする作業がどうしても大変と感じます。

苦労して集めた画像だけでは、

  • 画像に不要な物体がありすぎ
  • そもそも画像枚数がたりない

等が問題になり、精度が思うように上がらない場合も多々あります。

この場合は、近道なし!っとあきらめて、もっとWebスクレイピングする、必要な物体のみにトリミングするしかないと思ってます。ただし、画像枚数不足に関しては、ある程度の画像カサマシを自動で行う事が出来ます。

私自身色々な記事を参考にして、最終的に使っている画像カサマシ方法を記事にしました。

前準備

環境

実行環境は、Colaboratory を使用します。
Colaboratory は、完全にクラウドで実行される Jupyter ノートブック環境で、設定不要で、無料でご利用になれます。
今回は、Googleドライブにある画像に画像処理を行って、Googleドライブの指定フォルダに画像処理した画像を保存するpythonプログラムを作成します。

保存先フォルダと元画像配置

Googleドライブに以下のフォルダ構成を作成し、data/分類名フォルダ配下に、カサマシ元となる画像を保存しておきます。outputフォルダは出力先として前もって作成しておきます。(dog/catは例)

drive/
 ├ data/
 │  ├ dog/
  │  ├ cat/
 └ output/
    ├ dog/
    ├ cat/

Colaboratory を使うときのお約束

本コードをColaboratory で実行すると、GoogleドライブのTOPディレクトリをdrive/として
マウントします。

実行途中で、Googleアカウントにログインして、トークンを入力してください等メッセージが表示されるので従ってください。

以降、drive/data/cat/〇〇.jpgでGoogleドライブのファイルへアクセスが可能になります。

# google-drive-ocamlfuseのインストール
# https://github.com/astrada/google-drive-ocamlfuse
!apt-get install -y -qq software-properties-common python-software-properties module-init-tools
!add-apt-repository -y ppa:alessandro-strada/ppa 2>&1 > /dev/null
!apt-get update -qq 2>&1 > /dev/null
!apt-get -y install -qq google-drive-ocamlfuse fuse

# Colab用のAuth token作成
from google.colab import auth
auth.authenticate_user()

# Drive FUSE library用のcredential生成
from oauth2client.client import GoogleCredentials
creds = GoogleCredentials.get_application_default()
import getpass
!google-drive-ocamlfuse -headless -id={creds.client_id} -secret={creds.client_secret} < /dev/null 2>&1 | grep URL
vcode = getpass.getpass()
!echo {vcode} | google-drive-ocamlfuse -headless -id={creds.client_id} -secret={creds.client_secret}

# drive/ を作り、そこにGoogle Driveをマウントする
!mkdir -p drive
!google-drive-ocamlfuse drive

画像カサマシ

↓元の画像は、七夕で子供の写真を撮るために作成した飾りですw
IMG_1773.jpg

import等

# 必要なライブラリimport
import os
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import pandas as pd
from keras.preprocessing.image import ImageDataGenerator
from keras.preprocessing import image
import shutil
import cv2
import glob

# カサマシ画像元パス一覧
files = glob.glob('drive/data/*/*.*')
# 保存先
save_target = "drive/output"

元の画像のコントラストを強め・弱めの処理

min_table = 50
max_table = 205
diff_table = max_table - min_table

LUT_HC = np.arange(256, dtype = 'uint8' )
LUT_LC = np.arange(256, dtype = 'uint8' )

for i in range(0, min_table):
    LUT_HC[i] = 0
for i in range(min_table, max_table):
    LUT_HC[i] = 255 * (i - min_table) / diff_table
for i in range(max_table, 255):
    LUT_HC[i] = 255

for i in range(256):
    LUT_LC[i] = min_table + i * (diff_table) / 255
    
for row in files:
    src_file = row
    src_filename = src_file.split("/")[-1]
    src_filename_prefix = src_filename.split(".")[0]
    src_dir = src_file.split("/")[-2]
    dst_dir_name = "{0}/{1}/{2}".format(save_target,src_dir,src_filename)
    dst_dir = "{0}/{1}/".format(save_target,src_dir)
    
    # 変換
    src = cv2.imread(src_file, 1)
    high_cont_img = cv2.LUT(src, LUT_HC)
    low_cont_img = cv2.LUT(src, LUT_LC)
    
    if not os.path.exists(dst_dir):
        os.makedirs(dst_dir)
    
    dst_file = "{0}/{1}/{2}-h.jpg".format(save_target,src_dir,src_filename_prefix)
    cv2.imwrite(dst_file, high_cont_img)
    dst_file = "{0}/{1}/{2}-l.jpg".format(save_target,src_dir,src_filename_prefix)
    cv2.imwrite(dst_file, low_cont_img)
    
    #元ファイルをコピー
    shutil.copy(src_file,dst_dir_name)

IMG_1773-h.jpg

元の画像にガンマ補正処理

gamma1 = 0.75
gamma2 = 1.5
LUT_G1 = np.arange(256, dtype='uint8')
LUT_G2 = np.arange(256, dtype='uint8')
for i in range(256):
    LUT_G1[i] = 255 * pow(float(i) / 255, 1.0 / gamma1)
    LUT_G2[i] = 255 * pow(float(i) / 255, 1.0 / gamma2)

for row in files:
    src_file = row
    src_filename = src_file.split("/")[-1]
    src_filename_prefix = src_filename.split(".")[0]
    src_dir = src_file.split("/")[-2]
    dst_dir_name = "{0}/{1}/{2}".format(save_target,src_dir,src_filename)
    dst_dir = "{0}/{1}/".format(save_target,src_dir)
    
    # 変換
    src = cv2.imread(src_file, 1)
    high_cont_img = cv2.LUT(src, LUT_G1)
    low_cont_img = cv2.LUT(src, LUT_G2)
    
    if not os.path.exists(dst_dir):
        os.makedirs(dst_dir)
    
    dst_file = "{0}/{1}/{2}-g1.jpg".format(save_target,src_dir,src_filename_prefix)
    cv2.imwrite(dst_file, high_cont_img)
    dst_file = "{0}/{1}/{2}-g2.jpg".format(save_target,src_dir,src_filename_prefix)
    cv2.imwrite(dst_file, low_cont_img)
    
    #元ファイルをコピー
    shutil.copy(src_file,dst_dir_name)

IMG_1773-g1.jpg

元の画像にぼかし処理

for row in files:
    src_file = row
    src_filename = src_file.split("/")[-1]
    src_filename_prefix = src_filename.split(".")[0]
    src_dir = src_file.split("/")[-2]
    dst_dir_name = "{0}/{1}/{2}".format(save_target,src_dir,src_filename)
    dst_dir = "{0}/{1}/".format(save_target,src_dir)
    
    # 変換
    average_square = (10,10)
    src = cv2.imread(src_file, 1)
    blur_img = cv2.blur(src, average_square)
    
    if not os.path.exists(dst_dir):
        os.makedirs(dst_dir)
    
    dst_file = "{0}/{1}/{2}-b.jpg".format(save_target,src_dir,src_filename_prefix)
    cv2.imwrite(dst_file, blur_img)
    
    #元ファイルをコピー
    shutil.copy(src_file,dst_dir_name)

IMG_1773-b.jpg

元の画像にノイズ処理

for row in files:
    src_file = row
    src_filename = src_file.split("/")[-1]
    src_filename_prefix = src_filename.split(".")[0]
    src_dir = src_file.split("/")[-2]
    dst_dir_name = "{0}/{1}/{2}".format(save_target,src_dir,src_filename)
    dst_dir = "{0}/{1}/".format(save_target,src_dir)
    
    # 変換
    src = cv2.imread(src_file, 1)
    row,col,ch= src.shape
    mean = 0
    sigma = 15
    gauss = np.random.normal(mean,sigma,(row,col,ch))
    gauss = gauss.reshape(row,col,ch)
    gauss_img = src + gauss
    
    if not os.path.exists(dst_dir):
        os.makedirs(dst_dir)
    
    dst_file = "{0}/{1}/{2}-gn.jpg".format(save_target,src_dir,src_filename_prefix)
    cv2.imwrite(dst_file, gauss_img)
    
    #元ファイルをコピー
    shutil.copy(src_file,dst_dir_name)

IMG_1773-gn.jpg

元の画像にノイズ処理(つぶ大き目)

for row in files:
    src_file = row
    src_filename = src_file.split("/")[-1]
    src_filename_prefix = src_filename.split(".")[0]
    src_dir = src_file.split("/")[-2]
    dst_dir_name = "{0}/{1}/{2}".format(save_target,src_dir,src_filename)
    dst_dir = "{0}/{1}/".format(save_target,src_dir)
    
    # 変換
    src = cv2.imread(src_file, 1)
    row,col,ch = src.shape
    s_vs_p = 0.5
    amount = 0.004
    sp_img = src.copy()

    num_salt = np.ceil(amount * src.size * s_vs_p)
    coords = [np.random.randint(0, i-1 , int(num_salt)) for i in src.shape]
    sp_img[coords[:-1]] = (255,255,255)

    num_pepper = np.ceil(amount* src.size * (1. - s_vs_p))
    coords = [np.random.randint(0, i-1 , int(num_pepper)) for i in src.shape]
    sp_img[coords[:-1]] = (0,0,0)
    
    if not os.path.exists(dst_dir):
        os.makedirs(dst_dir)
    
    dst_file = "{0}/{1}/{2}-sp.jpg".format(save_target,src_dir,src_filename_prefix)
    cv2.imwrite(dst_file, sp_img)
    
    #元ファイルをコピー
    shutil.copy(src_file,dst_dir_name)

IMG_1773-sp.jpg

元の画像にRGB入れ替え処理

for row in files:
    src_file = row
    src_filename = src_file.split("/")[-1]
    src_filename_prefix = src_filename.split(".")[0]
    src_dir = src_file.split("/")[-2]
    dst_dir_name = "{0}/{1}/{2}".format(save_target,src_dir,src_filename)
    dst_dir = "{0}/{1}/".format(save_target,src_dir)
    
    # 変換
    src = cv2.imread(src_file, 1)
    img_bgr = cv2.split(src)
    # B->G, G->R, R->B
    img_cng = cv2.merge((img_bgr[1],img_bgr[2],img_bgr[0]))
    img_cng1 = cv2.merge((img_bgr[2],img_bgr[1],img_bgr[0]))

    if not os.path.exists(dst_dir):
        os.makedirs(dst_dir)

    dst_file = "{0}/{1}/{2}-grb.jpg".format(save_target,src_dir,src_filename_prefix)
    cv2.imwrite(dst_file, img_cng)
    dst_file = "{0}/{1}/{2}-rbg.jpg".format(save_target,src_dir,src_filename_prefix)
    cv2.imwrite(dst_file, img_cng1)

IMG_1773-grb.jpg

回転・拡大・縮小は学習段階で、ライブラリ等で自動でやってくれるので省略。

フォルダ構成をちゃんと準備して、上から順に実行すれば勝手に画像カサマシするようにコードを書いたので、楽をしたい方は是非コピペして使ってみてください。

Pocket

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です