Pythonで画像処理の基本をマスター!NumPyとPillowで読み込み・演算・保存


 

Pythonは、その豊富なライブラリエコシステムのおかげで、画像処理の分野でも非常に強力なツールとなっています。特に、NumPyPillowを組み合わせることで、画像の読み込み、様々な演算、そして保存までを効率的に行うことができます。この記事では、これらのライブラリを活用してPythonで画像処理の基本的なワークフローを構築する方法について、具体的なサンプルコードを交えながら詳しく解説します。


 

画像データとNumPy配列

 

デジタル画像は、基本的に画素(ピクセル)の集まりで構成されており、それぞれの画素は色情報を持つ数値として表現されます。例えば、グレースケール画像は画素ごとに明るさの値(0-255など)を持ち、カラー画像はRGB(赤、緑、青)の3つのチャンネルの画素値を持っています。

NumPy配列(ndarray)は、このような数値データを効率的に扱うための最適な構造です。画像データをNumPy配列として読み込むことで、NumPyの高速な数値演算機能を活用して、画素レベルでの多様な処理が可能になります。


 

1. 画像の読み込み

 

画像をNumPy配列として読み込むには、主にPillowライブラリを使用します。Pillowは様々な画像フォーマットに対応しており、読み込んだ画像をNumPyと連携しやすい形式に変換できます。

 

サンプルコード

 

Python
 
from PIL import Image
import numpy as np

# 画像を読み込み
# 例: dummy_image.jpg がカレントディレクトリにあると仮定
# dummy_image.jpgがない場合は、下記をコメントアウトして、
# Image.new()でダミー画像を作成してください。
try:
    img_pil = Image.open('dummy_image.jpg')
except FileNotFoundError:
    # ダミー画像を生成 (赤色の500x300画像)
    img_pil = Image.new('RGB', (500, 300), color = 'red')
    img_pil.save('dummy_image.jpg') # 保存して次回以降使えるようにする
    img_pil = Image.open('dummy_image.jpg')


print(f"Pillowで読み込んだ画像情報: Mode={img_pil.mode}, Size={img_pil.size}")

# Pillow画像をNumPy配列に変換
# カラー画像の場合: (高さ, 幅, チャンネル数) の形状になる
# グレースケール画像の場合: (高さ, 幅) の形状になる
img_np = np.array(img_pil)
print(f"NumPy配列の形状: {img_np.shape}, データ型: {img_np.dtype}")

 

出力例

 

Pillowで読み込んだ画像情報: Mode=RGB, Size=(500, 300)
NumPy配列の形状: (300, 500, 3), データ型: uint8

この例では、dummy_image.jpgというカラー画像を読み込み、NumPy配列に変換しています。形状が(高さ, 幅, チャンネル数)となっていることがわかります。dtype=uint8は、各画素値が0から255の範囲の符号なし8ビット整数であることを示しています。


 

2. 画像への演算(NumPyの活用)

 

画像がNumPy配列として表現されると、NumPyの強力な数値演算機能を活用して様々な処理を行うことができます。要素ごとの演算、スライシング、ブロードキャストなど、NumPyの全ての機能が画像処理に適用可能です。

 

1. 明るさ調整

 

全ての画素値に定数を加算・減算することで、画像の明るさを調整できます。画素値は通常0-255の範囲に収める必要があるため、np.clip()で範囲外の値をクリッピングします。

 

サンプルコード

 

Python
 
# 明るさを調整 (例: 全ピクセル値に50を加算)
# np.clipで0-255の範囲に収める
bright_img_np = np.clip(img_np + 50, 0, 255).astype(np.uint8)
print("明るさ調整後の配列形状:", bright_img_np.shape)

 

2. グレースケール変換

 

カラー画像をグレースケールに変換するには、通常、R, G, B各チャンネルの値を特定の比率で重み付けして合計します。

 

サンプルコード

 

Python
 
# RGB画像をグレースケールに変換 (シンプルな平均法)
# カラー画像(高さ, 幅, 3)が前提
if img_np.ndim == 3 and img_np.shape[2] == 3:
    gray_img_np = (img_np[:,:,0] * 0.2989 + # 赤
                   img_np[:,:,1] * 0.5870 + # 緑
                   img_np[:,:,2] * 0.1140).astype(np.uint8) # 青
    print("グレースケール変換後の配列形状:", gray_img_np.shape)
else:
    print("既にグレースケールまたは非RGB画像です。")

 

3. 画像の反転(左右反転)

 

NumPyのスライシング機能を使えば、画像を簡単に反転させることができます。

 

サンプルコード

 

Python
 
# 画像を左右反転
flipped_img_np = img_np[:, ::-1, :]
print("左右反転後の配列形状:", flipped_img_np.shape)

 

3. 画像の保存

 

処理済みのNumPy配列を画像ファイルとして保存するには、再びPillowライブラリのImage.fromarray()を使ってNumPy配列をPillowのImageオブジェクトに変換し、save()メソッドで保存します。

 

サンプルコード

 

Python
 
# 明るさ調整後のNumPy配列をPillow画像に変換
bright_img_pil = Image.fromarray(bright_img_np)

# 画像をファイルに保存
bright_img_pil.save('bright_image.jpg')
print("明るさ調整後の画像を 'bright_image.jpg' として保存しました。")

# グレースケール変換後のNumPy配列をPillow画像に変換
# グレースケール画像はMode='L' (Luminance)で保存
if 'gray_img_np' in locals(): # gray_img_npが作成されているか確認
    gray_img_pil = Image.fromarray(gray_img_np, mode='L')
    gray_img_pil.save('grayscale_image.jpg')
    print("グレースケール画像を 'grayscale_image.jpg' として保存しました。")

# 左右反転後のNumPy配列をPillow画像に変換
flipped_img_pil = Image.fromarray(flipped_img_np)
flipped_img_pil.save('flipped_image.jpg')
print("左右反転画像を 'flipped_image.jpg' として保存しました。")

 

まとめ

 

Pythonでの画像処理は、Pillowによる簡単なファイル入出力とNumPyによる高速な画素演算の組み合わせが基本となります。

  • 画像の読み込み: PIL.Image.open()で読み込み、np.array()でNumPy配列に変換。

  • 画像への演算: NumPy配列に対して直接、加算、減算、スライシングなどの数学的・論理的演算を適用。

  • 画像の保存: 処理済みのNumPy配列をPIL.Image.fromarray()でPillow画像に戻し、save()メソッドで保存。

この基本的なワークフローを理解することで、さらに複雑な画像処理や機械学習のための前処理にもスムーズに進むことができます。ぜひ、あなたのPythonプロジェクトで画像処理に挑戦してみてください!📸