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

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

【目次】

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

【サイト内の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] スライシング

画像評価

用語 関数・メソッド 説明
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}")