画像データ分析チートシート





# 本稿の概要
画像データ分析を行う際、最初に実行・確認すべきコードをまとめたので、皆も利用可能なように記載する。

尚、今回はPythonを用いて実装し、実行例はNishikaのトレーニングコンペ『[[Training] 日本絵画に描かれた人物の顔分類に機械学習で挑戦！](https://competition.nishika.com/competitions/kaokore_pra/summary)』のデータを表示する。

尚、必要な箇所をコピーして自身の環境に合わせて改変していただきたい。
本稿のコードをそのまま実行する場合は事前準備を実施してほしい。

[Githubにコードあり（リンク）](https://github.com/nonono-copen/image_cheatsheet/tree/main)

# 事前準備
実行環境はGoogle Drive（データ格納先）、Google Colabratoryを利用する。

### データダウンロードと格納、Colabでのマウント
まず『[[Training] 日本絵画に描かれた人物の顔分類に機械学習で挑戦！](https://competition.nishika.com/competitions/kaokore_pra/summary)』からデータをダウンロードし、自分のGoogle Driveへ下記の通りに格納する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1990652/181eef12-7688-6fb7-97d9-31d15ef18b6e.png)

その後、Google Colabratoryにてコードを実行し、マウントする。
```python:python
from google.colab import drive
drive.mount('/content/drive')
%cd /content/drive/ 'My Drive'/training_日本絵画顔分類
```

画像データがzipファイルで提供されているため、展開するコードを実行する。
```python:python
# zipファイルの解凍（すでに解凍済みの場合はスルー）
import os
if not os.path.exists('./data/train_images/'):
    import zipfile
    file_path = './data/train.zip'
    if os.path.exists(file_path):
        with zipfile.ZipFile(file_path) as existing_zip:
            existing_zip.extractall('./data/train_images')
    else:
        print(f"The file {file_path} does not exist.")
        file_path = './data/train.zip'
```

### ライブラリのインポート
下記コード群を実行するための必要なライブラリを一挙に記載する。

最初に、Colaboratry内に無いライブラリをインストールする。
```Shell:Shell
# colaboratryにないライブラリをインストール
!pip install japanize-matplotlib
```

ライブラリのインポートを実施
```python:python
# ライブラリインポート
import pandas               as pd  # ある意味の主役
import matplotlib.pyplot    as plt # グラフ表示
import japanize_matplotlib         # 日本語表示用
japanize_matplotlib.japanize()

import torch
from   torchvision             import datasets
from   torchvision.transforms  import transforms
from   torchvision.datasets    import ImageFolder
from   torch.utils.data        import Dataset, DataLoader, random_split
from   PIL                     import Image
```

これである程度の事前準備は完了である。

# 画像データ分析で実施すべきコード
ラベルデータの読み込み、欠損値、統計量、偏りを確認する。
また画像データも同様にデータ例の表示方法を記載する。

### ラベルデータファイルの読み込み
今回利用するデータはCSVデータで提供されているため、パスを指定して読み込む。

```python:python
# CSVファイルの読み込み
csvFilePath  = './data/train.csv'
data         = pd.read_csv(csvFilePath)
# 最初の5行の表示
print(data.head())
```
結果
>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1990652/ca165fa6-ebf7-6dc0-8547-6a84419f53d7.png)


今回のデータは0～7までのラベルが存在するが、そのままではわかりづらいため日本語を付与する。

```python:python
# わかりやすいようにラベル（数字）とラベル名（日本語）の対応表を辞書を作る
# https://competition.nishika.com/competitions/kaokore_pra/dataから'gender_status'のラベル名を確認
LABELDICT = {
    0:'0:男-貴族',
    1:'1:男-武士',
    2:'2:男-化身',
    3:'3:男-庶民',
    4:'4:女-貴族',
    5:'5:女-武士',
    6:'6:女-化身',
    7:'7:女-庶民'
  }
  
# ラベル名を付与
csvDataFrame['labelname'] = csvDataFrame['gender_status'].apply(lambda x: LABELDICT[x])

# 読み込んだcsvのimageパスを解凍先設定
csvDataFrame['imagePath'] = csvDataFrame['image'].apply(lambda x: './data/train_images/train/' + x)
csvDataFrame.head()
```
結果
>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1990652/795e0a93-14e0-e5cc-02ae-d045e9cb5cc5.png)

### 読み込みデータの基本情報を表示
```python:python
# カラム名一覧の表示
print(data.columns)

# 各カラムのデータ型一覧を表示
print(data.dtypes)

# 行数の表示
print(len(data))

# データの統計量を確認
data.describe()
```
結果
> Index(['image', 'gender_status', 'labelname', 'imagePath'], dtype='object')
image            object
gender_status     int64
labelname        object
imagePath        object
dtype: object
4238
>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1990652/76f3548b-5565-10bc-2c51-f905cf904c90.png)

今回のデータでは統計量（data.describe()）はあまり必要なさそう...

### 欠損値を確認
```python:python
# 欠損値を確認
data.isnull().any() # Falseなら欠損値なし
```
結果
>image            False
gender_status    False
labelname        False
imagePath        False
dtype: bool

今回のデータでは欠損値はなさそうである。

### グラフ化してラベルの偏りを可視化
```python:python
# ラベル量を棒グラフ化
# data['グラフ化したいカラム名'].value_counts().plot.bar()

#今回の場合
data['labelname'].value_counts().plot.bar()
```
結果
>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1990652/54d42239-9e26-b169-3a13-892e2e22def9.png)

ラベル数が多い順に表示されており、今回の例ならばラベル数の偏りがあることや、5のラベルは存在しないことが分かる。

```python:python
# 円グラフ(カテゴリ割合) sizeとlabelsはデータによって変更ください
# sizes     = data['グラフ化したいカラム名'].value_counts(sort=True)
# labels    = data['グラフ化したいカラム名'].value_counts(sort=True).index.tolist()

# 今回の例
sizes     = data['labelname'].value_counts(sort=True)
labels    = data['labelname'].value_counts(sort=True).index.tolist()
fig1, ax1 = plt.subplots()
ax1.pie(sizes, labels=labels, autopct='%1.1f%%', startangle=90, shadow=False, counterclock=False)
ax1.axis('equal')
plt.show()
```
結果
>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1990652/3c2cd1b7-805e-ebb3-6540-15c29f0ef9b9.png)

比率にするとわかりやすい。

## 画像の読み込み
OpenCVやPILで読み込んでも良いが、この後に行う学習等ですぐに利用できるようにクラスを作成して読み込みを行う。

### 画像を読み込むクラスを作成
```python:python
# csvからデータセットを読み込むためのクラス定義
class LoadDataset(Dataset):
    def __init__(self, dataframe, transform=None):
        self.dataframe = dataframe
        self.transform = transform
        
    def __len__(self):
        return len(self.dataframe)
        
    def __getitem__(self, idx):
      imgfile   = self.dataframe.loc[idx, 'image']
      imgpath   = self.dataframe.loc[idx, 'imagePath']
      label     = self.dataframe.loc[idx, 'gender_status']
      labelname = self.dataframe.loc[idx, 'labelname']
      # 画像の読み込み
      img = Image.open(imgpath).convert('RGB')

      if self.transform:
          img = self.transform(img)
      return img, label, imgfile, labelname
```

テストとして画像を数枚表示してみる。
```python:python
# ロードテスト用の画像の前処理
loadtest_transform = transforms.Compose([
    transforms.Resize((224, 224)),  # 画像サイズをモデルに合わせて変更
    transforms.ToTensor(),
])

# ロードテスト用のデータセットの作成
loadtest_dataset    = LoadDataset(dataframe=csvDataFrame, transform=loadtest_transform)
loadtest_batch_size = 4

# ロードテスト用のデータローダーの作成
loadtest_dataloader = DataLoader(loadtest_dataset, batch_size=loadtest_batch_size, shuffle=True)

# 画像をgridで表示（今回は2x2のgrid（計4枚）表示する）
fig, axs = plt.subplots(2, 2, figsize=(10, 10))
for i, (inputs, labels, imgPaths, labelnames) in enumerate(loadtest_dataloader):
    for j in range(4):
        img       = inputs[j].permute(1, 2, 0)  # チャンネルを最後に移動
        label     = labels[j].item()
        imgPath   = imgPaths[j]
        labelname = labelnames[j]
        axs[i * 2 + j // 2, j % 2].imshow(img)
        axs[i * 2 + j // 2, j % 2].set_title(f"Label:({labelname}), imgName:{imgPath}")
        axs[i * 2 + j // 2, j % 2].axis('off')
    break
plt.show()

```
結果
尚、下記の結果は実行毎に異なるため、注意すること。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1990652/87966b25-f8ec-f94b-36c7-afefe06624bc.png)

# まとめ
今回はラベルデータ、画像データに対し、最初に確認すべきチートシートをまとめた。

データ分析コンペや実業務では、この後にデータクレンジング等が必要になるが、今回はその前段の処理・可視化を実施した。

また、実際に上記をTrainingコンペ内で利用した例もあるため、別記事にて共有することとする。




日本絵Trainingコンペの記事：まだ書いてない