カラー画像処理の Python 実現ガイド

【概要】カラー画像処理の用語(40項目)とPython実装を示す。用語は画像の基本概念、色空間と色表現、画質改善技術、ノイズ評価と除去、畳み込み演算、エッジ解析、画像変換、画像評価に分類され、対応するPython関数を明示する。実装例として、画質改善処理、ノイズ量の評価、畳み込みフィルタの適用、エッジの解析、リサイズと補間の5つのプログラムを提供する。

【目次】

  1. カラー画像処理の基本
  2. 用語リスト
  3. Pythonでの実現
  4. Pythonプログラム例

【サイト内のPython関連主要ページ】

【外部リソース】

カラー画像処理の基本

本資料は、Pythonによるカラー画像処理の基礎を学ぶための教材である。OpenCVとNumPyを用いた画像処理の実装を対象とし、プログラミングの基礎知識を前提とする。

カラー画像処理で使用する基本概念を示す。各概念の詳細は「用語リスト」を参照されたい。

用語リスト

画像の基本概念

色空間と色表現

画質改善技術

ノイズ評価と除去

畳み込み演算

エッジ解析

画像変換

画像評価

Pythonでの実現

カラー画像処理の用語と関数の対応表を示す。各関数の使用方法は「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] NumPyスライシングで切り出す

画像評価

用語 関数・メソッド 説明
PSNR cv2.PSNR(image1, image2) ピーク信号対雑音比を計算する
MSE np.mean((image1 - image2)**2) 平均二乗誤差を計算する
ヒストグラム比較 cv2.compareHist(hist1, hist2, method) ヒストグラム類似度を計算する
ブラー検出 cv2.Laplacian(gray, cv2.CV_64F).var() ぼやけ度を評価する

Python開発環境、ライブラリ類

ここでは、最低限の事前準備について説明する。機械学習や深層学習を行う場合は、NVIDIA CUDA、Visual Studio、Cursorなどの追加インストールが便利である。詳細は別ページ https://www.kkaneko.jp/cc/dev/aiassist.html を参照されたい。

Python 3.12 のインストール

インストール済みの場合は実行不要である。

管理者権限でコマンドプロンプトを起動し(Windowsキーまたはスタートメニュー、cmdと入力、右クリック、「管理者として実行」)、以下を実行する。管理者権限は、wingetの--scope machineオプションでシステム全体にインストールするために必要である。

REM Python 3.12 をシステム領域にインストール
winget install --scope machine --id Python.Python.3.12 -e --silent --accept-source-agreements --accept-package-agreements
REM Python のパス設定
set "PYTHON_PATH=C:\Program Files\Python312"
set "PYTHON_SCRIPTS_PATH=C:\Program Files\Python312\Scripts"
if exist "%PYTHON_PATH%" setx PYTHON_PATH "%PYTHON_PATH%" /M >nul
if exist "%PYTHON_SCRIPTS_PATH%" setx PYTHON_SCRIPTS_PATH "%PYTHON_SCRIPTS_PATH%" /M >nul
for /f "skip=2 tokens=2*" %a in ('reg query "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /v Path') do set "SYSTEM_PATH=%b"
echo "%SYSTEM_PATH%" | find /i "%PYTHON_PATH%" >nul
if errorlevel 1 setx PATH "%PYTHON_PATH%;%PYTHON_SCRIPTS_PATH%;%SYSTEM_PATH%" /M >nul

関連する外部ページ

Python の公式ページ: https://www.python.org/

AIエディタ Windsurf のインストール

Pythonプログラムの編集・実行には、AIエディタの利用を推奨する。ここでは、Windsurfのインストールを説明する。

管理者権限でコマンドプロンプトを起動し(Windowsキーまたはスタートメニュー、cmdと入力、右クリック、「管理者として実行」)、以下を実行する。

winget install --scope machine --id Codeium.Windsurf -e --silent --accept-source-agreements --accept-package-agreements

関連する外部ページ

Windsurf の公式ページ: https://windsurf.com/

OpenCV(cv2)、NumPy、Matplotlib のインストール

コマンドプロンプトを管理者として実行し(Windowsキーまたはスタートメニュー、cmdと入力、右クリック、「管理者として実行」)、以下を実行する。

pip install opencv-python numpy matplotlib

Pythonプログラム例

以下のプログラム例を実行する前に、次の前提条件を確認する。

1. 画質改善処理

このプログラムは画像の明るさとコントラストを改善する手法を実装する。暗い画像や明るすぎる画像の視認性向上に用いる。ヒストグラム均等化、CLAHE、ガンマ補正、コントラスト調整、彩度調整の5つの手法を比較する。

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. ノイズ量の評価

このプログラムは画像に含まれるノイズの量を定量的に評価する。ノイズの種類に応じたフィルタ選択の指標を示す。ガウシアンノイズにはガウシアンフィルタまたはバイラテラルフィルタ、ソルト&ペッパーノイズにはメディアンフィルタを用いる。

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、Cannyの3つの手法を比較し、用途に応じた手法選択の参考を示す。ノイズが多い画像では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}")