最近KerasからPyTorchに全面移行しようとしているのですが、自作データセットを扱う場合、skimageやcv2経由で得られるnumpy.array
では扱いづらいようでしたのでPILを使っていくことにしました。
ただ、必要な情報のみが書かれているPILの記事が容易には見つけられなかったため、書くことにしました。
どんな人に向けているか
- 最近KerasからPyTorchに乗りかえた(または乗り換えを検討している)
- 落ちているものではなく自作のデータセットを使いたい
- もっぱら
numpy.array
で慣れていてPIL
の扱いに慣れていない
tl;dr
PILは便利だけど覚えるのが面倒くさいなら↓で変換できるので使うといいと思うよ
from skimage.io import imread
from PIL import Image
img = Image.fromarray(imread('./img-path.jpg'))
目標
CustomDataset.py
from torch.utils.data import DataLoader
from torchvision import transforms
transform = transforms.Compose([
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
])
class CustomDataset(torch.utils.data.Dataset):
def __init__(self, img_paths, labels, transform=None):
self.transform = transform
self.img_paths = img_paths # 画像パスのリスト
self.labels = labels
def __getitem__(self, index):
img_path = self.img_paths[index]
label = self.labels[index]
######### ここから #########
with open(img_path, 'rb') as f:
image = Image.open(f)
image = image.convert('RGB')
######### ここまでの話 #########
if self.transform:
image = self.transform(image)
return image, label
def __len__(self):
return len(self.images)
custom_dataset = CustomDataset(img_paths, labels, transform)
custom_loader = torch.utils.data.DataLoader(dataset=custom_dataset, ~)
のPILの部分を簡潔に理解できるようにすること。
なお、回転などはTransform内で処理をすればよいため触れていません。
PILの実装パターン
まずは以下のモジュールをインポートしておきます。
これ以下の章ではインポートされている前提で進めます。
今回使用した画像は、 幅が125, 高さが150pixelのRGB画像 です。
from skimage.io import imread # 比較のため少しだけ使うのでインポートしておく
from PIL import Image
# jpgファイルを読み込んでいますが、PNG, PPM, BMP, TIFF, GIFなども対応している
img = Image.open('./img-path.jpg')
基本
画像の種類を見る
- 画像のサイズは(幅x高さ)
- つまり
skimage.io.imread
の挙動と逆
- つまり
- RGB画像でもチャンネル数は表示されない
print(f'img.format: {img.format}') # img.format: JPEG
print(f'img.size : {img.size}') # img.size : (125, 150)
print(f'img.mode : {img.mode}') # img.mode : RGB
# 個別のアクセスもできます
print(img.width) #=> 125
print(img.height) #=> 150
グレースケールとRGBの変換
img.convert('1') # 2値化
img.convert('L') # グレースケール化
# グレースケール化してからRGB化。グレースケールの時と同じ画像が出てくる。
# ただし、 .modeはRGBとなっている。
img.convert('L').convert('RGB').mode #=> RGB
コピーと値の変更
- コピーの方法、挙動は同じ
-
PIL.Image
はインデックスでアクセスできないことに留意する
skimage-copy.py
# skimageを使った場合
img = imread('./img-path.jpg')
tmp = img.copy()
tmp[0][0][0] = 0
print(f'img[0][0][0]: {img[0][0][0]}') # 232
print(f'tmp[0][0][0]: {tmp[0][0][0]}') # 0
PIL-Image-copy.py
# PIL.Imageを使った場合
img = Image.open('./img-path.jpg')
tmp = img.copy()
tmp.putpixel((0, 0), (255, 255, 255))
print(tmp.getpixel((0, 0))) # (255, 255, 255)
print(img.getpixel((0, 0))) # (232, 220, 196)
そのほか
- 色の範囲 ...
img.getextrema()
- 画像の保存 ...
img.save(path)
- リサイズ ...
img.resize((width, height))
- 表示 ... jupyter使ってるなら
img
でエンターすると出ると思います。.show
メソッドはあるけど別窓で出るので不要な場合が多いと思います - 画像の融合 ...
image.blend
np.arrayと違うところのTIPS
上にも書いてますが、注意点として書いておきます。
- 形状をみるには、
.shape
ではなく.size
を使う- sizeはチャンネル数は表示されない
-
.mode
を使って画像の種類(RGB, グレースケールなど)を確認する必要有
- PILは
img[0]
みたいにインデックスでアクセスできない。-
img.getpixel((100, 100))
のようにする
-