画像分類を行うサービスや、転移学習(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
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)
元の画像にガンマ補正処理
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)
元の画像にぼかし処理
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)
元の画像にノイズ処理
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)
元の画像にノイズ処理(つぶ大き目)
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)
元の画像に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)
回転・拡大・縮小は学習段階で、ライブラリ等で自動でやってくれるので省略。
フォルダ構成をちゃんと準備して、上から順に実行すれば勝手に画像カサマシするようにコードを書いたので、楽をしたい方は是非コピペして使ってみてください。