特定サイズ以上の画像へのパスをリストにする

# 初めに
ImageNet内の画像はサイズが一定ではないので、前処理としてresizeしてしまうのが手っ取り早いですが、画像処理タスクによってはリサイズするのが望ましくなかったりします。
データセットから特定のサイズ以上の画像だけを取り出して使うときに書いたコードを残しておきます。
単純に、サイズが大きいものだけを予めパスのリストとして作っておきます。毎回画像をロードしてチェックするのは時間がもったいないんじゃないかなということですね（実際は大差ないかもしれないですが）。
あと、いくつか実験を繰り返すときに同じデータを繰り返し使えるというメリットもあります。
私はデータサーバから画像をロードして学習を行う環境で、手元にデータセット置かない前提なので、いろいろ余裕があるなら素直にデータセットを作って保存しておく方がいいと思います。

# 前提
ディレクトリの構造は以下の形を想定しています

```
root/
　├ dataset/
　│　├ n01690466/
　│　│　├ n00445351_1.JPEG
　│　│　├ ......
　│　│　└ n00445351_29045.JPEG
　│　├　....... 
　│　├　.......
　│　├　.......
　│　│
　│　└ n00445351/
　│　 　├ n00445351.JPEG
　│　 　├ ......
　│　 　└ n01690466_20676.JPEG
　├ CreateListofImagesToLoad.py
　├ Dataset.py
　└ Train.py
```

# 本題
本題は以下です。
合計枚数を指定するとその枚数分だけdatasetからパスを取ってきます。
一つのクラスから取りすぎると学習が偏りそうなので、一つのフォルダから取ってくれる枚数に制限もかけられる親切設計です。
これをそのまま回すと256x256以上のサイズがある画像へのパスを記録したテキストファイルを作ってくれます。


```CreateListofImagesToLoad.py
import argparse
import glob
from pathlib import Path
from PIL import Image


"""
given dataset, check through all the images while storing paths of images whose image size is enough for training.
call from the same directory of training file.
"""
def parse():
    parser = argparse.ArgumentParser(description='')
    
    parser.add_argument('dataset', type=str, help='dataset path')
    parser.add_argument('save_name', type=str, help='savename for the file.')
    parser.add_argument('--img_size', default=256, type=int, help='image size needed. smaller than this size will be listed as "to exclude"')
    parser.add_argument('--suffix', default='JPEG', help='image suffix')
    parser.add_argument('--img_num_per_folder', '-pf', default=50, type=int, help='num of images per folder')
    parser.add_argument('--img_num_total', default=10000, type=int, help='max num of dataset')

    return parser

def main():
    """given path to the dataset, create a txt file of list of path to images"""
    args = parse().parse_args()
    print(args)
    dataset_path = args.dataset

    # take all the path to jpeg file from the given path
    img_type = "*." + args.suffix
    DatasetPath = Path(dataset_path)
    ImgFolderPathGenerator = DatasetPath.glob('*')

    # list
    load_img_list = []

    # load image
    cnt_all = 0
    for each_img_folder in ImgFolderPathGenerator:
        cnt = 0
        for each_img_path in Path(each_img_folder).glob(img_type):

            check_img = Image.open(each_img_path)
            width, height = check_img.size
            if width >= args.img_size and height >= args.img_size:
                load_img_list.append(str(each_img_path))
                cnt += 1
                cnt_all += 1
            if cnt_all >= args.img_num_total: break
            if cnt >= args.img_num_per_folder:
                break

    save_name = args.save_name + '.txt'
    with open(save_name, mode='w') as f:
        f.write('\n'.join(load_img_list))

    return 

if __name__ == "__main__":
    main()
```

# 使う時
これだけ残しといてもあれなので、実際にどう使っていたかです。
データセットクラスの使い方にも関連するのでこっちのが需要あるかもしれないですね

```Dataset.py

import numpy as np
from PIL import Image

import torch
from torch.utils.data import Dataset
from torchvision import transforms

class Dataset(Dataset):
    def __init__(self, data_path_txt, size):
        super(Dataset).__init__()
        with open(data_path_txt, 'r') as f:
            self.data = list(map(lambda x: x.strip('\n'), f.readlines()))
        self.size = size
        
    def __getitem__(self, index):
        img_path = self.data[index]
        img_pil = Image.open(img_path).convert('RGB')
        # crop, and  to [0,1]
        transform = transforms.Compose([
            transforms.RandomCrop(self.size),
            transforms.ToTensor()
        ])
        img_tensor = transform(img_pil)

        # do something
        processed_img_tensor = something(img_tensor)

        return processed_img_tensor, img_tensor
    
    def __len__(self):
        return len(self.data)
```

# おわりに
しばらく何も書いてなかったので半ば無理やりのアウトプットです。
共有のデータセットを使っていて元の画像ファイルをいじることはできないとか、限られた環境だと入用です。
誰かの役に立ったらうれしいです。