こんにちは、タナカです。
この記事では、画像のサイズを統一する方法を説明します。
畳み込みニューラルネットワーク(CNN)では一般的に入力画像のサイズを統一する必要があります。
入力画像のサイズとは画像の縦、横のピクセル数のことです。
ここに、様々な数字が書かれた画像が472枚あります。
これらの画像は縦、横のサイズがばらばらとなっており、このままではCNNの入力として使うことができません。
異なる画像のイメージはこのようになります。
今回は、縦、横のサイズが異なる画像のサイズを統一する方法を解説します。
- CNNで入力する際の画像サイズを統一する方法がわかる
1. 画像サイズを確認する
まず初めに、画像のサイズの分布を確認します。
import cv2
import numpy as np
import matplotlib.pyplot as plt
import glob
paths = glob.glob(r'C:\Users\user\Pictures\numbers\*.jpg')
widths = []
heights = []
for path in paths:
img = cv2.imread(path)
h, w = img.shape[:2]
widths.append(w)
heights.append(h)
# plt.hist(widths, bins = 10)
plt.hist(heights, bins = 10)
plt.title('heights')
plt.show()
ヒストグラムで縦、横のサイズの分布確認するとバラバラになっていることがわかります。
2. 画像サイズを統一する方法
画像サイズを統一する方法はいくつかあります。今回紹介する方法以外にもあると思います。
- リサイズで変更する
- paddingで周囲を埋める
1つひとつ解説していきます。
リサイズで変更する
これは一番シンプルな方法になります。opnecvやpillowというライブラリを使えば、簡単に画像をリサイズすることができます。
例えば、今回のケースで平均の画像サイズにリサイズしてみたいと思います。
# 平均画像サイズ
resize_h = int(np.mean(heights))
resize_w = int(np.mean(widths))
# すべての画像をリサイズ
for path in paths:
img = cv2.imread(path)
img = cv2.resize(img, dsize = (resize_h, resize_w))
この結果のとおり、すべての画像のサイズが30×26にリサイズされていることがわかります。
ただしリサイズの場合は、アスペクト比を完全に無視することになるので、注意が必要です。
paddingで埋める
次は、paddingで埋める方法になります。paddingとは画像の上下左右(前後)を埋める処理になります。
機械学習の畳み込み処理で使われたりしますね。
paddingの処理はこのようになります。
h_max = max(heights)
w_max = max(widths)
for path in paths:
img = cv2.imread(path)
h, w = img.shape[:2]
if h < h_max:
# 偶数の場合
if (h_max - h) % 2 == 0:
img_pad = np.pad(img, ((int((h_max - h) / 2), int((h_max - h) / 2)), (0, 0), (0, 0)), mode = 'constant', constant_values = (255, 255))
if w < w_max:
if (w_max - w) % 2 == 0:
img_pad = np.pad(img_pad, ((0, 0), (int((w_max - w) / 2), int((w_max - w) / 2)), (0, 0)), mode = 'constant', constant_values = (255, 255))
else:
img_pad = np.pad(img_pad, ((0, 0), (int((w_max - w) / 2) + 1, int((w_max - w) / 2)), (0, 0)), mode = 'constant', constant_values = (255, 255))
# 奇数の場合
else:
img_pad = np.pad(img, ((int((h_max - h) / 2) + 1, int((h_max - h) / 2)), (0, 0), (0, 0)), mode = 'constant', constant_values = (255, 255))
if w < w_max:
if (w_max - w) % 2 == 0:
img_pad = np.pad(img_pad, ((0, 0), (int((w_max - w) / 2), int((w_max - w) / 2)), (0, 0)), mode = 'constant', constant_values = (255, 255))
else:
img_pad = np.pad(img_pad, ((0, 0), (int((w_max - w) / 2) + 1, int((w_max - w) / 2)), (0, 0)), mode = 'constant', constant_values = (255, 255))
あらかじめ、472枚の画像の最大高さと、最大幅を取得しておいて、そのサイズに統一する処理をしています。
今回の場合、最大高さと最大幅はそれぞれ、58、37になるので、そのサイズになるように255で周囲を埋めています。
h_maxとh、もしくはw_maxとwの差分が埋めるべきピクセル数になります。
また、その差分が奇数か偶数かで処理を変更するようにしています。なぜならピクセルは小数点で表すことができないからです。
下記のようなコードでpaddingを実施しています。
img_pad = np.pad(img, ((int((h_max - h) / 2), int((h_max - h) / 2)), (0, 0), (0, 0)), mode = 'constant', constant_values = (255, 255))
3つの座標((int((h_max – h) / 2), int((h_max – h) / 2)), (0, 0), (0, 0))を記載していますが、今回の画像が3次元のためです。グレースケールのように見えますが、1ピクセルにRGBそれぞれの値を保持しています。
これがグレースケールになった場合は、((int((h_max – h) / 2), int((h_max – h) / 2)), (0, 0))のようになります。
最後に処理結果の例を1つ示すとこのようになります。
今回の場合は、背景が白(255)なのでpaddingで埋めても違和感なく画像のサイズを統一することができています。
しかし、通常の画像を白(255)や黒(0)で埋めると学習結果に影響が出る場合があります。タスクに合わせた画像サイズの統一を図る必要があると思います。
まとめ
CNNに入力する画像のサイズを統一する方法を紹介しました。
リサイズにしろ、paddingにしろ、タスクに合わせた方法で画像サイズを変更することが必要になります。