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

Python

はじめに

画像処理は、デジタル画像を操作してその内容を解析、変換、編集する技術です。この技術は、写真編集、医療画像解析、自動運転車の視覚システムなど、さまざまな分野で利用されています。このシリーズは、実際の画像処理技術をPythonで実装することで、理論と実践を組み合わせた学習を目指しています。画像処理において、ノイズ除去は非常に重要なステップです。ノイズの多い画像では、後続の処理や解析の精度が低下してしまいます。そこで、今回はノイズ除去の代表的な手法であるガウシアンフィルタとメディアンフィルタについて学びます。これらのフィルタの基本的な原理から実装方法までを丁寧に解説し、実際のノイズ除去の効果を確認していきます。この記事では、画像処理100本ノックで用意されている「imori_noise.jpg」をダウンロードしてください。ダウンロードのURLはこちらです。

画像処理100本ノック 009. ガウシアンフィルタ

ガウシアンフィルタ(3×3、標準偏差1.3)を実装し、imori_noise.jpgのノイズを除去せよ。

画像処理100本ノック

方針

ガウシアンフィルタは、画像の各ピクセルに対してその周辺ピクセルの加重平均を計算し、ノイズを低減するためのフィルタです。重みはガウス分布に従って決定され、中心に近いピクセルほど重みが大きくなります。ガウス分布の数式は以下の通りです:

ここで、σは標準偏差を表し、フィルタの広がり具合を調整します。ガウシアンフィルタを適用するためには、以下の手順を踏みます:

  1. ガウシアンカーネルを生成する。
  2. 画像にゼロパディングを施し、画像の境界部分の処理を行う。
  3. 各ピクセルに対してガウシアンカーネルを適用し、新しい値を計算する。

実装手順

  1. 画像の読み込み
    • 画像を読み込みます。
  2. ガウシアンカーネルを作成する
    • ガウシアンカーネルを計算し、カーネルの合計が1になるように正規化します。
  3. ゼロパディング
    • 画像にゼロパディングを施し、カーネルの適用範囲を確保します。
  4. ガウシアンフィルタを適用する
    • 各ピクセルに対してカーネルを適用し、フィルタリングされた画像を生成します。

解答と実行結果

gaussian_kernel関数を定義します。gaussian_kernel関数は、指定されたサイズと標準偏差を基にガウシアンカーネルを生成します。ガウス分布の数式を使用して各要素を計算し、カーネルの合計が1になるように正規化します。画像を読み込み、ゼロパディングを施します。ゼロパディングにより、画像の端のピクセルでもフィルタを適用できるようにします。フィルタを適用し、各ピクセルの新しい値を計算します。これにより、ノイズが除去されたスムーズな画像が得られます。

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

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

# ガウシアンカーネルの作成
def gaussian_kernel(size, sigma):
    kernel = np.fromfunction(
        lambda x, y: (1/ (2*np.pi*sigma**2)) * np.exp(- ((x - (size - 1) / 2) ** 2 + (y - (size - 1) / 2) ** 2) / (2 * sigma ** 2)), (size, size)
    )
    return kernel / np.sum(kernel)

kernel = gaussian_kernel(3, 1.3)

# ゼロパディング
pad_size = kernel.shape[0] // 2

# ガウシアンフィルタの適用
def apply_gaussian_filter(img, kernel):
    padded_img = np.pad(img, ((pad_size, pad_size), (pad_size, pad_size), (0, 0)), mode='constant')
    output = np.zeros_like(img)
    for c in range(3):  # 各チャンネルに対してフィルタを適用
        for i in range(img.shape[0]):
            for j in range(img.shape[1]):
                output[i, j, c] = np.sum(kernel * padded_img[i:i + kernel.shape[0], j:j + kernel.shape[1], c])
    return output

output = apply_gaussian_filter(img, kernel)

# 結果の表示
plt.imshow(cv2.cvtColor(output, cv2.COLOR_BGR2RGB))
plt.title('Gaussian Filter Output')
plt.show()

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

画像処理100本ノック 010. メディアンフィルタ

メディアンフィルタ(3×3)を実装し、imori_noise.jpgのノイズを除去せよ。メディアンフィルタとは画像の平滑化を行うフィルタの一種である。これは注目画素の3×3の領域内の、メディアン値(中央値)を出力するフィルタである。 これもゼロパディングせよ。

画像処理100本ノック

方針

メディアンフィルタは、画像の各ピクセルに対してその周辺ピクセルの中央値を計算し、ノイズを低減するフィルタです。メディアン値を使用することで、極端な値(ノイズ)の影響を排除し、画像を平滑化します。メディアンフィルタを適用するためには、以下の手順を踏みます:

  1. 画像を読み込む。
  2. 画像にゼロパディングを施し、画像の境界部分の処理を行う。
  3. 各ピクセルに対して3×3の領域の中央値を計算し、新しい値を設定する。

実装手順

  1. 画像の読み込み
    • 画像を読み込みます。
  2. ゼロパディング
    • 画像にゼロパディングを施し、フィルタの適用範囲を確保します。
  3. メディアンフィルタを適用する
    • 各チャンネル(R, G, B)に対して3×3の領域の中央値を計算し、フィルタリングされた画像を生成します。

解答と実行結果

median_filter関数は、各チャンネルに対してゼロパディングを施し、3×3の領域の中央値を計算します。これにより、ノイズが除去された平滑なカラー画像が得られます。

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

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

# メディアンフィルタの適用
def median_filter(img, kernel_size):
    pad_size = kernel_size // 2
    output = np.zeros_like(img)
    
    for c in range(3):  # 各チャンネルに対してフィルタを適用
        padded_img = np.pad(img[:, :, c], ((pad_size, pad_size), (pad_size, pad_size)), mode='constant')
        for i in range(img.shape[0]):
            for j in range(img.shape[1]):
                output[i, j, c] = np.median(padded_img[i:i + kernel_size, j:j + kernel_size])
    
    return output

# 3x3のメディアンフィルタを適用
output_median = median_filter(img, 3)

# 結果の表示
plt.imshow(cv2.cvtColor(output_median, cv2.COLOR_BGR2RGB))
plt.title('Median Filter Output')
plt.show()

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

最後に

今回は、ガウシアンフィルタとメディアンフィルタを用いてカラー画像のノイズ除去を行う方法を学びました。ガウシアンフィルタは加重平均を計算することで画像を平滑化し、メディアンフィルタは極端な値の影響を排除することでノイズを低減します。それぞれのフィルタには特有の強みと用途があります。この記事を通じて、これらのフィルタの基礎を理解し、実装できるようになってください。

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

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

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

コメント

タイトルとURLをコピーしました