カラー画像処理の Python 実現ガイド
【概要】カラー画像処理の用語(40項目)とPython実装を示す。用語は画像の基本概念、色空間と色表現、画質改善技術、ノイズ評価と除去、畳み込み演算、エッジ解析、画像変換、画像評価に分類され、対応するPython関数を明示する。実装例として、画質改善処理、ノイズ量の評価、畳み込みフィルタの適用、エッジの解析、リサイズと補間の5つのプログラムを提供する。
【目次】
【サイト内のPython関連主要ページ】
- Windows AI支援Python開発環境構築ガイド: 別ページ »で説明
- AIエディタ Windsurf の活用: 別ページ »で説明
- AIエディタCursorガイド: 別ページ »で説明
- Google Colaboratory: 別ページ »で説明
- Python(Google Colaboratoryを含む)のまとめ: 別ページ »で説明
- 機械学習の Python 実現ガイド: 別ページ »で説明
- 行列計算の Python 実現ガイド: 別ページ »で説明
- 統計分析のPython での実現ガイド: 別ページ »で説明
- 音声信号処理の Python 実現ガイド: 別ページ »で説明
- カラー画像処理の Python 実現ガイド: 別ページ »で説明
- Python 言語によるとても簡単なアドベンチャーゲーム(変数,式,if,while,関数,print,time.sleep, def, global を使用): 別ページ »で説明
- Pythonプログラミング講座:基礎から応用まで(授業資料,全15回): 別ページ »で説明
- Pythonプログラミングの例と実践ガイド: 別ページ »で説明
【外部リソース】
- Pythonの公式サイト: https://www.python.org
- 東京大学の「Pythonプログラミング入門」: https://utokyo-ipp.github.io/IPP_textbook.pdf
- ITmedia社の「Pythonチートシート」の記事: https://atmarkit.itmedia.co.jp/ait/articles/2004/20/news015.html
カラー画像処理の基本
ピクセル(画素):デジタル画像を構成する最小単位。各ピクセルは位置座標と色情報を持つ。カラー画像では通常、赤(R)、緑(G)、青(B)の3つの値で色を表現する。
解像度:画像の精細さを表す指標。幅と高さのピクセル数で表現される。解像度が高いほど詳細な情報を保持できるが、データ量も増加する。
色空間:色を数値的に表現するための座標系。RGB色空間はディスプレイ表示に適し、HSV色空間は色相・彩度・明度により人間の色認識に近い表現を提供する。
チャンネル:画像の各色成分を表す配列。RGB画像は3チャンネル、グレースケール画像は1チャンネルを持つ。各チャンネルは独立した2次元配列として処理できる。
畳み込み演算:画像処理における基本的な演算。小さなフィルタ(カーネル)を画像全体に適用し、各ピクセルとその近傍ピクセルの重み付き和を計算する。ぼかし、シャープ化、エッジ検出などに使用される。
ノイズ:画像に含まれる不要な変動成分。撮影時のセンサーノイズ、圧縮ノイズ、伝送エラーなどが原因となる。ガウシアンノイズ、ソルト&ペッパーノイズなどの種類がある。
エッジ:画像内で輝度や色が急激に変化する境界。物体の輪郭や表面の不連続点に対応する。エッジ検出は物体認識や領域分割の基礎となる。
ヒストグラム:画像内の各輝度値または色値の出現頻度を表すグラフ。画像の明るさ分布や色分布を可視化し、画質評価やコントラスト調整に利用される。
補間:画像のリサイズや回転時に、新しいピクセル位置の値を周囲のピクセルから推定する処理。最近傍補間、バイリニア補間、バイキュービック補間などの手法がある。
フィルタ:画像の特定の性質を強調または抑制する処理。平滑化フィルタはノイズを除去し、鮮鋭化フィルタは細部を強調する。
用語リスト
画像の基本概念
画像サイズ:画像の幅と高さをピクセル単位で表した値。一般的な形式として、640×480(VGA)、1920×1080(Full HD)、3840×2160(4K)などがある。画像サイズにはチャンネル数も含まれ、RGB画像は(高さ, 幅, 3)の形状を持つ。
ビット深度:各色チャンネルを表現するビット数。8ビット(0-255の256階調)が一般的である。ビット深度が大きいほど滑らかな階調表現が可能となる。
アスペクト比:画像の幅と高さの比率。16:9、4:3、1:1などが一般的である。リサイズ時にアスペクト比を維持しないと画像が歪む。
色空間と色表現
RGB色空間:赤(Red)、緑(Green)、青(Blue)の3原色の加法混色により色を表現する色空間。各成分は0-255の整数値で表現される。ディスプレイやカメラで標準的に使用される。
HSV色空間:色相(Hue)、彩度(Saturation)、明度(Value)により色を表現する色空間。OpenCVでは色相は0-180、彩度と明度は0-255で表される。色ベースの画像処理に適している。
グレースケール:輝度情報のみを持つ単色画像。RGBからの変換では人間の視覚感度を考慮した重み付けが行われる。
色相:色の種類を表す値。赤、黄、緑、青などの色の違いを角度で表現する。HSV色空間のH成分に対応する。
彩度:色の鮮やかさを表す値。彩度が高いほど鮮やかで、低いほど灰色に近い。HSV色空間のS成分に対応する。
明度:色の明るさを表す値。明度が高いほど明るく、低いほど暗い。HSV色空間のV成分に対応する。
画質改善技術
ヒストグラム均等化:画像のヒストグラムを平坦化してコントラストを改善する処理。累積分布関数を用いて輝度値を再配置する。暗い画像や明るすぎる画像の視認性向上に効果的である。
CLAHE(適応的ヒストグラム均等化):画像を小領域に分割し、各領域でヒストグラム均等化を適用する手法。局所的なコントラスト改善が可能であり、通常のヒストグラム均等化よりも自然な結果が得られる。
ガンマ補正:画像の輝度を非線形に変換する処理。変換式は Iout = Iinγ であり、γ < 1で明るく、γ > 1で暗くなる。ディスプレイの特性補正や明るさ調整に使用される。
コントラスト調整:画像の明暗差を変更する処理。線形変換 Iout = α × Iin + β により実現される。αはコントラスト係数、βは明度オフセットである。
ノイズ評価と除去
ガウシアンノイズ:正規分布に従うランダムなノイズ。撮影時のセンサーノイズの主要な成分である。平均値と標準偏差により特徴づけられる。
ソルト&ペッパーノイズ:ランダムに白点(ソルト)と黒点(ペッパー)が現れるノイズ。伝送エラーやセンサーの不良画素により発生する。
ノイズ分散:ノイズの強さを表す統計量。局所領域の輝度値の分散により推定される。値が大きいほどノイズが強い。
SNR(信号対雑音比):信号の強度とノイズの強度の比。単位はdBであり、値が大きいほど画質が良い。計算式は SNR = 10 log10 (Psignal / Pnoise) である。
ガウシアンフィルタ:ガウス分布に基づく重み付けを行う平滑化フィルタ。ノイズ除去に使用される。標準偏差パラメータでぼかしの強さを制御する。
メディアンフィルタ:近傍ピクセルの中央値を出力する非線形フィルタ。ソルト&ペッパーノイズの除去に効果的であり、エッジを保存する特性を持つ。
バイラテラルフィルタ:空間的な近さと色の類似性の両方を考慮する非線形フィルタ。エッジを保存しながら平滑化を行うため、ノイズ除去と詳細保存を両立できる。
畳み込み演算
カーネル(フィルタ):畳み込み演算で使用される小さな行列。3×3、5×5などのサイズが一般的である。カーネルの値により、平滑化、鮮鋭化、エッジ検出などの様々な処理を実現できる。
畳み込み処理:カーネルを画像全体に適用し、各位置で重み付き和を計算する処理。画像処理の基本演算であり、多くのフィルタ処理の基礎となる。
平滑化カーネル:周囲のピクセル値を平均化するカーネル。画像をぼかし、ノイズを除去する。全要素が等しい値を持つ単純な形式が一般的である。
鮮鋭化カーネル:中心ピクセルを強調し、周囲を減算するカーネル。画像のエッジや細部を強調する。ぼやけた画像の改善に使用される。
エッジ検出カーネル:輝度の勾配を計算するカーネル。Sobelカーネルなどがあり、水平方向や垂直方向のエッジを検出する。
エッジ解析
勾配:画像の輝度変化の大きさと方向を表す量。勾配が大きい場所がエッジに対応する。X方向とY方向の偏微分により計算される。
Sobelフィルタ:画像の勾配を計算してエッジを検出するフィルタ。水平方向と垂直方向の微分を近似する3×3のカーネルを使用する。計算が高速で実装が容易である。
Laplacianフィルタ:2次微分によりエッジを検出するフィルタ。全方向のエッジを一度に検出できるが、ノイズに敏感である。ゼロ交差点を検出することでエッジ位置を特定する。
Cannyエッジ検出:多段階のアルゴリズムによる高精度なエッジ検出手法。ノイズ除去、勾配計算、非極大値抑制、ヒステリシス閾値処理を順に実行する。広く使用されるエッジ検出手法である。
エッジ強度:各ピクセルでのエッジの強さを表す値。勾配の大きさに対応し、√(Gx2 + Gy2) で計算される。値が大きいほど明確なエッジである。
エッジ方向:エッジの向きを表す角度。勾配ベクトルの方向に対応し、arctan(Gy / Gx) で計算される。物体の輪郭の向きを表す。
画像変換
リサイズ:画像のサイズを変更する処理。拡大または縮小が可能である。Webページ用の画像縮小、印刷用の画像拡大などに使用される。
最近傍補間:最も近いピクセルの値をそのまま使用する補間手法。計算が高速だが、画質は低く、ジャギー(階段状のギザギザ)が発生しやすい。
バイリニア補間:周囲4ピクセルの値を線形補間する手法。最近傍補間より滑らかな結果が得られる。計算コストと画質のバランスが良い。
バイキュービック補間:周囲16ピクセルの値を3次関数で補間する手法。高品質な結果が得られるが、計算コストが高い。写真の拡大に適している。
回転:画像を指定角度だけ回転させる処理。回転中心と角度を指定する。画像の向き補正や、データ拡張に使用される。
反転:画像を水平または垂直に反転させる処理。鏡像を作成する。データ拡張や、画像の向き調整に使用される。
画像評価
PSNR(ピーク信号対雑音比):画像の品質を評価する客観的指標。元画像と処理後画像の平均二乗誤差から計算される。単位はdBであり、値が大きいほど品質が高い。一般的に30dB以上で良好な品質とされる。
MSE(平均二乗誤差):2つの画像の差の二乗の平均。値が小さいほど類似度が高い。計算式は MSE = (1/N) Σ (I1 - I2)2 である。PSNRの計算に使用される。
ヒストグラム比較:2つの画像のヒストグラムの類似度を測定する手法。相関、カイ二乗検定、交差、Bhattacharyya距離などの指標が使用される。画像の類似性判定や、画像検索に応用される。
ブラー検出:画像のぼやけ度合いを定量化する処理。Laplacian分散により評価され、値が小さいほどぼやけている。ピンぼけ画像の自動検出に使用される。
Pythonでの実現
カラー画像処理の用語と関数の対応表を以下に示す。
画像の基本概念
| 用語 | 関数・メソッド | 説明 |
|---|---|---|
| 画像サイズ | image.shape |
(高さ, 幅, チャンネル数)のタプルを返す |
| ビット深度 | image.dtype |
データ型を返す(uint8, uint16など) |
| データ型 | image.astype(dtype) |
データ型を変換 |
| アスペクト比 | width / height |
幅と高さの比率 |
色空間と色表現
| 用語 | 関数・メソッド | 説明 |
|---|---|---|
| RGB色空間 | cv2.cvtColor(image, cv2.COLOR_BGR2RGB) |
BGRからRGBへ変換 |
| HSV色空間 | cv2.cvtColor(image, cv2.COLOR_BGR2HSV) |
BGRからHSVへ変換 |
| グレースケール | cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) |
カラーからグレースケールへ変換 |
| 色相 | hsv[:,:,0] |
HSV画像のH成分 |
| 彩度 | hsv[:,:,1] |
HSV画像のS成分 |
| 明度 | hsv[:,:,2] |
HSV画像のV成分 |
画質改善技術
| 用語 | 関数・メソッド | 説明 |
|---|---|---|
| ヒストグラム均等化 | cv2.equalizeHist(gray) |
グレースケール画像のヒストグラム均等化 |
| CLAHE | cv2.createCLAHE(clipLimit, tileGridSize).apply(gray) |
適応的ヒストグラム均等化 |
| ガンマ補正 | np.power(image/255.0, gamma) * 255 |
非線形輝度変換 |
| コントラスト調整 | cv2.convertScaleAbs(image, alpha=alpha, beta=beta) |
線形変換 |
| 明度調整 | cv2.add(image, value) |
全ピクセルに値を加算 |
| 彩度調整 | HSV空間のS成分を調整 | 彩度の変更 |
ノイズ評価と除去
| 用語 | 関数・メソッド | 説明 |
|---|---|---|
| ガウシアンノイズ | np.random.normal(mean, std, image.shape) |
正規分布ノイズの生成 |
| ソルト&ペッパーノイズ | np.random.rand() で確率的に生成 |
白黒点ノイズの生成 |
| ノイズ分散 | np.var(noise) |
ノイズの分散 |
| SNR | 10 * np.log10(signal_power / noise_power) |
信号対雑音比 |
| ガウシアンフィルタ | cv2.GaussianBlur(image, ksize, sigma) |
ガウス平滑化 |
| メディアンフィルタ | cv2.medianBlur(image, ksize) |
中央値フィルタ |
| バイラテラルフィルタ | cv2.bilateralFilter(image, d, sigmaColor, sigmaSpace) |
エッジ保存平滑化 |
畳み込み演算
| 用語 | 関数・メソッド | 説明 |
|---|---|---|
| カーネル | np.array([[...], [...], [...]]) |
フィルタ行列の定義 |
| 畳み込み処理 | cv2.filter2D(image, -1, kernel) |
カスタムカーネルの適用 |
| 平滑化カーネル | np.ones((ksize, ksize)) / (ksize**2) |
平均化フィルタ |
| 鮮鋭化カーネル | 中心を強調するカーネル | エッジ強調 |
| エッジ検出カーネル | Sobelカーネルなど | 勾配計算 |
エッジ解析
| 用語 | 関数・メソッド | 説明 |
|---|---|---|
| 勾配 | cv2.Sobel() の結果 |
輝度変化の大きさと方向 |
| Sobelフィルタ | cv2.Sobel(image, ddepth, dx, dy, ksize) |
勾配計算 |
| Laplacianフィルタ | cv2.Laplacian(image, ddepth) |
2次微分 |
| Cannyエッジ検出 | cv2.Canny(image, threshold1, threshold2) |
多段階エッジ検出 |
| エッジ強度 | np.sqrt(gx**2 + gy**2) |
勾配の大きさ |
| エッジ方向 | np.arctan2(gy, gx) |
勾配の方向 |
画像変換
| 用語 | 関数・メソッド | 説明 |
|---|---|---|
| リサイズ | cv2.resize(image, dsize, interpolation) |
サイズ変更 |
| 最近傍補間 | cv2.resize(..., interpolation=cv2.INTER_NEAREST) |
最近傍補間 |
| バイリニア補間 | cv2.resize(..., interpolation=cv2.INTER_LINEAR) |
線形補間 |
| バイキュービック補間 | cv2.resize(..., interpolation=cv2.INTER_CUBIC) |
3次補間 |
| 回転 | cv2.getRotationMatrix2D() + cv2.warpAffine() |
回転変換 |
| 反転 | cv2.flip(image, flipCode) |
画像の反転 |
| トリミング | image[y1:y2, x1:x2] |
スライシング |
画像評価
| 用語 | 関数・メソッド | 説明 |
|---|---|---|
| PSNR | cv2.PSNR(image1, image2) |
ピーク信号対雑音比 |
| MSE | np.mean((image1 - image2)**2) |
平均二乗誤差 |
| ヒストグラム比較 | cv2.compareHist(hist1, hist2, method) |
ヒストグラム類似度 |
| ブラー検出 | cv2.Laplacian(gray, cv2.CV_64F).var() |
ぼやけ度の評価 |
Pythonプログラム例
1. 画質改善処理
このコードは画像の明るさとコントラストを改善する基本的な手法を実装する。ヒストグラム均等化により輝度分布を平坦化し、CLAHE(適応的ヒストグラム均等化)により局所的なコントラストを改善する。ガンマ補正による非線形な明るさ調整、コントラスト調整、彩度調整も実装する。暗い画像や明るすぎる画像の視認性向上に有効である。
import cv2
import numpy as np
import matplotlib.pyplot as plt
# テスト画像の生成(暗い画像)
def create_dark_image():
image = np.zeros((300, 400, 3), dtype=np.uint8)
cv2.rectangle(image, (50, 50), (150, 150), (60, 60, 60), -1)
cv2.circle(image, (300, 100), 50, (80, 80, 80), -1)
cv2.rectangle(image, (100, 200), (300, 280), (40, 40, 40), -1)
return image
# ヒストグラム均等化
def histogram_equalization(image):
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
equalized = cv2.equalizeHist(gray)
return equalized
# CLAHE(適応的ヒストグラム均等化)
def clahe_enhancement(image):
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
enhanced = clahe.apply(gray)
return enhanced
# ガンマ補正
def gamma_correction(image, gamma):
normalized = image / 255.0
corrected = np.power(normalized, gamma)
return (corrected * 255).astype(np.uint8)
# コントラスト調整
def adjust_contrast(image, alpha, beta):
adjusted = cv2.convertScaleAbs(image, alpha=alpha, beta=beta)
return adjusted
# 彩度調整
def adjust_saturation(image, scale):
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV).astype(np.float32)
hsv[:,:,1] = hsv[:,:,1] * scale
hsv[:,:,1] = np.clip(hsv[:,:,1], 0, 255)
hsv = hsv.astype(np.uint8)
return cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
# 実行
image = create_dark_image()
eq_result = histogram_equalization(image)
clahe_result = clahe_enhancement(image)
gamma_bright = gamma_correction(image, 0.5)
contrast_result = adjust_contrast(image, alpha=1.5, beta=30)
# カラー画像での彩度調整用のテスト画像
color_image = np.ones((300, 400, 3), dtype=np.uint8) * 128
cv2.rectangle(color_image, (50, 50), (150, 150), (200, 100, 100), -1)
cv2.circle(color_image, (300, 100), 50, (100, 200, 100), -1)
cv2.rectangle(color_image, (100, 200), (300, 280), (100, 100, 200), -1)
saturation_high = adjust_saturation(color_image, 1.5)
saturation_low = adjust_saturation(color_image, 0.5)
# 表示
plt.figure(figsize=(15, 10))
plt.subplot(3, 3, 1)
plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
plt.title('Original (Dark)')
plt.axis('off')
plt.subplot(3, 3, 2)
plt.imshow(eq_result, cmap='gray')
plt.title('Histogram Equalization')
plt.axis('off')
plt.subplot(3, 3, 3)
plt.imshow(clahe_result, cmap='gray')
plt.title('CLAHE')
plt.axis('off')
plt.subplot(3, 3, 4)
plt.imshow(cv2.cvtColor(gamma_bright, cv2.COLOR_BGR2RGB))
plt.title('Gamma Correction (γ=0.5)')
plt.axis('off')
plt.subplot(3, 3, 5)
plt.imshow(cv2.cvtColor(contrast_result, cv2.COLOR_BGR2RGB))
plt.title('Contrast Adjustment')
plt.axis('off')
plt.subplot(3, 3, 6)
gray_original = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
plt.hist(gray_original.ravel(), bins=256, range=[0, 256], alpha=0.5, label='Original')
plt.hist(eq_result.ravel(), bins=256, range=[0, 256], alpha=0.5, label='Equalized')
plt.title('Histogram Comparison')
plt.xlabel('Pixel Value')
plt.ylabel('Frequency')
plt.legend()
plt.subplot(3, 3, 7)
plt.imshow(cv2.cvtColor(color_image, cv2.COLOR_BGR2RGB))
plt.title('Original (Color)')
plt.axis('off')
plt.subplot(3, 3, 8)
plt.imshow(cv2.cvtColor(saturation_high, cv2.COLOR_BGR2RGB))
plt.title('High Saturation (×1.5)')
plt.axis('off')
plt.subplot(3, 3, 9)
plt.imshow(cv2.cvtColor(saturation_low, cv2.COLOR_BGR2RGB))
plt.title('Low Saturation (×0.5)')
plt.axis('off')
plt.tight_layout()
plt.show()
print("画像サイズ:", image.shape)
print("データ型:", image.dtype)
print("アスペクト比:", image.shape[1] / image.shape[0])
2. ノイズ量の評価
このコードは画像に含まれるノイズの量を定量的に評価する。ガウシアンノイズとソルト&ペッパーノイズを人工的に付加し、ノイズ分散とSNR(信号対雑音比)を計算する。ガウシアンフィルタ、メディアンフィルタ、バイラテラルフィルタによるノイズ除去効果を評価し、PSNRにより画質を定量化する。
import cv2
import numpy as np
import matplotlib.pyplot as plt
# テスト画像の生成
def create_test_image():
image = np.ones((300, 400, 3), dtype=np.uint8) * 128
cv2.rectangle(image, (50, 50), (150, 150), (200, 100, 100), -1)
cv2.circle(image, (300, 100), 50, (100, 200, 100), -1)
cv2.rectangle(image, (100, 200), (300, 280), (100, 100, 200), -1)
return image
# ガウシアンノイズの付加
def add_gaussian_noise(image, mean=0, std=25):
noise = np.random.normal(mean, std, image.shape)
noisy = image.astype(float) + noise
return np.clip(noisy, 0, 255).astype(np.uint8)
# ソルト&ペッパーノイズの付加
def add_salt_pepper_noise(image, prob=0.05):
noisy = image.copy()
salt = np.random.rand(*image.shape[:2]) < prob / 2
noisy[salt] = 255
pepper = np.random.rand(*image.shape[:2]) < prob / 2
noisy[pepper] = 0
return noisy
# SNR計算
def calculate_snr(original, noisy):
original_gray = cv2.cvtColor(original, cv2.COLOR_BGR2GRAY).astype(float)
noisy_gray = cv2.cvtColor(noisy, cv2.COLOR_BGR2GRAY).astype(float)
signal_power = np.mean(original_gray ** 2)
noise = original_gray - noisy_gray
noise_power = np.mean(noise ** 2)
if noise_power == 0:
return float('inf')
snr = 10 * np.log10(signal_power / noise_power)
return snr
# ノイズ分散の計算
def calculate_noise_variance(original, noisy):
original_gray = cv2.cvtColor(original, cv2.COLOR_BGR2GRAY).astype(float)
noisy_gray = cv2.cvtColor(noisy, cv2.COLOR_BGR2GRAY).astype(float)
noise = original_gray - noisy_gray
return np.var(noise)
# 実行
original = create_test_image()
gaussian_noisy = add_gaussian_noise(original, std=25)
sp_noisy = add_salt_pepper_noise(original, prob=0.05)
# ノイズ評価
gaussian_snr = calculate_snr(original, gaussian_noisy)
sp_snr = calculate_snr(original, sp_noisy)
gaussian_var = calculate_noise_variance(original, gaussian_noisy)
sp_var = calculate_noise_variance(original, sp_noisy)
# ノイズ除去
gaussian_filtered = cv2.GaussianBlur(gaussian_noisy, (5, 5), 0)
sp_filtered = cv2.medianBlur(sp_noisy, 5)
bilateral_filtered = cv2.bilateralFilter(gaussian_noisy, 9, 75, 75)
# PSNR計算
psnr_gaussian_filtered = cv2.PSNR(original, gaussian_filtered)
psnr_sp_filtered = cv2.PSNR(original, sp_filtered)
psnr_bilateral = cv2.PSNR(original, bilateral_filtered)
# MSE計算
mse_gaussian = np.mean((original.astype(float) - gaussian_noisy.astype(float))**2)
mse_sp = np.mean((original.astype(float) - sp_noisy.astype(float))**2)
# 表示
plt.figure(figsize=(15, 12))
plt.subplot(3, 3, 1)
plt.imshow(cv2.cvtColor(original, cv2.COLOR_BGR2RGB))
plt.title('Original')
plt.axis('off')
plt.subplot(3, 3, 2)
plt.imshow(cv2.cvtColor(gaussian_noisy, cv2.COLOR_BGR2RGB))
plt.title(f'Gaussian Noise\nSNR: {gaussian_snr:.2f} dB\nVar: {gaussian_var:.2f}\nMSE: {mse_gaussian:.2f}')
plt.axis('off')
plt.subplot(3, 3, 3)
plt.imshow(cv2.cvtColor(gaussian_filtered, cv2.COLOR_BGR2RGB))
plt.title(f'Gaussian Filtered\nPSNR: {psnr_gaussian_filtered:.2f} dB')
plt.axis('off')
plt.subplot(3, 3, 5)
plt.imshow(cv2.cvtColor(sp_noisy, cv2.COLOR_BGR2RGB))
plt.title(f'Salt & Pepper\nSNR: {sp_snr:.2f} dB\nVar: {sp_var:.2f}\nMSE: {mse_sp:.2f}')
plt.axis('off')
plt.subplot(3, 3, 6)
plt.imshow(cv2.cvtColor(sp_filtered, cv2.COLOR_BGR2RGB))
plt.title(f'Median Filtered\nPSNR: {psnr_sp_filtered:.2f} dB')
plt.axis('off')
plt.subplot(3, 3, 8)
plt.imshow(cv2.cvtColor(bilateral_filtered, cv2.COLOR_BGR2RGB))
plt.title(f'Bilateral Filtered\nPSNR: {psnr_bilateral:.2f} dB')
plt.axis('off')
plt.tight_layout()
plt.show()
print(f"ガウシアンノイズ - SNR: {gaussian_snr:.2f} dB, 分散: {gaussian_var:.2f}, MSE: {mse_gaussian:.2f}")
print(f"ソルト&ペッパーノイズ - SNR: {sp_snr:.2f} dB, 分散: {sp_var:.2f}, MSE: {mse_sp:.2f}")
print(f"ガウシアンフィルタ後 PSNR: {psnr_gaussian_filtered:.2f} dB")
print(f"メディアンフィルタ後 PSNR: {psnr_sp_filtered:.2f} dB")
print(f"バイラテラルフィルタ後 PSNR: {psnr_bilateral:.2f} dB")
3. 畳み込みフィルタの適用
このコードは様々な畳み込みカーネルを画像に適用し、その効果を比較する。平滑化カーネル、鮮鋭化カーネル、エッジ検出カーネルを定義し、畳み込み処理により画像を処理する。カーネルの設計により多様な画像処理が実現できることを示す。
import cv2
import numpy as np
import matplotlib.pyplot as plt
# テスト画像の生成
def create_test_image():
image = np.ones((300, 400, 3), dtype=np.uint8) * 200
cv2.rectangle(image, (50, 50), (150, 150), (100, 100, 100), -1)
cv2.rectangle(image, (50, 50), (150, 150), (80, 80, 80), 2)
cv2.circle(image, (300, 100), 50, (120, 120, 120), -1)
cv2.circle(image, (300, 100), 50, (90, 90, 90), 2)
pts = np.array([[100, 200], [200, 200], [150, 280]], np.int32)
cv2.fillPoly(image, [pts], (130, 130, 130))
cv2.polylines(image, [pts], True, (100, 100, 100), 2)
return image
# カーネルの定義
def create_kernels():
# 平滑化カーネル(3x3平均化)
blur_kernel = np.ones((3, 3), np.float32) / 9
# 鮮鋭化カーネル
sharpen_kernel = np.array([
[0, -1, 0],
[-1, 5, -1],
[0, -1, 0]
], np.float32)
# エッジ検出カーネル(水平)
edge_h_kernel = np.array([
[-1, -1, -1],
[0, 0, 0],
[1, 1, 1]
], np.float32)
# エッジ検出カーネル(垂直)
edge_v_kernel = np.array([
[-1, 0, 1],
[-1, 0, 1],
[-1, 0, 1]
], np.float32)
# エッジ強調カーネル
edge_enhance_kernel = np.array([
[-1, -1, -1],
[-1, 8, -1],
[-1, -1, -1]
], np.float32)
return blur_kernel, sharpen_kernel, edge_h_kernel, edge_v_kernel, edge_enhance_kernel
# 畳み込み処理
def apply_convolution(image, kernel):
return cv2.filter2D(image, -1, kernel)
# 実行
image = create_test_image()
blur_k, sharp_k, edge_h_k, edge_v_k, edge_enh_k = create_kernels()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 各カーネルの適用
blurred = apply_convolution(image, blur_k)
sharpened = apply_convolution(image, sharp_k)
edge_h = apply_convolution(gray, edge_h_k)
edge_v = apply_convolution(gray, edge_v_k)
edge_enhanced = apply_convolution(gray, edge_enh_k)
# 表示
plt.figure(figsize=(15, 10))
plt.subplot(2, 3, 1)
plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
plt.title('Original')
plt.axis('off')
plt.subplot(2, 3, 2)
plt.imshow(cv2.cvtColor(blurred, cv2.COLOR_BGR2RGB))
plt.title('Blur (3×3 Average)')
plt.axis('off')
plt.subplot(2, 3, 3)
plt.imshow(cv2.cvtColor(sharpened, cv2.COLOR_BGR2RGB))
plt.title('Sharpen')
plt.axis('off')
plt.subplot(2, 3, 4)
plt.imshow(edge_h, cmap='gray')
plt.title('Edge Detection (Horizontal)')
plt.axis('off')
plt.subplot(2, 3, 5)
plt.imshow(edge_v, cmap='gray')
plt.title('Edge Detection (Vertical)')
plt.axis('off')
plt.subplot(2, 3, 6)
plt.imshow(edge_enhanced, cmap='gray')
plt.title('Edge Enhancement')
plt.axis('off')
plt.tight_layout()
plt.show()
print("カーネルサイズ:")
print(f"平滑化カーネル: {blur_k.shape}")
print(f"鮮鋭化カーネル: {sharp_k.shape}")
print(f"エッジ検出カーネル: {edge_h_k.shape}")
4. エッジの解析
このコードは画像内のエッジを検出し、その強度と方向を解析する。Sobelフィルタにより勾配を計算し、エッジの大きさと方向を可視化する。Laplacianフィルタによる2次微分、Cannyエッジ検出による精密なエッジ抽出も実装する。物体の輪郭抽出や形状解析の基礎となる。
import cv2
import numpy as np
import matplotlib.pyplot as plt
# テスト画像の生成
def create_test_image():
image = np.ones((300, 400, 3), dtype=np.uint8) * 200
cv2.rectangle(image, (50, 50), (150, 150), (100, 100, 100), -1)
cv2.circle(image, (300, 100), 50, (120, 120, 120), -1)
pts = np.array([[100, 200], [200, 200], [150, 280]], np.int32)
cv2.fillPoly(image, [pts], (130, 130, 130))
cv2.line(image, (250, 200), (350, 280), (80, 80, 80), 3)
return image
# Sobelエッジ検出
def sobel_edge_detection(image):
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
sobel_x = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=3)
sobel_y = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=3)
magnitude = np.sqrt(sobel_x**2 + sobel_y**2)
magnitude = np.uint8(255 * magnitude / magnitude.max())
direction = np.arctan2(sobel_y, sobel_x)
return magnitude, direction, sobel_x, sobel_y
# Cannyエッジ検出
def canny_edge_detection(image, low_threshold, high_threshold):
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 1.4)
edges = cv2.Canny(blurred, low_threshold, high_threshold)
return edges
# Laplacianエッジ検出
def laplacian_edge_detection(image):
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
laplacian = cv2.Laplacian(gray, cv2.CV_64F)
laplacian = np.uint8(np.absolute(laplacian))
return laplacian
# ブラー検出
def detect_blur(image):
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
laplacian_var = cv2.Laplacian(gray, cv2.CV_64F).var()
return laplacian_var
# 実行
image = create_test_image()
magnitude, direction, sobel_x, sobel_y = sobel_edge_detection(image)
canny_edges = canny_edge_detection(image, 50, 150)
laplacian_edges = laplacian_edge_detection(image)
blur_score = detect_blur(image)
# 表示
plt.figure(figsize=(15, 10))
plt.subplot(3, 3, 1)
plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
plt.title('Original')
plt.axis('off')
plt.subplot(3, 3, 2)
plt.imshow(magnitude, cmap='gray')
plt.title('Sobel Magnitude (Edge Strength)')
plt.axis('off')
plt.subplot(3, 3, 3)
plt.imshow(direction, cmap='hsv')
plt.title('Sobel Direction (Edge Direction)')
plt.axis('off')
plt.colorbar(label='Angle (radians)')
plt.subplot(3, 3, 4)
plt.imshow(canny_edges, cmap='gray')
plt.title('Canny Edges')
plt.axis('off')
plt.subplot(3, 3, 5)
plt.imshow(np.abs(sobel_x), cmap='gray')
plt.title('Sobel X (Gradient)')
plt.axis('off')
plt.subplot(3, 3, 6)
plt.imshow(np.abs(sobel_y), cmap='gray')
plt.title('Sobel Y (Gradient)')
plt.axis('off')
plt.subplot(3, 3, 7)
plt.imshow(laplacian_edges, cmap='gray')
plt.title('Laplacian')
plt.axis('off')
plt.subplot(3, 3, 8)
plt.hist(magnitude.ravel(), bins=50, color='blue', alpha=0.7)
plt.title('Edge Strength Histogram')
plt.xlabel('Magnitude')
plt.ylabel('Frequency')
plt.subplot(3, 3, 9)
plt.text(0.1, 0.5, f'Blur Detection Score:\n{blur_score:.2f}\n\n(Higher = Sharper)',
fontsize=14, verticalalignment='center')
plt.axis('off')
plt.tight_layout()
plt.show()
print(f"検出されたエッジピクセル数(Canny): {np.sum(canny_edges > 0)}")
print(f"平均エッジ強度: {np.mean(magnitude):.2f}")
print(f"ブラー検出スコア: {blur_score:.2f}")
5. リサイズと補間
このコードは画像のリサイズと各種補間手法を比較する。最近傍補間、バイリニア補間、バイキュービック補間の違いを可視化する。画像の拡大と縮小の両方を実装し、回転・反転・トリミングなどの基本的な幾何学変換も示す。ヒストグラム比較により、元画像と変換後画像の類似度を評価する。
import cv2
import numpy as np
import matplotlib.pyplot as plt
# テスト画像の生成
def create_test_image():
image = np.ones((200, 200, 3), dtype=np.uint8) * 200
for i in range(0, 200, 20):
cv2.line(image, (i, 0), (i, 200), (100, 100, 100), 2)
cv2.line(image, (0, i), (200, i), (100, 100, 100), 2)
cv2.circle(image, (100, 100), 50, (150, 100, 100), -1)
cv2.rectangle(image, (50, 50), (150, 150), (100, 150, 100), 3)
for i in range(60, 140, 10):
cv2.line(image, (60, i), (140, i), (80, 80, 80), 1)
return image
# 各種補間手法でのリサイズ
def resize_with_interpolation(image, scale):
h, w = image.shape[:2]
new_size = (int(w * scale), int(h * scale))
nearest = cv2.resize(image, new_size, interpolation=cv2.INTER_NEAREST)
linear = cv2.resize(image, new_size, interpolation=cv2.INTER_LINEAR)
cubic = cv2.resize(image, new_size, interpolation=cv2.INTER_CUBIC)
return nearest, linear, cubic
# 回転
def rotate_image(image, angle):
h, w = image.shape[:2]
center = (w // 2, h // 2)
matrix = cv2.getRotationMatrix2D(center, angle, 1.0)
rotated = cv2.warpAffine(image, matrix, (w, h))
return rotated
# 反転
def flip_image(image):
horizontal = cv2.flip(image, 1)
vertical = cv2.flip(image, 0)
return horizontal, vertical
# トリミング
def crop_image(image, x, y, w, h):
return image[y:y+h, x:x+w]
# ヒストグラム比較
def compare_histograms(image1, image2):
gray1 = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)
gray2 = cv2.cvtColor(image2, cv2.COLOR_BGR2GRAY)
hist1 = cv2.calcHist([gray1], [0], None, [256], [0, 256])
hist2 = cv2.calcHist([gray2], [0], None, [256], [0, 256])
correlation = cv2.compareHist(hist1, hist2, cv2.HISTCMP_CORREL)
return correlation
# 実行
image = create_test_image()
# 拡大(2倍)
nearest_up, linear_up, cubic_up = resize_with_interpolation(image, 2.0)
# 縮小(0.5倍)
nearest_down, linear_down, cubic_down = resize_with_interpolation(image, 0.5)
# 回転と反転
rotated_45 = rotate_image(image, 45)
h_flip, v_flip = flip_image(image)
# トリミング
cropped = crop_image(image, 50, 50, 100, 100)
# ヒストグラム比較
hist_corr_nearest = compare_histograms(image, cv2.resize(nearest_down, (200, 200)))
hist_corr_linear = compare_histograms(image, cv2.resize(linear_down, (200, 200)))
hist_corr_cubic = compare_histograms(image, cv2.resize(cubic_down, (200, 200)))
# 表示
fig = plt.figure(figsize=(20, 12))
# 元画像
plt.subplot(3, 5, 1)
plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
plt.title('Original (200×200)')
plt.axis('off')
# 拡大(2倍)
plt.subplot(3, 5, 2)
plt.imshow(cv2.cvtColor(nearest_up, cv2.COLOR_BGR2RGB))
plt.title('2× Nearest Neighbor')
plt.axis('off')
plt.subplot(3, 5, 3)
plt.imshow(cv2.cvtColor(linear_up, cv2.COLOR_BGR2RGB))
plt.title('2× Bilinear')
plt.axis('off')
plt.subplot(3, 5, 4)
plt.imshow(cv2.cvtColor(cubic_up, cv2.COLOR_BGR2RGB))
plt.title('2× Bicubic')
plt.axis('off')
# 縮小(0.5倍)
plt.subplot(3, 5, 6)
plt.imshow(cv2.cvtColor(nearest_down, cv2.COLOR_BGR2RGB))
plt.title(f'0.5× Nearest\nHist Corr: {hist_corr_nearest:.3f}')
plt.axis('off')
plt.subplot(3, 5, 7)
plt.imshow(cv2.cvtColor(linear_down, cv2.COLOR_BGR2RGB))
plt.title(f'0.5× Bilinear\nHist Corr: {hist_corr_linear:.3f}')
plt.axis('off')
plt.subplot(3, 5, 8)
plt.imshow(cv2.cvtColor(cubic_down, cv2.COLOR_BGR2RGB))
plt.title(f'0.5× Bicubic\nHist Corr: {hist_corr_cubic:.3f}')
plt.axis('off')
# 回転
plt.subplot(3, 5, 11)
plt.imshow(cv2.cvtColor(rotated_45, cv2.COLOR_BGR2RGB))
plt.title('Rotated 45°')
plt.axis('off')
# 反転
plt.subplot(3, 5, 12)
plt.imshow(cv2.cvtColor(h_flip, cv2.COLOR_BGR2RGB))
plt.title('Horizontal Flip')
plt.axis('off')
plt.subplot(3, 5, 13)
plt.imshow(cv2.cvtColor(v_flip, cv2.COLOR_BGR2RGB))
plt.title('Vertical Flip')
plt.axis('off')
# トリミング
plt.subplot(3, 5, 14)
plt.imshow(cv2.cvtColor(cropped, cv2.COLOR_BGR2RGB))
plt.title('Cropped (100×100)')
plt.axis('off')
plt.tight_layout()
plt.show()
print("画像サイズ情報:")
print(f"元画像: {image.shape}")
print(f"2倍拡大(最近傍補間): {nearest_up.shape}")
print(f"0.5倍縮小(最近傍補間): {nearest_down.shape}")
print(f"トリミング: {cropped.shape}")
print(f"\nヒストグラム相関(縮小→拡大後):")
print(f"最近傍補間: {hist_corr_nearest:.3f}")
print(f"バイリニア補間: {hist_corr_linear:.3f}")
print(f"バイキュービック補間: {hist_corr_cubic:.3f}")