😃

【Python】NumPy配列(画像データなど)にカラーフィルターをかける方法(赤、緑、青、グレー)

2024/10/05に公開

はじめに

PythonのNumPyは、数値計算や画像処理において非常に強力なライブラリです。

とくに、画像データをNumPy配列として扱うことで、さまざまな画像処理を効率的に行うことができます。

本記事では、NumPy配列(画像データなど)から特定の色のチャンネル、赤色、緑色、青色、そしてグレースケールへの変換についても触れ、NumPyの画像処理の基礎を理解するための手助けとします。

赤色のカラーフィルター

NumPy配列で表される画像データは、一般的に3次元配列で、各次元は高さ、幅、そして色チャンネル(RGB)に対応しています。

赤色チャンネルのみを抽出するには、この3次元配列の赤色チャンネルに対応するインデックスを指定すれば良いのです。

import numpy as np
import matplotlib.pyplot as plt

# サンプル画像データを生成(3次元NumPy配列)
img = np.random.randint(0, 256, size=(100, 100, 3))

# 赤色チャンネルのみ抽出
red_channel = img * [1, 0, 0]

# 可視化
plt.imshow(red_channel)
plt.title('Red Channel')
plt.show()

▼ コード解説

赤色チャンネルのみ抽出:

元の画像の各要素にベクトル [1, 0, 0] を掛け合わせることで、赤色成分のみを残し、緑色と青色の成分をゼロにするという、より直感的な操作を行います。

[1, 0, 0] というベクトルは、赤色成分に1を掛け、緑色成分と青色成分に0を掛けることを意味します。

ここではブロードキャストという仕組みが使われています。

NumPyのブロードキャスト

ブロードキャストとは?

NumPyのブロードキャストは、形状の異なる配列同士の演算を可能にする仕組みです。

異なる形状の配列に対して算術演算を行おうとした場合、NumPyは自動的に形状を調整して、要素ごとの演算が行えるようにします。

上の例では、(100, 100, 3) の形状を持つ画像データ img と、(3,) の形状を持つベクトル [1, 0, 0] を掛け合わせています。

(ここで (3,) は、1次元配列で、要素数が3であることを示しており、さらに次元が1であることを明示的に示しています。(3)も 1次元配列なので、本質的には同じです。)

・画像データ img: 高さ100画素、幅100画素、3チャンネル(RGB)の画像データです。各要素は、その画素の赤、緑、青の値を表しています。

・ベクトル [1, 0, 0]: 3つの要素からなるベクトルで、それぞれ赤、緑、青のチャンネルに対応しています。
この2つの配列は形状が異なりますが、NumPyのブロードキャストによって、以下の手順で要素ごとの掛け算が行われます。

ベクトルの拡張: [1, 0, 0] というベクトルは、画像データの各要素に対して同じ値で掛け合わせられるため、画像データと同じ形状の(100, 100, 3) の配列に拡張されます。
要素ごとの掛け算: 拡張されたベクトルと画像データの各要素が、対応する位置で掛け合わされます。

具体例

import numpy as np

# 画像データの生成 (例)
img = np.random.randint(0, 256, size=(100, 100, 3))

# ベクトル
vector = np.array([1, 0, 0])

# 掛け算
result = img * vector

# 結果の形状を確認
print(result.shape)  # (100, 100, 3)

このコードでは、vector が (100, 100, 3) に拡張され、img の各要素と掛け合わされます。

結果として、赤色チャンネルの値はそのまま残り、緑色と青色のチャンネルの値はすべて0になります。

つまり、赤色チャンネルのみが抽出された画像が得られます。

緑色のカラーフィルター

緑色チャンネルの抽出も同様です。

今度は趣向をかえて、演算子の=-のみを使った場合を考えてみます。


import numpy as np
import matplotlib.pyplot as plt

# 画像データの生成
img = np.random.randint(0, 256, size=(100, 100, 3))

green_channel = img.copy()

# 青色チャンネルを0にする
green_channel[:, :, 0] = green_channel[:, :, 0] - green_channel[:, :, 0]

# 赤色チャンネルを0にする
green_channel[:, :, 2] = green_channel[:, :, 2] - green_channel[:, :, 2]

plt.imshow(green_channel)
plt.title('Green Channel')
plt.show()

▼ コード解説

青色チャンネルを0にする:

green_channel[:, :, 0] = green_channel[:, :, 0] - green_channel[:, :, 0] で、青色チャンネルのすべての要素から自分自身を引くことで、0になります。

赤色チャンネルを0にする:

green_channel[:, :, 2] = green_channel[:, :, 2] - green_channel[:, :, 2] で、赤色チャンネルのすべての要素から自分自身を引くことで、0になります。

なぜこの方法でうまくいくのか?:

任意の数から自分自身を引くと0になるという数学的な性質を利用しています。

この方法では、他のチャンネルの値に依存せずに、単純に自分自身を引くことで、目的のチャンネル以外の値を確実に0にできます。

青色のカラーフィルター

今度は趣向をかえて、演算子の=のみを使った場合を考えてみます。

import numpy as np
import matplotlib.pyplot as plt

img = np.random.randint(0, 256, size=(100, 100, 3))

# 青色チャンネルのみを抽出(赤色と緑色を0にする)
blue_channel = img.copy()
blue_channel[:, :, 0] = 0  # 赤色チャンネル
blue_channel[:, :, 1] = 0  # 緑色チャンネル

# 結果の表示
plt.imshow(blue_channel)
plt.show()

▼ コード解説

このコードでは、imgから赤色 ([:, :, 0]) と緑色 ([:, :, 1]) を 0 にすることで、青色チャンネルのみを保持しています。

グレーのカラーフィルター

グレースケールに変換するには、一般的にRGBチャンネルの値を平均化します。

演算子の=/のみを使った場合を考えてみます。

import numpy as np
import matplotlib.pyplot as plt

img = np.random.randint(0, 256, size=(100, 100, 3))

# 各ピクセルのRGB値を合計
img_sum = img.sum(axis=2)

# 3で割って平均値を計算
img_gray = img_sum / 3

# 結果の表示
plt.imshow(img_gray, cmap='gray')
plt.show()

▼ コード解説

img.sum(axis=2):

各ピクセルのRGB値を足し合わせ、チャンネル方向(axis=2)に沿って合計します。

NumPyのsum関数において、axis=2 は、3次元配列の最後の軸(この場合は、RGBの3つの値に対応する軸)に沿って合計するという意味です。

これにより、各ピクセルに対応する一つの値(RGBの合計値)が得られます。

これは、各ピクセルの明るさの総量を求めていることになります。

img_sum / 3:

合計値を3で割ることで、各ピクセルの平均的な明るさを計算しています。

この平均値が、グレースケール画像におけるそのピクセルの輝度値となります。

ちょっと説明するのですが、RGBの値が大きいほど、その色は明るくなります。

例えば、(255, 255, 255)は白(最も明るい色)、(0, 0, 0)は黒(最も暗い色)を表します。

RGBの3つの値を単純に足し合わせることで、ある程度の明るさの目安を得ることができます。

これは、RGBの3つの要素が、人間の視覚における明るさ感にほぼ等しく寄与しているためです。

plt.imshow(img_gray, cmap='gray'):

Matplotlibを用いて、グレースケール画像を表示します。cmap='gray'を指定することで、グレースケールで表示されます。

このコードでは、RGBの各チャンネルの平均値を求め、その値を3つのチャンネルに適用してグレースケール画像を作成しています。

このコードは、RGB画像の各ピクセルのRGB値の平均値を計算し、その平均値を輝度値として新しい画像を作成することで、カラー画像をグレースケール画像に変換していま

この方法により、色の情報が失われ、明るさのみの情報で画像が表現されるようになります。

じつはこの方法は、あくまで簡便な明るさの計算方法であり、厳密な意味での物理的な明るさを表しているわけではありません。

しかし、画像処理の様々な場面で、この方法が利用されています。

他のグレースケール化の方法:

重み付け平均: RGBの各チャンネルに異なる重み付けをして平均値を計算する方法。例えば、人間の目は緑色に敏感なので、緑色のチャンネルに大きな重みを与えることがあります。

ルミナンス: 色空間の変換を用いて輝度成分を抽出する方法。

また、.convert()を使用した簡単な方法もあります。(参考サイトを参照)

参考サイト

https://blog.shikoan.com/pillow-gray-three-channels/

https://qiita.com/yoya/items/dba7c40b31f832e9bc2a

まとめ

NumPyを用いれば、画像データを数値配列として扱い、特定のチャンネルを抽出したり、グレースケールに変換したりといった画像処理を簡単に実行できます。

本記事では、赤色チャンネルの抽出を例に、NumPyを用いた画像処理の基本的な手法を解説しました。

Discussion