プロジェクト — 画像処理

1. プロジェクト:NumPy を使った画像処理

(1) シナリオ

コンピュータビジョンのパイプラインを開発しています。画像をピクセル単位で処理する必要があります。つまり、グレースケールへの変換、フィルタの適用、統計量の算出などを行う必要がありますが、これらすべてをNumPyの配列演算を使って行います。

(2) タスク

1. 合成画像を作成する

PYTHON
import numpy as np

# Create a 100x100 RGB image
rng = np.random.default_rng(42)
image = rng.integers(0, 256, size=(100, 100, 3), dtype=np.uint8)
print(f"Image shape: {image.shape}")  # (100, 100, 3)
print(f"Image dtype: {image.dtype}")  # uint8

2. グレースケールに変換する

PYTHON
# Grayscale: weighted average of RGB channels
# Standard weights: 0.299 R + 0.587 G + 0.114 B
gray = np.dot(image[..., :3], [0.299, 0.587, 0.114]).astype(np.uint8)
print(f"Gray shape: {gray.shape}")  # (100, 100)

3. 簡単なフィルタの適用(水平エッジ検出)

PYTHON
# Edge detection: subtract adjacent rows
edges = np.abs(gray[:-1] - gray[1:])
print(f"Edge map shape: {edges.shape}")  # (99, 100)
print(f"Edge intensity: mean={edges.mean():.1f}, max={edges.max()}")

4. カラーチャンネルの抽出

PYTHON
# Extract individual channels
red = image[:, :, 0]
green = image[:, :, 1]
blue = image[:, :, 2]

print(f"Red channel: mean={red.mean():.1f}, min={red.min()}, max={red.max()}")
print(f"Green channel: mean={green.mean():.1f}, min={green.min()}, max={green.max()}")
print(f"Blue channel: mean={blue.mean():.1f}, min={blue.min()}, max={blue.max()}")

5. 画像の統計情報

PYTHON
# Histogram (simplified: count pixels in each intensity range)
bins = np.array([0, 50, 100, 150, 200, 256])
indices = np.digitize(gray.ravel(), bins)
hist = np.bincount(indices, minlength=len(bins)+1)[1:-1]

for i in range(len(bins)-1):
    print(f"Intensity {bins[i]:3d}-{bins[i+1]:3d}: {hist[i]:4d} pixels")

(1) ▶ サンプル:合成画像の作成(難易度 ⭐)

PYTHON
import numpy as np

# Create a 50x50 RGB image with a gradient
x = np.arange(50)
y = np.arange(50)
xx, yy = np.meshgrid(x, y)

red = np.zeros((50, 50), dtype=np.uint8)
green = (xx * 5).astype(np.uint8)
blue = (yy * 5).astype(np.uint8)

image = np.stack([red, green, blue], axis=-1)
print("Shape:", image.shape)
print("Red channel:", image[:, :, 0].min(), "-", image[:, :, 0].max())
print("Green channel:", image[:, :, 1].min(), "-", image[:, :, 1].max())
print("Blue channel:", image[:, :, 2].min(), "-", image[:, :, 2].max())

出力:

TEXT
形状: (50, 50, 3)
赤チャンネル:0 - 0
グリーンチャンネル:0~245
青チャンネル:0 ~ 245

(2) ▶ サンプル:グレースケール変換(難易度 ⭐)

PYTHON
import numpy as np

# Simulate an RGB image
rng = np.random.default_rng(42)
image = rng.integers(0, 256, size=(10, 10, 3), dtype=np.uint8)

# Standard grayscale weights
gray = (0.299 * image[:, :, 0] + 0.587 * image[:, :, 1] + 0.114 * image[:, :, 2]).astype(np.uint8)
print("Original shape:", image.shape)
print("Gray shape:", gray.shape)
print("Gray pixel [0, 0]:", gray[0, 0])
print("Gray mean:", gray.mean())

出力:

TEXT
元の形状:(10, 10, 3)
灰色の図形:(10, 10)
グレーのピクセル [0, 0]: 72
グレーの平均値:129.0

(3) ▶ サンプル:簡単なエッジ検出(難易度 ⭐⭐)

PYTHON
import numpy as np

# Create a simple image with a vertical edge
image = np.zeros((10, 10), dtype=np.uint8)
image[:, 5:] = 255

# Horizontal edge detection
h_edges = np.abs(image[:-1, :] - image[1:, :])
print("Horizontal edges:\n", h_edges)

# Vertical edge detection
v_edges = np.abs(image[:, :-1] - image[:, 1:])
print("Vertical edges:\n", v_edges)

出力:

TEXT
水平方向の縁:
 [[0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0]]
垂直エッジ:
 [[  0   0   0   0 255   0   0   0   0]
 [  0   0   0   0 255   0   0   0   0]
 [  0   0   0   0 255   0   0   0   0]
 [  0   0   0   0 255   0   0   0   0]
 [  0   0   0   0 255   0   0   0   0]
 [  0   0   0   0 255   0   0   0   0]
 [  0   0   0   0 255   0   0   0   0]
 [  0   0   0   0 255   0   0   0   0]
 [  0   0   0   0 255   0   0   0   0]]


❓ よくある質問

Q なぜグレースケールに 0.299 R + 0.587 G + 0.114 B を使うのですか?
A これらの重み付けは人間の知覚に合致しています。人間は緑に最も敏感で、青には最も鈍感だからです。 単純な平均計算(R+G+B)/3ではこの点が考慮されず、色が薄っぺらな結果になってしまいます。
Q NumPyを使って実際の画像を処理することはできますか?
A はい。画像を matplotlib.pyplot.imread() または imageio.imread() で読み込むと、NumPy配列が返されます。その後、ここで示したのと同じ演算を適用してください。
Q 画像において、uint8とfloat32の違いは何ですか?
A uint8(0~255)は、保存および表示のための標準形式です。float32は、オーバーフローや丸め誤差を防ぐために、演算(フィルタ処理や変換など)に使用されます。

📖 まとめ

このプロジェクトでは、以下のことを実践しました:


📝 練習問題

  1. 初心者向け(難易度 ⭐):画像の赤チャンネルと青チャンネルを入れ替えてみてください。これにより、どのような色かぶりが生じますか?

  2. 中級(難易度 ⭐⭐):行の代わりに隣接する列を差し引くことで、垂直方向のエッジ検出器を実装してください。その結果を比較してください。

  3. 上級 (難易度 ⭐⭐⭐): 簡単なぼかしフィルターを実装してください。各ピクセルを、その周囲3×3のピクセルの平均値に置き換えます。ブロードキャストとスライシングを使用し、ループは使わないでください。

Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%