画像コンペのzipファイル内の画像を解凍せずに読み込む

データサイエンスの画像コンペでは、画像データがzip形式で圧縮されています。
画像データの数がそんなに多くなければ、zipを解凍して画像を読み込んだりもしますが、大量にある場合はzipのまま、画像データを読み込めればと思い、備忘録的に投稿します。

使うデータは**SIGNATE**の【練習問題】**画像ラベリング（10種類）**を使います。

[【練習問題】画像ラベリング（10種類）](https://signate.jp/competitions/133/data)

まずは、ライブラリをインポート

```python
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
import zipfile
import io
```

# zipファイル内の画像ファイルを確認
zipファイルのパスを指定して、中身を確認してみます。
with構文を使って**ZipFile()**でzipファイルを読み込み、**infolist()**で読み込んだzipファイル内のすべてのファイル名を取得します。

```python
# zipファイルのパス
zip_path = './data/train_images.zip'

# zipの中身を確認
with zipfile.ZipFile(zip_path, 'r') as zip_file:
    for info in zip_file.infolist():
        print(info)
```

出力

```python
(...略...)

<ZipInfo filename='train_images/train_989.jpg' compress_type=deflate external_attr=0x20 file_size=3334 compress_size=3203>
<ZipInfo filename='train_images/train_99.jpg' compress_type=deflate external_attr=0x20 file_size=2873 compress_size=2737>
<ZipInfo filename='train_images/train_990.jpg' compress_type=deflate external_attr=0x20 file_size=2743 compress_size=2606>
<ZipInfo filename='train_images/train_991.jpg' compress_type=deflate external_attr=0x20 file_size=3005 compress_size=2869>
<ZipInfo filename='train_images/train_992.jpg' compress_type=deflate external_attr=0x20 file_size=2031 compress_size=1894>
<ZipInfo filename='train_images/train_993.jpg' compress_type=deflate external_attr=0x20 file_size=3064 compress_size=2910>
<ZipInfo filename='train_images/train_994.jpg' compress_type=deflate external_attr=0x20 file_size=2858 compress_size=2723>
<ZipInfo filename='train_images/train_995.jpg' compress_type=deflate external_attr=0x20 file_size=3016 compress_size=2882>
<ZipInfo filename='train_images/train_996.jpg' compress_type=deflate external_attr=0x20 file_size=2631 compress_size=2495>
<ZipInfo filename='train_images/train_997.jpg' compress_type=deflate external_attr=0x20 file_size=2315 compress_size=2170>
<ZipInfo filename='train_images/train_998.jpg' compress_type=deflate external_attr=0x20 file_size=3012 compress_size=2877>
<ZipInfo filename='train_images/train_999.jpg' compress_type=deflate external_attr=0x20 file_size=2488 compress_size=2348>
```

# zipファイル内の画像ファイルを読み込み

ここで注意したいのは、``infolist()``をそのままループして読み込むと、ラベルデータのファイル順で読み込むことができません。

（... → train_989 → train_99 → train_990 → train_991 → ...）

必ず画像のラベルデータのファイル順に読み込みます。

というわけでラベルデータを読み込んで中身を確認します。

```python
# ラベルデータの読み込み
train_labels = pd.read_csv('./data/train_master.tsv', sep='\t')
print(train_labels.head())
```

読み込んだ画像データを格納するリストをあらかじめ作っておき、続いてwith構文を使って**ZipFile()**でzipファイルを読み込みます。

そして、ラベルデータのファイル順にループして読み込み、**zip_file.open()**に対象のファイル名を指定し、img_fileに読み込んでいきます。

対象のファイル名が``train_images/train_〇〇.jpg``となっているのをすでに確認しているため、``train_labels['file_name']``の要素（``train_〇〇.jpg``）をループして``train_images/ +i``とします。

画像のバイナリデータを**IOライブラリ**の**BytesIO()**関数で読み込み、**Pillow**の**Image.open()**で開き、Numpy配列に変換したものを格納リストに追加していきます。

```python
# zipの読み込み
with zipfile.ZipFile(zip_path, 'r') as zip_file:
    for i in train_labels['file_name']:
        with zip_file.open('train_images/'+i) as img_file:
            # 画像のバイナリデータを読み込む
            img_bin = io.BytesIO(img_file.read())
            # バイナリデータをPillowで開く
            img = Image.open(img_bin)
            # 画像データを配列化
            img_array = np.array(img)
            # 格納リストに追加
            X_train.append(img_array)
```

# 読み込んだ画像データの確認

zipファイルからちゃんと画像データを読み込めたか確認して、さらにラベル名と一致しているか確認していきます。

このデータは画像データのファイル名とラベルIDが対応していて（train_master.tsv）、さらにラベルIDとラベル名が対応（label_master.tsv）しているため、画像データとラベル名を対応させたものを表示させます。

まずはラベルマスタを読み込みます。

```python
# ラベルマスタの読み込み
label_master = pd.read_csv('./data/label_master.tsv', sep='\t')
print(label_master.head())
```

続いて、ラベルマスタからラベル名を抽出します。

```python
# ラベル名を抽出
labels = label_master['label_name']
```

以下のコードを実行すると、画像とそれが何を表すのか、40個のサンプルを表示します。

```python
# 画像の確認
plt.figure(figsize=(10,10))
for i in range(0,40):
    plt.subplot(5,8,i+1)
    plt.title(labels[train_labels.iloc[i, 1]]) # train_labelsのラベルID抽出後、IDに対応するラベル名を抽出
    plt.axis('off') # 軸と目盛りをオフ
    plt.imshow(X_train[i])
```

プログラムを実行すると以下のように表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/471633/d588025d-de61-f178-0e40-60fd380180b1.png)

ちゃんと、画像に写っているものと、ラベルが一致しているのが確認できました。

学習に進む場合は、画像を格納したリストをNumpy配列に変換すればOKです。

# 最終スクリプト

```python
'''
zipファイル内の画像ファイルを確認
'''

# ライブラリのインポート
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
import zipfile
import io

# zipファイルのパス
zip_path = './data/train_images.zip'

# zipの中身を確認
with zipfile.ZipFile(zip_path, 'r') as zip_file:
    for info in zip_file.infolist():
        print(info)

'''
zipファイル内の画像ファイルの読み込み
'''

# ラベルデータの読み込み
train_labels = pd.read_csv('./data/train_master.tsv', sep='\t')
print(train_labels.head())

# 画像データの格納リスト
X_train = [] # 画像のピクセル値とラベルを格納するリストを生成（説明変数）

# zipの読み込み
with zipfile.ZipFile(zip_path, 'r') as zip_file:
    for i in train_labels['file_name']:
        with zip_file.open('train_images/'+i) as img_file:
            # 画像のバイナリデータを読み込む
            img_bin = io.BytesIO(img_file.read())
            # バイナリデータをpillowで開く
            img = Image.open(img_bin)
            # 画像データを配列化
            img_array = np.array(img)
            # 格納リストに追加
            X_train.append(img_array)

# ラベルマスタの読み込み
label_master = pd.read_csv('./data/label_master.tsv', sep='\t')
print(label_master.head())

# ラベル名を抽出
labels = label_master['label_name']

# 画像の確認
plt.figure(figsize=(10,10))
for i in range(0,40):
    plt.subplot(5,8,i+1)
    plt.title(labels[train_labels.iloc[i, 1]]) # train_labelsのラベルID抽出後、IDに対応するラベル名を抽出
    plt.axis('off') # 軸と目盛りをオフ
    plt.imshow(X_train[i])

# np.arrayに変換
X_train = np.array(X_train)
```