Skip to content

8. 画像処理 ②:古典的な特徴抽出

第 8 回では、画像処理における古典的な特徴抽出手法について学びます。

特徴抽出 は、画像から重要な情報(エッジ、コーナー、テクスチャなど)を取り出す処理です。これらの特徴は、物体認識や画像分類などの機械学習タスクで利用されます。本講義では、エッジ検出、コーナー検出、HOG(Histogram of Oriented Gradients)などの古典的手法を学びます。

8.1 特徴抽出とは

8.1.1 特徴抽出の目的

特徴抽出(Feature Extraction) は、生の画像データから、機械学習モデルに有用な情報を抽出する処理です。

graph LR
    A["生の画像<br/>大量の画素データ"] --> B["特徴抽出"]
    B --> C["特徴ベクトル<br/>少数の重要な情報"]
    C --> D["機械学習<br/>モデル"]
    D --> E["予測"]

    style A fill:#e6f3ff,stroke:#66b3ff,stroke-width:3px
    style B fill:#fff4cc,stroke:#ffcc00,stroke-width:3px
    style C fill:#ccffcc,stroke:#66cc66,stroke-width:3px
    style D fill:#ffffcc,stroke:#ffcc00,stroke-width:3px
    style E fill:#ffcccc,stroke:#ff6666,stroke-width:3px

特徴抽出の利点:

  • 次元削減: 大量の画素データを少数の特徴量に圧縮
  • 不変性: 照明や位置の変化に強い
  • 解釈性: どんな特徴を使っているか理解しやすい

8.1.2 古典的手法とディープラーニング

graph TB
    A["特徴抽出の手法"] --> B["古典的手法<br/>手動で設計"]
    A --> C["ディープラーニング<br/>自動で学習"]

    B --> B1["エッジ検出<br/>コーナー検出<br/>HOG, SIFT"]
    C --> C1["CNN<br/>自動的に特徴を学習"]

    B1 --> D["明示的で解釈しやすい"]
    C1 --> E["高精度だが<br/>ブラックボックス"]

    style A fill:#e6ccff,stroke:#9966ff,stroke-width:3px
    style B fill:#ccffcc,stroke:#66cc66,stroke-width:2px
    style C fill:#ffffcc,stroke:#ffcc00,stroke-width:2px
    style B1 fill:#e6ffe6,stroke:#66cc66,stroke-width:1px
    style C1 fill:#fffef0,stroke:#ffcc00,stroke-width:1px
    style D fill:#ccffcc,stroke:#66cc66,stroke-width:2px
    style E fill:#ffffcc,stroke:#ffcc00,stroke-width:2px

古典的手法:

  • 人間が設計した特徴(エッジ、コーナーなど)
  • 解釈しやすい
  • 計算コストが低い

ディープラーニング:

  • データから自動的に特徴を学習
  • 高精度
  • 大量のデータと計算資源が必要

ディープラーニング(CNN)については、次回(第 9 回)紹介する予定です。

8.2 エッジ検出

8.2.1 エッジとは

エッジ(Edge) は、画像内で明るさが急激に変化する箇所です。物体の輪郭や境界を表現します。

graph LR
    A["エッジ検出"] --> B["明るさの変化<br/>を検出"]
    B --> C["物体の輪郭<br/>を抽出"]
    C --> D["形状認識<br/>物体検出"]

    style A fill:#e6ccff,stroke:#9966ff,stroke-width:3px
    style B fill:#ccffcc,stroke:#66cc66,stroke-width:3px
    style C fill:#ffffcc,stroke:#ffcc00,stroke-width:3px
    style D fill:#ffcccc,stroke:#ff6666,stroke-width:3px

エッジが現れる場所:

  • 物体の輪郭
  • 表面の境界
  • 影の境界
  • テクスチャの変化

8.2.2 Sobel フィルタ

Sobel フィルタ は、最も基本的なエッジ検出手法の一つです。画像の勾配(微分)を計算してエッジを検出します。

エッジ検出には scikit-image の filters.sobel() 関数を使用します。水平方向のエッジには filters.sobel_h()、垂直方向のエッジには filters.sobel_v() を使用できます。

Sobel フィルタのカーネル:

水平方向(x 方向):

\[ G_x = \begin{bmatrix} -1 & 0 & +1 \\ -2 & 0 & +2 \\ -1 & 0 & +1 \end{bmatrix} \]

垂直方向(y 方向):

\[ G_y = \begin{bmatrix} -1 & -2 & -1 \\ 0 & 0 & 0 \\ +1 & +2 & +1 \end{bmatrix} \]

エッジの強度:

\[ G = \sqrt{G_x^2 + G_y^2} \]
from skimage import data, filters
import matplotlib.pyplot as plt
import numpy as np

# サンプル画像の読み込み
img = data.camera()

# Sobelフィルタの適用
sobel_h = filters.sobel_h(img)  # 水平方向
sobel_v = filters.sobel_v(img)  # 垂直方向
sobel = filters.sobel(img)      # 統合

# 表示
fig, axes = plt.subplots(2, 2, figsize=(8, 8))

axes[0, 0].imshow(img, cmap='gray')
axes[0, 0].set_title('元画像')
axes[0, 0].axis('off')

axes[0, 1].imshow(sobel_h, cmap='gray')
axes[0, 1].set_title('Sobel(水平方向)')
axes[0, 1].axis('off')

axes[1, 0].imshow(sobel_v, cmap='gray')
axes[1, 0].set_title('Sobel(垂直方向)')
axes[1, 0].axis('off')

axes[1, 1].imshow(sobel, cmap='gray')
axes[1, 1].set_title('Sobel(統合)')
axes[1, 1].axis('off')

plt.tight_layout()
plt.show()

8-1

8.2.3 Canny エッジ検出

Canny エッジ検出 は、より高度なエッジ検出アルゴリズムです。ノイズに強く、細いエッジを検出できます。

Canny エッジ検出には scikit-image の feature.canny() 関数を使用します。

Canny のアルゴリズム:

  1. ガウシアンフィルタでノイズ除去
  2. Sobel フィルタで勾配を計算
  3. 非最大値抑制(Non-Maximum Suppression)
  4. ヒステリシス閾値処理(Double Thresholding)
from skimage import data, feature
import matplotlib.pyplot as plt

# サンプル画像の読み込み
img = data.camera()

# Cannyエッジ検出(異なる閾値)
canny1 = feature.canny(img, sigma=1)
canny2 = feature.canny(img, sigma=2)
canny3 = feature.canny(img, sigma=3)

# 表示
fig, axes = plt.subplots(2, 2, figsize=(8, 8))

axes[0, 0].imshow(img, cmap='gray')
axes[0, 0].set_title('元画像')
axes[0, 0].axis('off')

axes[0, 1].imshow(canny1, cmap='gray')
axes[0, 1].set_title('Canny(sigma=1)')
axes[0, 1].axis('off')

axes[1, 0].imshow(canny2, cmap='gray')
axes[1, 0].set_title('Canny(sigma=2)')
axes[1, 0].axis('off')

axes[1, 1].imshow(canny3, cmap='gray')
axes[1, 1].set_title('Canny(sigma=3)')
axes[1, 1].axis('off')

plt.tight_layout()
plt.show()

8-2

sigma パラメータ

  • 小さい sigma(1.0): 細かいエッジまで検出(ノイズも検出しやすい)
  • 大きい sigma(3.0): 主要なエッジのみ検出(ノイズに強い)

8.2.4 Prewitt フィルタと Scharr フィルタ

Sobel 以外にも、エッジ検出のためのフィルタがいくつかあります。

Prewitt フィルタには filters.prewitt() 関数を、Scharr フィルタには filters.scharr() 関数を使用します。

Prewitt フィルタ:

\[ G_x = \begin{bmatrix} -1 & 0 & +1 \\ -1 & 0 & +1 \\ -1 & 0 & +1 \end{bmatrix} \]

Scharr フィルタ:

\[ G_x = \begin{bmatrix} -3 & 0 & +3 \\ -10 & 0 & +10 \\ -3 & 0 & +3 \end{bmatrix} \]
from skimage import data, filters
import matplotlib.pyplot as plt

# サンプル画像の読み込み
img = data.camera()

# 各種エッジ検出フィルタ
sobel = filters.sobel(img)
prewitt = filters.prewitt(img)
scharr = filters.scharr(img)

# 表示
fig, axes = plt.subplots(2, 2, figsize=(8, 8))

axes[0, 0].imshow(img, cmap='gray')
axes[0, 0].set_title('元画像')
axes[0, 0].axis('off')

axes[0, 1].imshow(sobel, cmap='gray')
axes[0, 1].set_title('Sobel')
axes[0, 1].axis('off')

axes[1, 0].imshow(prewitt, cmap='gray')
axes[1, 0].set_title('Prewitt')
axes[1, 0].axis('off')

axes[1, 1].imshow(scharr, cmap='gray')
axes[1, 1].set_title('Scharr')
axes[1, 1].axis('off')

plt.tight_layout()
plt.show()

8-3

8.3 コーナー検出

8.3.1 コーナーとは

コーナー(Corner) は、2 つ以上のエッジが交わる点です。画像内で特徴的な点として利用されます。

graph LR
    A["コーナー検出"] --> B["エッジの交点<br/>を検出"]
    B --> C["特徴点として<br/>利用"]
    C --> D["物体追跡<br/>画像マッチング"]

    style A fill:#e6ccff,stroke:#9966ff,stroke-width:3px
    style B fill:#ccffcc,stroke:#66cc66,stroke-width:3px
    style C fill:#ffffcc,stroke:#ffcc00,stroke-width:3px
    style D fill:#ffcccc,stroke:#ff6666,stroke-width:3px

コーナーの特徴:

  • 位置が安定している
  • 回転や拡大縮小に強い
  • 画像マッチングに有用

8.3.2 Harris コーナー検出

Harris コーナー検出 は、最も有名なコーナー検出アルゴリズムの一つです。

コーナー検出には feature.corner_harris() 関数を、検出されたコーナーの座標を取得するには feature.corner_peaks() 関数を使用します。

from skimage import data, feature, color
import matplotlib.pyplot as plt

# サンプル画像の読み込み
img = data.checkerboard()

# Harrisコーナー検出
coords = feature.corner_harris(img)
coords_binary = coords > 0.01 * coords.max()

# コーナーの座標を取得
corner_coords = feature.corner_peaks(coords, min_distance=10)

# 表示
fig, axes = plt.subplots(1, 3, figsize=(13, 5))

axes[0].imshow(img, cmap='gray')
axes[0].set_title('元画像')
axes[0].axis('off')

axes[1].imshow(coords, cmap='gray')
axes[1].set_title('Harris応答')
axes[1].axis('off')

axes[2].imshow(img, cmap='gray')
axes[2].plot(corner_coords[:, 1], corner_coords[:, 0], 'r.', markersize=10)
axes[2].set_title(f'検出されたコーナー({len(corner_coords)}個)')
axes[2].axis('off')

plt.tight_layout()
plt.show()

8-4

8.3.3 FAST(Features from Accelerated Segment Test)

FAST は、高速なコーナー検出アルゴリズムです。リアルタイム処理に適しています。

FAST 特徴点の検出には feature.corner_fast() 関数を使用します。

from skimage import data, feature
import matplotlib.pyplot as plt

# サンプル画像の読み込み
img = data.camera()

# FAST特徴点検出
fast = feature.corner_fast(img, threshold=0.15)
keypoints = feature.corner_peaks(fast)

# 表示
plt.figure(figsize=(8, 8))
plt.imshow(img, cmap='gray')
plt.plot(keypoints[:, 1], keypoints[:, 0], 'r.', markersize=8)
plt.title(f'FAST特徴点({len(keypoints)}個)')
plt.axis('off')
plt.tight_layout()
plt.show()

8-5

コーナー検出の使い分け

  • Harris: 高精度で安定したコーナー検出、画像マッチングに適する
  • FAST: 高速処理が必要な場合(リアルタイムアプリケーション)

8.4 ヒストグラム特徴

8.4.1 色ヒストグラム

色ヒストグラム は、画像内の色の分布を表現する特徴量です。画像全体の色の傾向を捉えます。

from skimage import data
import matplotlib.pyplot as plt
import numpy as np

# カラー画像の読み込み
img = data.astronaut()

# 各チャンネルのヒストグラムを計算
colors = ('red', 'green', 'blue')
fig, axes = plt.subplots(2, 2, figsize=(12, 10))

# 元の画像
axes[0, 0].imshow(img)
axes[0, 0].set_title('元画像')
axes[0, 0].axis('off')

# RGBヒストグラム
for i, color in enumerate(colors):
    hist, bins = np.histogram(img[:, :, i].ravel(), bins=256, range=(0, 256))
    axes[0, 1].plot(bins[:-1], hist, color=color, alpha=0.7, label=color.upper())
axes[0, 1].set_title('RGB ヒストグラム')
axes[0, 1].set_xlabel('画素値')
axes[0, 1].set_ylabel('頻度')
axes[0, 1].legend()
axes[0, 1].grid(alpha=0.3)

# 各チャンネルの分離表示
for i, color in enumerate(colors):
    channel = np.zeros_like(img)
    channel[:, :, i] = img[:, :, i]
    axes[1, i//2 + i%2].imshow(channel)
    axes[1, i//2 + i%2].set_title(f'{color.upper()} チャンネル')
    axes[1, i//2 + i%2].axis('off')

plt.tight_layout()
plt.show()

8-6

8.4.2 グレースケールヒストグラム

グレースケール画像のヒストグラムは、明るさの分布を表します。

from skimage import data
import matplotlib.pyplot as plt
import numpy as np

# 異なる画像のヒストグラムを比較
img1 = data.camera()
img2 = data.coins()

fig, axes = plt.subplots(2, 2, figsize=(12, 10))

# 画像1
axes[0, 0].imshow(img1, cmap='gray')
axes[0, 0].set_title('Camera')
axes[0, 0].axis('off')

axes[1, 0].hist(img1.ravel(), bins=256, range=(0, 256), color='blue', alpha=0.7)
axes[1, 0].set_title('Camera のヒストグラム')
axes[1, 0].set_xlabel('画素値')
axes[1, 0].set_ylabel('頻度')
axes[1, 0].grid(alpha=0.3)

# 画像2
axes[0, 1].imshow(img2, cmap='gray')
axes[0, 1].set_title('Coins')
axes[0, 1].axis('off')

axes[1, 1].hist(img2.ravel(), bins=256, range=(0, 256), color='red', alpha=0.7)
axes[1, 1].set_title('Coins のヒストグラム')
axes[1, 1].set_xlabel('画素値')
axes[1, 1].set_ylabel('頻度')
axes[1, 1].grid(alpha=0.3)

plt.tight_layout()
plt.show()

8-7

8.5 HOG(Histogram of Oriented Gradients)

8.5.1 HOG とは

HOG(Histogram of Oriented Gradients) は、画像の局所的な勾配の方向をヒストグラムとして表現する特徴量です。物体検出(特に人物検出)で広く使われています。

graph LR
    A["画像"] --> B["勾配の計算"]
    B --> C["セルに分割"]
    C --> D["各セルで<br/>勾配方向の<br/>ヒストグラム"]
    D --> E["HOG特徴量<br/>ベクトル"]

    style A fill:#e6f3ff,stroke:#66b3ff,stroke-width:3px
    style B fill:#fff4cc,stroke:#ffcc00,stroke-width:3px
    style C fill:#ccffcc,stroke:#66cc66,stroke-width:3px
    style D fill:#ffffcc,stroke:#ffcc00,stroke-width:3px
    style E fill:#ffcccc,stroke:#ff6666,stroke-width:3px

HOG の特徴:

  • 局所的な形状を捉える
  • 照明変化に強い
  • 人物検出で高い性能

8.5.2 HOG の実装

HOG 特徴量の抽出には scikit-image の feature.hog() 関数を使用します。この関数は、画像から勾配方向のヒストグラムを計算し、特徴ベクトルとして出力します。

from skimage import data, feature, exposure
import matplotlib.pyplot as plt

# サンプル画像の読み込み
img = data.camera()

# HOG特徴量の抽出
fd, hog_image = feature.hog(img, orientations=9, pixels_per_cell=(8, 8),
 cells_per_block=(2, 2), visualize=True)

# 可視化のためコントラストを調整
hog_image_rescaled = exposure.rescale_intensity(hog_image, in_range=(0, 10))

# 表示
fig, axes = plt.subplots(1, 2, figsize=(9, 5))

axes[0].imshow(img, cmap='gray')
axes[0].set_title('元画像')
axes[0].axis('off')

axes[1].imshow(hog_image_rescaled, cmap='gray')
axes[1].set_title('HOG 特徴量(可視化)')
axes[1].axis('off')

plt.tight_layout()
plt.show()

print(f"HOG特徴量の次元: {len(fd)}")

8-8

HOG 特徴量の次元

HOG 特徴量は画像のサイズやパラメータによって次元数が変わります。

  • orientations: 勾配方向のビン数
  • pixels_per_cell: セルのサイズ
  • cells_per_block: ブロックのサイズ

例: 64×128の画像で orientations=9, pixels_per_cell=(8,8), cells_per_block=(2,2) の場合、約3,780次元の特徴ベクトルになります。

8.5.3 HOG のパラメータ

主要なパラメータ:

  • orientations: 勾配方向のビン数(通常 9)
  • pixels_per_cell: セルのサイズ(通常 8×8)
  • cells_per_block: ブロックのサイズ(通常 2×2 セル)
from skimage import data, feature, exposure
import matplotlib.pyplot as plt

# サンプル画像
img = data.astronaut()
img_gray = img.mean(axis=2).astype('uint8')

# 異なるパラメータでHOGを抽出
params = [
    {'orientations': 9, 'pixels_per_cell': (8, 8)},
    {'orientations': 9, 'pixels_per_cell': (16, 16)},
    {'orientations': 3, 'pixels_per_cell': (8, 8)},
]

fig, axes = plt.subplots(2, 2, figsize=(8, 8))

axes[0, 0].imshow(img_gray, cmap='gray')
axes[0, 0].set_title('元画像')
axes[0, 0].axis('off')

for idx, param in enumerate(params):
    fd, hog_image = feature.hog(img_gray, cells_per_block=(2, 2), visualize=True, **param)
    hog_image_rescaled = exposure.rescale_intensity(hog_image, in_range=(0, 10))

    row, col = (idx + 1) // 2, (idx + 1) % 2
    axes[row, col].imshow(hog_image_rescaled, cmap='gray')
    axes[row, col].set_title(f"orientations={param['orientations']}, " f"cell={param['pixels_per_cell']}")
    axes[row, col].axis('off')

plt.tight_layout()
plt.show()

8-9

8.5.4 HOG を使った分類

HOG 特徴量と機械学習を組み合わせることで、画像分類のタスクを実装することができます。以下の画像は、縞模様の方向の分類タスクの実装例です。画像分類の実践的な演習は、第 9 回で行う予定です。

8-10

HOG + 機械学習による分類

HOG 特徴量とロジスティック回帰などの機械学習アルゴリズムを組み合わせることで、画像分類を行うことができます。この組み合わせは、ディープラーニングが普及する前に広く使われていました。

利点:

  • 解釈性が高い(どの特徴を使っているか分かる)
  • 少ないデータでも学習可能
  • 計算コストが低い
  • シンプルで理解しやすい

欠点:

  • ディープラーニングに比べて精度が低い
  • 特徴設計が必要(人間が設計する必要がある)

8.6 その他の特徴抽出手法

8.6.1 LBP(Local Binary Pattern)

LBP(Local Binary Pattern) は、テクスチャを表現する特徴量の一つです。各画素の周辺画素との明暗関係を 2 値パターンで表現します。

LBP の特徴:

  • テクスチャ(模様)の表現に優れる
  • 照明変化に強い
  • 計算が高速
  • 顔認識やテクスチャ分類で利用される

LBP 特徴量の抽出には feature.local_binary_pattern() 関数を使用します。詳細は次のセクションの比較コードで確認できます。

8.7 画像特徴量の比較

異なる特徴抽出手法を比較してみましょう。

from skimage import data, feature, filters
import matplotlib.pyplot as plt
import numpy as np

# サンプル画像の読み込み
img = data.camera()

# 各種特徴抽出
edges_sobel = filters.sobel(img)
edges_canny = feature.canny(img, sigma=2)
corners = feature.corner_harris(img)
hog_fd, hog_image = feature.hog(img, visualize=True)
lbp = feature.local_binary_pattern(img, 8, 1, method='uniform')

# 表示
fig, axes = plt.subplots(2, 3, figsize=(15, 10))

axes[0, 0].imshow(img, cmap='gray')
axes[0, 0].set_title('元画像')
axes[0, 0].axis('off')

axes[0, 1].imshow(edges_sobel, cmap='gray')
axes[0, 1].set_title('エッジ(Sobel)')
axes[0, 1].axis('off')

axes[0, 2].imshow(edges_canny, cmap='gray')
axes[0, 2].set_title('エッジ(Canny)')
axes[0, 2].axis('off')

axes[1, 0].imshow(corners, cmap='gray')
axes[1, 0].set_title('コーナー(Harris)')
axes[1, 0].axis('off')

axes[1, 1].imshow(hog_image, cmap='gray')
axes[1, 1].set_title('HOG特徴量')
axes[1, 1].axis('off')

axes[1, 2].imshow(lbp, cmap='gray')
axes[1, 2].set_title('LBP特徴量')
axes[1, 2].axis('off')

plt.tight_layout()
plt.show()

8-12

特徴抽出手法の使い分け

  • エッジ検出(Sobel, Canny): 物体の輪郭抽出、形状認識
  • コーナー検出(Harris, FAST): 特徴点マッチング、物体追跡
  • HOG: 物体検出(特に人物検出)
  • LBP: テクスチャ認識、顔認識
  • 色ヒストグラム: 色に基づく画像検索、画像分類

8.8 演習問題

演習 8-1: エッジ検出の比較

演習 8-1

scikit-image のサンプル画像(coins)を使って、複数のエッジ検出手法を比較してください。

タスク:

  1. coins 画像を読み込み
  2. Sobel、Prewitt、Scharr、Canny の4種類のエッジ検出を適用
  3. すべての結果を1つのグラフに表示

演習 8-2: HOG 特徴量の抽出

演習 8-2

scikit-image のサンプル画像(chelsea)を使って、HOG 特徴量を抽出し、パラメータを変えて比較してください。

タスク:

  1. chelsea 画像をグレースケールに変換
  2. HOG 特徴量を抽出(デフォルトパラメータ)
  3. 異なる pixels_per_cell で HOG を抽出(例: (4, 4), (8, 8), (16, 16))
  4. 各パラメータでの HOG 可視化結果を比較表示
  5. 特徴量の次元数を確認

8.10 まとめ

本章では、古典的な特徴抽出手法について学びました。

第 8 回のまとめ

特徴抽出の基礎

  • 生の画像データから有用な情報を抽出
  • 次元削減、不変性、解釈性の利点
  • 古典的手法とディープラーニングの違い

エッジ検出

  • Sobel フィルタ: 基本的なエッジ検出
  • Canny: 高度なエッジ検出、ノイズに強い
  • Prewitt, Scharr: Sobel の変種

コーナー検出

  • Harris: 古典的なコーナー検出
  • FAST: 高速なコーナー検出

ヒストグラム特徴

  • 色ヒストグラム: 色の分布を表現
  • グレースケールヒストグラム: 明るさの分布

HOG(Histogram of Oriented Gradients)

  • 局所的な勾配の方向をヒストグラムで表現
  • 物体検出(特に人物検出)で広く使用
  • パラメータ調整が重要

その他の手法

  • LBP: テクスチャ認識、顔認識などに利用される

特徴抽出の意義

  • ディープラーニング以前の主要な画像認識手法
  • 解釈性が高く、少ないデータでも動作する
  • 現在でも特定の用途で有用