画像処理100本ノック完全ガイド 007:フィルタ④

Python

はじめに

画像処理は、デジタル画像を操作してその内容を解析、変換、編集する技術です。この技術は、写真編集、医療画像解析、自動運転車の視覚システムなど、さまざまな分野で利用されています。このシリーズは、実際の画像処理技術をPythonで実装することで、理論と実践を組み合わせた学習を目指しています。この記事では、エッジ検出に使用される2つの重要なフィルタ、SobelフィルタとPrewittフィルタについて解説します。これらのフィルタは、画像内の縦方向および横方向のエッジを検出するために使用されます。エッジ検出は、画像解析の基本的な技術であり、物体認識や画像分割などの前処理としてよく使用されます。専門用語を知らない方でも理解できるように、各ステップを丁寧に解説し、Pythonの関数を使わずに実装する方法を紹介します。この記事では、画像処理100本ノックで用意されている「imori_noise.jpg」をダウンロードしてください。ダウンロードのURLはこちらです。

画像処理100本ノック 015. Sobelフィルタ

Sobelフィルタ(3×3)を実装せよ。
ソーベルフィルタ(Sobelフィルタ)は特定方向(縦や横)のエッジのみを抽出するフィルタであり、次式でそれぞれ定義される。

画像処理100本ノック
    (a)縦方向       (b)横方向
       1  2  1           1  0 -1
K = [  0  0  0 ]   K = [ 2  0 -2 ]
      -1 -2 -1           1  0 -1

方針

Sobelフィルタは、画像の縦方向および横方向のエッジを強調するためのフィルタです。Sobelフィルタは、特定の方向に対するエッジの強度を計算するために使用されます。縦方向と横方向のエッジを検出するために、それぞれ以下のカーネルを使用します:

縦方向のカーネル:

横方向のカーネル:

Sobelフィルタを適用するためには、以下の手順を踏みます:

  1. 画像を読み込む。
  2. 画像にゼロパディングを施し、画像の境界部分の処理を行う。
  3. 各ピクセルに対して縦方向および横方向のSobelフィルタを適用し、エッジを強調する。

実装手順

  1. 画像の読み込み
    • 画像を読み込みます。
  2. ゼロパディング
    • 画像にゼロパディングを施し、カーネルの適用範囲を確保します。
  3. Sobelフィルタを適用する
    • 各ピクセルに対して縦方向および横方向のSobelフィルタを適用し、エッジを強調します。

解答と実行結果

まず、必要なライブラリをインポートし、ノイズのあるカラー画像を読み込みます。次に、画像の境界部分を処理するためにゼロパディングを施します。ここでは、画像の周囲に1ピクセルのゼロパディングを追加します。

次に、Sobelフィルタのカーネルを定義します。縦方向のカーネルkernel_sobel_yと横方向のカーネルkernel_sobel_xをそれぞれ定義します。Sobelフィルタを適用するための関数apply_sobel_filterを定義します。この関数は、各ピクセルに対してカーネルを適用し、エッジを強調します。具体的には、3×3の領域内のピクセル値とカーネルの積を計算し、その絶対値を新しいピクセル値として設定します。

最後に、apply_sobel_filter(padded_img, kernel_sobel_x)とapply_sobel_filter(padded_img, kernel_sobel_y)を使用してSobelフィルタを適用し、フィルタを適用した画像を表示します。

import cv2
import numpy as np
import matplotlib.pyplot as plt

# 画像の読み込み(カラー)
img = cv2.imread('/mnt/data/imori_noise.jpg')

# グレースケールに変換
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# ゼロパディング
pad_size = 1
padded_img = np.pad(gray, pad_size, mode='constant')

# Sobelフィルタのカーネル
kernel_sobel_x = np.array([[1, 0, -1], [2, 0, -2], [1, 0, -1]])
kernel_sobel_y = np.array([[1, 2, 1], [0, 0, 0], [-1, -2, -1]])

# Sobelフィルタの適用
def apply_sobel_filter(img, kernel):
    output = np.zeros_like(img)
    for i in range(img.shape[0] - 2):
        for j in range(img.shape[1] - 2):
            region = img[i:i+3, j:j+3]
            output[i, j] = np.abs(np.sum(kernel * region))
    return output

output_sobel_x = apply_sobel_filter(padded_img, kernel_sobel_x)
output_sobel_y = apply_sobel_filter(padded_img, kernel_sobel_y)

# 結果の表示
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
plt.imshow(output_sobel_x, cmap='gray')
plt.title('Sobel Filter Output (X-direction)')
plt.subplot(1, 2, 2)
plt.imshow(output_sobel_y, cmap='gray')
plt.title('Sobel Filter Output (Y-direction)')
plt.show()

このコードを実行すると、次のような結果(右の画像)が得られます。

画像処理100本ノック 016. Prewittフィルタ

Prewittフィルタ(3×3)を実装せよ。
Prewittフィルタはエッジ抽出フィルタの一種であり、次式で定義される。

画像処理100本ノック
    (a)縦方向          (b)横方向
      -1 -1 -1          -1 0 1
K = [  0  0  0 ]  K = [ -1 0 1 ]
       1  1  1          -1 0 1

方針

Prewittフィルタは、Sobelフィルタと同様に、画像の縦方向および横方向のエッジを強調するためのフィルタです。Prewittフィルタも特定の方向に対するエッジの強度を計算するために使用されます。縦方向と横方向のエッジを検出するために、それぞれ以下のカーネルを使用します:

縦方向のカーネル:

横方向のカーネル:

Prewittフィルタを適用するためには、以下の手順を踏みます:

  1. 画像を読み込む。
  2. 画像にゼロパディングを施し、画像の境界部分の処理を行う。
  3. 各ピクセルに対して縦方向および横方向のPrewittフィルタを適用し、エッジを強調する。

実装手順

  1. 画像の読み込み
    • 画像を読み込みます。
  2. ゼロパディング
    • 画像にゼロパディングを施し、カーネルの適用範囲を確保します。
  3. Prewittフィルタを適用する
    • 各ピクセルに対して縦方向および横方向のPrewittフィルタを適用し、エッジを強調します。

解答と実行結果

まず、必要なライブラリをインポートし、ノイズのあるカラー画像を読み込みます。次に、画像をグレースケールに変換します。ここでは、cv2.cvtColor関数を使用して、画像をBGRからグレースケールに変換します。

次に、画像の境界部分を処理するためにゼロパディングを施します。ここでは、画像の周囲に1ピクセルのゼロパディングを追加します。Prewittフィルタのカーネルを定義します。縦方向のカーネルkernel_prewitt_yと横方向のカーネルkernel_prewitt_xをそれぞれ定義します。Prewittフィルタを適用するための関数apply_prewitt_filterを定義します。この関数は、各ピクセルに対してカーネルを適用し、エッジを強調します。具体的には、3×3の領域内のピクセル値とカーネルの積を計算し、その絶対値を新しいピクセル値として設定します。

最後に、apply_prewitt_filter(padded_img, kernel_prewitt_x)とapply_prewitt_filter(padded_img, kernel_prewitt_y)を使用してPrewittフィルタを適用し、フィルタを適用した画像を表示します。

import cv2
import numpy as np
import matplotlib.pyplot as plt

# 画像の読み込み(カラー)
img = cv2.imread('/mnt/data/imori_noise.jpg')

# グレースケールに変換
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# ゼロパディング
pad_size = 1
padded_img = np.pad(gray, pad_size, mode='constant')

# Prewittフィルタのカーネル
kernel_prewitt_x = np.array([[-1, 0, 1], [-1, 0, 1], [-1, 0, 1]])
kernel_prewitt_y = np.array([[-1, -1, -1], [0, 0, 0], [1, 1, 1]])

# Prewittフィルタの適用
def apply_prewitt_filter(img, kernel):
    output = np.zeros_like(img)
    for i in range(img.shape[0] - 2):
        for j in range(img.shape[1] - 2):
            region = img[i:i+3, j:j+3]
            output[i, j] = np.abs(np.sum(kernel * region))
    return output

output_prewitt_x = apply_prewitt_filter(padded_img, kernel_prewitt_x)
output_prewitt_y = apply_prewitt_filter(padded_img, kernel_prewitt_y)

# 結果の表示
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
plt.imshow(output_prewitt_x, cmap='gray')
plt.title('Prewitt Filter Output (X-direction)')
plt.subplot(1, 2, 2)
plt.imshow(output_prewitt_y, cmap='gray')
plt.title('Prewitt Filter Output (Y-direction)')
plt.show()

このコードを実行すると、次のような結果(右の画像)が得られます。

最後に

この記事では、画像処理の基本的なエッジ検出技術であるSobelフィルタとPrewittフィルタの原理と実装方法について詳しく解説しました。これらのフィルタを適用することで、画像のエッジを強調し、画像内の重要な情報を抽出することができます。Pythonの関数を使わずにこれらのフィルタを実装する方法を紹介しましたが、理解を深めるために実際にコードを試してみることをお勧めします。今後も、画像処理の様々な技術について解説していきますので、ぜひ参考にしてください。

私の経歴などについては以下の記事から確認することができます!

ブログランキングに参加しています。ぜひクリックで応援お願いします

ブログランキング・にほんブログ村へ
「#Python」人気ブログランキング

コメント