LoginSignup
24
25

More than 5 years have passed since last update.

PyTorch使いのためのPIL入門

Posted at

最近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))のようにする

参考

24
25
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
24
25