Skip to content

7. 画像処理の基礎

近年、機械学習の発展により、画像認識技術は飛躍的に進化しています。本講義では、まず画像データの基本的な扱い方を学び、次回以降で古典的な特徴抽出や CNN(畳み込みニューラルネットワーク)について学んでいきます。

第 7 回では、機械学習における画像処理の基礎について学びます。

7.1 画像データの基礎

7.1.1 デジタル画像とは

デジタル画像は、画素(ピクセル) と呼ばれる小さな点の集まりで表現されます。各画素は色や明るさの情報を持っており、これらを格子状に配置することで画像を構成します。

graph LR
    A["デジタル画像<br/>(例: 1920×1080)"]
    B["画素の集まり<br/>(ピクセル配列)"]
    C["各画素の値<br/>(R, G, B: 0-255)"]
    D["数値データ<br/>(行列として処理)"]

    A --> B --> C --> D

    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

画像の主要な要素

  • 画素(ピクセル):画像を構成する最小単位
  • 解像度:画像の幅 × 高さのピクセル数(例:1920×1080)
  • 色深度:各画素で表現できる色の数(例:8bit = 256 色)

7.1.2 画像の種類

デジタル画像は、色の表現方法によって主に 3 つの種類に分けられます。

graph TB
    A["デジタル画像の種類"]

    B["グレースケール画像<br/>(1チャンネル)<br/>例: 白黒写真"]
    C["カラー画像 RGB<br/>(3チャンネル)<br/>例: 通常の写真"]
    D["RGBA画像<br/>(4チャンネル)<br/>例: PNG透過画像"]

    A --> B & C & D

    B --> B1["明るさのみ<br/>0(黒) ~ 255(白)"]
    C --> C1["Red, Green, Blue<br/>各 0~255 の組み合わせ"]
    D --> D1["RGB + Alpha透明度<br/>0(透明) ~ 255(不透明)"]

    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 D fill:#ffcccc,stroke:#ff6666,stroke-width:2px
    style B1 fill:#e6ffe6,stroke:#66cc66,stroke-width:1px
    style C1 fill:#fffef0,stroke:#ffcc00,stroke-width:1px
    style D1 fill:#ffe6e6,stroke:#ff6666,stroke-width:1px

グレースケール画像

  • 各画素が 1 つの値(明るさ)を持つ
  • 0(黒)から 255(白)の範囲
  • 形状:(高さ, 幅)

カラー画像(RGB)

  • 各画素が R(赤)、G(緑)、B(青)の 3 つの値を持つ
  • 各チャンネルが 0〜255 の範囲
  • 形状:(高さ, 幅, 3)

RGBA 画像

  • RGB + Alpha(透明度)
  • 形状:(高さ, 幅, 4)

7.1.3 Python での画像の扱い

Python で画像を扱うには、主に以下のライブラリを使用します。

主要なライブラリ

  • OpenCV (cv2):読み込み・保存から高度な画像処理・コンピュータビジョンまで扱う実務標準ライブラリ
  • Pillow (PIL):軽量な画像処理ライブラリ。読み込み・保存・リサイズなど基本操作に向く
  • scikit-image:科学計算向けの画像処理ライブラリ。実験用のサンプル画像が便利
  • matplotlib:画像の表示
  • NumPy:画像データの数値計算

Pillow と OpenCV の使い分け

Python で画像を扱うライブラリには Pillow と OpenCV の 2 つが広く使われています。シンプルな読み込み・リサイズ・保存だけなら Pillow の方がコード量が少なく済むこともあります。

本講義では主に以下の理由から、OpenCV を使用します。

  • しきい値処理・フィルタ・幾何変換などの 画像処理機能を一通り内蔵 している
  • コンピュータビジョン分野の標準的なライブラリ で、物体検出など機械学習寄りの処理まで一貫して扱える
  • 返り値が NumPy 配列 で、後段の処理(NumPy・scikit-learn など)と直接つなげやすい
graph TB
    A["画像ファイル<br/>(PNG, JPEG, etc.)"]
    B["読み込み:<br/>OpenCV (cv2.imread)"]
    C["NumPy配列に変換<br/>(height, width, channels)"]
    D["画像処理・演算<br/>(NumPy / OpenCV)"]
    E1["表示: matplotlib"]
    E2["保存: OpenCV (cv2.imwrite)"]

    A --> B --> C --> D
    D --> E1
    D --> E2

    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 E1 fill:#ffcccc,stroke:#ff6666,stroke-width:2px
    style E2 fill:#ffe6f0,stroke:#ff99cc,stroke-width:2px

7.2 画像の読み込みと表示

7.2.1 OpenCV による画像の読み込み

OpenCV(cv2) は、Python で画像を扱う実務標準のライブラリです。画像を読み込むには cv2.imread() 関数を使用します。

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

# 画像の読み込み(NumPy 配列として返る)
img = cv2.imread('sample.jpg')

# 画像情報の確認
print(f"配列の形状: {img.shape}")  # (高さ, 幅, チャンネル数)
print(f"データ型: {img.dtype}")
print(f"値の範囲: {img.min()} ~ {img.max()}")

# OpenCV は BGR 順序で読み込むため、表示前に RGB に変換
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

# 画像の表示
plt.figure(figsize=(8, 6))
plt.imshow(img_rgb)
plt.title('元の画像')
plt.axis('off')
plt.show()

OpenCV の BGR 順序に注意

OpenCV の cv2.imread() は色チャンネルを BGR(青・緑・赤) の順序で読み込みます。これは歴史的な経緯による仕様です。matplotlib の imshow() は RGB 順序 を前提とするため、そのまま表示すると色が反転して見えます。

この問題を解決するには、表示前に cv2.cvtColor() で変換します。

img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

一方、本講義で多用する skimage.data のサンプル画像(7.7 節)は RGB 順序 で返るため、cv2 で処理しつつ matplotlib でそのまま表示できます。

OpenCV のインストール

OpenCV をインストールするには、以下のコマンドを実行します。

pip install opencv-python

※ CHIKUWA Editor にはインストール済みです。

7.2.2 サンプル画像の生成

実習用に、NumPy を使ってサンプル画像を生成してみましょう。

import numpy as np
import matplotlib.pyplot as plt

# グレースケール画像の生成(グラデーション)
height, width = 200, 300
gradient = np.linspace(0, 255, width, dtype=np.uint8)
gray_img = np.tile(gradient, (height, 1))

# カラー画像の生成(RGB)
color_img = np.zeros((200, 300, 3), dtype=np.uint8)
color_img[:, :100, 0] = 255  # 左側を赤
color_img[:, 100:200, 1] = 255  # 中央を緑
color_img[:, 200:, 2] = 255  # 右側を青

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

axes[0].imshow(gray_img, cmap='gray')
axes[0].set_title('グレースケール画像')
axes[0].axis('off')

axes[1].imshow(color_img)
axes[1].set_title('カラー画像(RGB)')
axes[1].axis('off')

plt.tight_layout()
plt.show()

print(f"グレースケール画像の形状: {gray_img.shape}")
print(f"カラー画像の形状: {color_img.shape}")

7-1

7.3 画素と色空間

7.3.1 RGB 色空間

RGB 色空間 は、赤(Red)、緑(Green)、青(Blue)の 3 つの原色を組み合わせて色を表現する方法です。

graph TB
    A["RGB 色空間<br/>(加法混色)"]
    B["R: 0-255<br/>(赤チャンネル)"]
    C["G: 0-255<br/>(緑チャンネル)"]
    D["B: 0-255<br/>(青チャンネル)"]
    E["色の組み合わせ<br/>256³ = 16,777,216 色"]

    A --> B & C & D
    B & C & D --> E

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

主要な色の例

色 R G B
黒 0 0 0
白 255 255 255
赤 255 0 0
緑 0 255 0
青 0 0 255
黄 255 255 0
紫 255 0 255
水色 0 255 255
import numpy as np
import matplotlib.pyplot as plt

# 表示する色(色名とRGB値)
colors = {
    '黒': [0, 0, 0],
    '白': [255, 255, 255],
    '赤': [255, 0, 0],
    '緑': [0, 255, 0],
    '青': [0, 0, 255],
    '黄': [255, 255, 0],
    '紫': [255, 0, 255],
    '水色': [0, 255, 255]
}

# 2行4列で8色を表示
fig, axes = plt.subplots(2, 4, figsize=(12, 6))
axes = axes.ravel()

# 各色を順番に表示
for i, (name, rgb) in enumerate(colors.items()):
    img = np.full((100, 100, 3), rgb, dtype=np.uint8)
    axes[i].imshow(img)
    axes[i].set_title(f'{name}\nRGB{tuple(rgb)}')
    axes[i].axis('off')

plt.tight_layout()
plt.show()

7-2

7.3.2 グレースケール変換

カラー画像をグレースケール画像に変換する方法はいくつかあります。

  1. 単純平均法:Gray = (R + G + B) / 3
  2. 加重平均法:Gray = 0.299×R + 0.587×G + 0.114×B ※ 人間の視覚特性を考慮した重み付け
  3. OpenCV の変換:cv2.cvtColor() 関数で cv2.COLOR_RGB2GRAY を指定(内部で加重平均を計算)
import cv2
import numpy as np
import matplotlib.pyplot as plt

# サンプル画像の作成
color_img = np.zeros((200, 300, 3), dtype=np.uint8)
color_img[:, :100, 0] = 255  # 赤
color_img[:, 100:200, 1] = 255  # 緑
color_img[:, 200:, 2] = 255  # 青

# 方法1: 単純平均法
gray_avg = np.mean(color_img, axis=2).astype(np.uint8)

# 方法2: 加重平均法
gray_weighted = (0.299 * color_img[:, :, 0] +
                0.587 * color_img[:, :, 1] +
                0.114 * color_img[:, :, 2]).astype(np.uint8)

# 方法3: OpenCV を使用(内部で加重平均の計算)
gray_cv = cv2.cvtColor(color_img, cv2.COLOR_RGB2GRAY)

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

axes[0, 0].imshow(color_img)
axes[0, 0].set_title('元のカラー画像')
axes[0, 0].axis('off')

axes[0, 1].imshow(gray_avg, cmap='gray')
axes[0, 1].set_title('単純平均法')
axes[0, 1].axis('off')

axes[1, 0].imshow(gray_weighted, cmap='gray')
axes[1, 0].set_title('加重平均法')
axes[1, 0].axis('off')

axes[1, 1].imshow(gray_cv, cmap='gray')
axes[1, 1].set_title('cv2.cvtColor')
axes[1, 1].axis('off')

plt.tight_layout()
plt.show()

7-3

色空間の使い分け

  • RGB:画像の表示、一般的な画像処理、ニューラルネットワークの入力
  • グレースケール:形状認識、高速処理、計算量削減

7.4 機械学習のための画像前処理

機械学習モデルに画像を入力する前に、適切な前処理を行うことが重要です。このセクションでは、ニューラルネットワークで画像を扱う際に必須となる処理を学びます。

7.4.1 画像のリサイズ

ニューラルネットワークは固定サイズの入力を必要とするため、画像のリサイズは必須の前処理です。画像のリサイズには cv2.resize() 関数を使用します。

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

# サンプル画像の読み込み
original = data.astronaut()  # (512, 512, 3)

# 画像をリサイズ(cv2.resize の引数は (幅, 高さ) の順序)
resized = cv2.resize(original, (224, 224))  # よく使われるサイズ

print(f"元の画像: {original.shape}")
print(f"リサイズ後: {resized.shape}")

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

axes[0].imshow(original)
axes[0].set_title(f'元の画像 {original.shape[:2]}')
axes[0].axis('off')

axes[1].imshow(resized)
axes[1].set_title(f'リサイズ後 {resized.shape[:2]}')
axes[1].axis('off')

plt.tight_layout()
plt.show()

7-4

よく使われる画像サイズ

  • 224×224:VGG、ResNetなどの標準サイズ
  • 299×299:Inception系のネットワーク
  • 32×32:CIFAR-10データセット
  • 28×28:MNISTデータセット(手書き数字)

7.4.2 正規化(0-1 変換)

画像の画素値(0-255)を(0-1)に正規化するには、以下のようにします。

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

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

# 正規化: 0-255 → 0-1
img_normalized = img / 255.0

print(f"元の画像の範囲: {img.min()} ~ {img.max()}")
print(f"元の画像のデータ型: {img.dtype}")
print(f"正規化後の範囲: {img_normalized.min():.2f} ~ {img_normalized.max():.2f}")
print(f"正規化後のデータ型: {img_normalized.dtype}")

# 表示(matplotlib は 0-1 と 0-255 の両方に対応)
fig, axes = plt.subplots(1, 2, figsize=(12, 5))

axes[0].imshow(img, cmap='gray')
axes[0].set_title(f'元の画像 (0-255)\n{img.dtype}')
axes[0].axis('off')

axes[1].imshow(img_normalized, cmap='gray')
axes[1].set_title(f'正規化後 (0-1)\n{img_normalized.dtype}')
axes[1].axis('off')

plt.tight_layout()
plt.show()

正規化の重要性

正規化が必要な理由

  1. 学習の安定性:大きな値(0-255)はニューラルネットワークの学習を不安定にする
  2. 勾配の制御:適切な範囲の値により、勾配消失・爆発を防ぐ
  3. 収束速度:正規化により学習が速く収束する

標準的な正規化方法

  • img / 255.0 → [0, 1] の範囲
  • (img - mean) / std → 平均0、標準偏差1(標準化)

7.4.3 データ拡張の基礎

データ拡張は、訓練データを人工的に増やす技術です。過学習を防ぎ、モデルの汎化性能を向上させることができます。OpenCV では cv2.flip() で反転、cv2.getRotationMatrix2D() と cv2.warpAffine() で回転を行います。

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

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

# 15 度回転用のアフィン変換行列を作成
h, w = img.shape[:2]
M = cv2.getRotationMatrix2D((w / 2, h / 2), 15, 1.0)  # (中心, 角度, 拡大率)

# データ拡張の例
augmented = {
    '元の画像': img,
    '左右反転': cv2.flip(img, 1),                    # 1=水平、0=垂直、-1=両方
    '回転 15度': cv2.warpAffine(img, M, (w, h)),
    '明るさ +50': np.clip(img + 50, 0, 255).astype(np.uint8),
}

# 表示
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
axes = axes.ravel()

for idx, (title, aug_img) in enumerate(augmented.items()):
    axes[idx].imshow(aug_img)
    axes[idx].set_title(title)
    axes[idx].axis('off')

plt.tight_layout()
plt.show()

7-5

データ拡張のポイント

よく使われる拡張手法

  • 左右反転:最も一般的な拡張
  • 回転:物体の向きに依存しない認識
  • 明るさ調整:照明条件の変化に対応
  • クロッピング:ランダムな位置から切り取り

注意点

  • 拡張は訓練データのみに適用(テストデータには適用しない)
  • タスクに応じて適切な拡張を選択(例:文字認識では上下反転は不適切)

7.5 しきい値処理

しきい値処理 は、画像を二値化(白と黒の 2 値に分ける)する基本的な画像処理手法です。物体検出や領域分割の前処理として広く使われます。

graph LR
    A["グレースケール画像<br/>(画素値: 0-255)"]
    B["しきい値と比較<br/>(例: T=128)"]
    C{"画素値 > T?"}
    D["白 (255)<br/>(前景)"]
    E["黒 (0)<br/>(背景)"]
    F["二値画像<br/>(0 or 255)"]

    A --> B --> C
    C -->|Yes| D
    C -->|No| E
    D --> F
    E --> F

    style A fill:#e6f3ff,stroke:#66b3ff,stroke-width:3px
    style B fill:#fff4cc,stroke:#ffcc00,stroke-width:3px
    style C fill:#ffcccc,stroke:#ff6666,stroke-width:3px
    style D fill:#ffffff,stroke:#999999,stroke-width:2px
    style E fill:#888888,stroke:#666666,stroke-width:2px,color:#fff
    style F fill:#ccffcc,stroke:#66cc66,stroke-width:3px

しきい値処理の用途:

  • 物体と背景の分離
  • 文字認識の前処理
  • 物体の輪郭抽出
  • 特定の明るさ範囲の抽出

7.5.1 固定しきい値処理

OpenCV で固定しきい値の二値化を行うには cv2.threshold() 関数を使用します。返り値は (使用したしきい値, 二値化された画像) のタプルです。

import cv2
import matplotlib.pyplot as plt
from skimage import data

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

# 固定しきい値で二値化(しきい値 = 128、超えた画素を 255 に)
threshold = 128
ret, binary = cv2.threshold(img, threshold, 255, cv2.THRESH_BINARY)

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

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

axes[1].imshow(binary, cmap='gray')
axes[1].set_title(f'しきい値 = {threshold}')
axes[1].axis('off')

plt.tight_layout()
plt.show()

7-6

7.5.2 大津の二値化(Otsu's method)

大津の二値化 は、画像のヒストグラムから自動的に最適なしきい値を決定する手法です。クラス内分散を最小化(クラス間分散を最大化)することで、最適なしきい値を求めます。OpenCV では cv2.threshold() の第 4 引数に cv2.THRESH_OTSU を追加し、第 2 引数のしきい値は 0 を渡します(自動決定される)。

graph TB
    A["ヒストグラム分析<br/>(画素値の分布)"]
    B["全候補を探索<br/>(T = 0 ~ 255)"]
    C["分散計算<br/>(クラス間分散σ²)"]
    D["最大値を選択<br/>(max σ²)"]
    E["最適しきい値<br/>(自動決定)"]

    A --> B --> C --> D --> E

    style A fill:#e6f3ff,stroke:#66b3ff,stroke-width:3px
    style B fill:#fff4cc,stroke:#ffcc00,stroke-width:2px
    style C fill:#ffffcc,stroke:#ffcc00,stroke-width:2px
    style D fill:#ffcccc,stroke:#ff6666,stroke-width:2px
    style E fill:#ccffcc,stroke:#66cc66,stroke-width:3px
import cv2
import matplotlib.pyplot as plt
from skimage import data

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

# 大津の二値化でしきい値を自動決定
ret, binary_otsu = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)

print(f"大津の二値化による最適しきい値: {ret}")

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

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

axes[1].imshow(binary_otsu, cmap='gray')
axes[1].set_title(f'大津の二値化 (しきい値 = {ret:.1f})')
axes[1].axis('off')

plt.tight_layout()
plt.show()

7-7

しきい値処理のポイント

しきい値の選び方

  • 固定しきい値:照明が均一で、対象物と背景のコントラストが明確な場合
  • 大津の二値化:最適なしきい値を自動で決定したい場合

注意点

  • しきい値処理の前に、ノイズ除去(ガウシアンフィルタなど)を適用すると効果的
  • カラー画像の場合は、グレースケールに変換してから処理する

7.6 画像フィルタリング

画像のノイズを除去するために、フィルタリング(平滑化)を行います。機械学習の前処理としても重要です。

7.6.1 ガウシアンフィルタ

ガウシアンフィルタ は、画像のノイズを除去しながらエッジを比較的保持するフィルタです。OpenCV では cv2.GaussianBlur() 関数を使用します。

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

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

# ノイズを追加
noise = np.random.normal(0, 25, img.shape)
noisy_img = np.clip(img + noise, 0, 255).astype(np.uint8)

# ガウシアンフィルタでノイズ除去
# 引数: 画像, カーネルサイズ (0,0 で sigma から自動計算), sigma
filtered = cv2.GaussianBlur(noisy_img, (0, 0), 2)

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

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

axes[1].imshow(noisy_img, cmap='gray')
axes[1].set_title('ノイズあり')
axes[1].axis('off')

axes[2].imshow(filtered, cmap='gray')
axes[2].set_title('ガウシアンフィルタ適用後')
axes[2].axis('off')

plt.tight_layout()
plt.show()

7-8

ガウシアンフィルタのパラメータ

  • カーネルサイズ:(0, 0) を指定すると sigma から自動計算される。明示する場合は奇数(例:(5, 5))
  • sigma:フィルタの強さ(大きいほど強くぼかす)
    • sigma=1:弱いぼかし
    • sigma=3:中程度のぼかし
    • sigma=5:強いぼかし

7.7 scikit-image のサンプル画像

scikit-image は、画像処理のための Python ライブラリです。本講義では主に OpenCV を使いますが、scikit-image の data モジュールに含まれる実験用のサンプル画像はファイル不要で読み込めるため、演習や実習で活用します。

主なサンプル画像

関数名 説明 サイズ 種類 用途例
data.camera() カメラマンの写真 512×512 グレースケール 一般的な画像処理の実験
data.astronaut() 宇宙飛行士の写真 512×512×3 カラー(RGB) カラー画像処理、チャンネル分離
data.coins() 複数のコインの写真 303×384 グレースケール しきい値処理、物体検出
data.chelsea() 猫の写真 300×451×3 カラー(RGB) データ拡張、リサイズ
data.coffee() コーヒーカップの写真 400×600×3 カラー(RGB) 色空間の変換
data.moon() 月面の写真 512×512 グレースケール コントラスト調整
data.text() テキストの画像 172×448 グレースケール 二値化、文字認識
data.rocket() ロケット打ち上げの写真 427×640×3 カラー(RGB) リサイズ、データ拡張、物体検出
data.horse() 馬のシルエット 328×400 二値(bool) 形状解析、マスク処理
data.immunohistochemistry() 免疫組織化学染色の医療画像 512×512×3 カラー(RGB) 色空間の変換、医療画像処理
data.checkerboard() チェッカーボード(合成) 200×200 グレースケール 特徴点検出、コーナー検出

使用例

from skimage import data
import matplotlib.pyplot as plt

# サンプル画像の読み込み(RGB 順序で返される)
img = data.astronaut()

# 画像の表示
plt.figure(figsize=(8, 6))
plt.imshow(img)
plt.title('Astronaut')
plt.axis('off')
plt.show()

# 画像情報の確認
print(f"形状: {img.shape}")
print(f"データ型: {img.dtype}")
print(f"値の範囲: {img.min()} ~ {img.max()}")

scikit-image と OpenCV の組み合わせ

skimage.data は RGB 順序 で画像を返すため、cv2.cvtColor などで処理した後にそのまま matplotlib で表示できます。cv2.imread で読み込んだ場合は BGR → RGB 変換が必要です。

7.8 演習問題

演習 7-1: グレースケール変換の比較

演習 7-1(初級)

scikit-image のサンプル画像(coffee)を使って、グレースケール変換の違いを確認してください。

タスク

  1. coffee 画像を読み込み、サイズと形状を確認
  2. 単純平均法でグレースケールに変換
  3. OpenCV の cv2.cvtColor でグレースケールに変換(cv2.COLOR_RGB2GRAY)
  4. 元のカラー画像、単純平均法、cv2.cvtColor の 3 つを 1 行 3 列で並べて表示
テンプレートコード
import cv2
import numpy as np
import matplotlib.pyplot as plt
from skimage import data

# 1. coffee 画像を読み込み、サイズと形状を確認

# 2. 単純平均法でグレースケールに変換

# 3. cv2.cvtColor でグレースケールに変換

# 4. 元のカラー画像、単純平均法、cv2.cvtColor の 3 つを 1 行 3 列で並べて表示

演習 7-2: ノイズ除去とフィルタ強度の比較

演習 7-2(初級)

scikit-image のサンプル画像(chelsea, 猫のカラー画像)にガウシアンノイズを追加し、cv2.GaussianBlur の sigma を 3 通り変えてノイズ除去の強度の違いを比較してください。

タスク

  1. chelsea 画像を読み込み(カラー RGB)
  2. ガウシアンノイズを追加(平均 0、標準偏差 25、np.random.seed(42))
  3. cv2.GaussianBlur を sigma=1, 3, 5 の 3 通りで適用
  4. ノイズあり画像と 3 通りのフィルタ結果(合計 4 枚)を 2×2 で並べて表示
  5. sigma を大きくしたときに何が起こるか(ノイズの減り方・ディテールの保持)をコメントで考察
テンプレートコード
import cv2
import numpy as np
import matplotlib.pyplot as plt
from skimage import data

# 1. chelsea 画像を読み込み

# 2. ガウシアンノイズを追加(平均 0、標準偏差 25、seed=42)

# 3. sigma=1, 3, 5 で cv2.GaussianBlur を適用

# 4. ノイズあり + フィルタ 3 通りを 2×2 で並べて表示

# 5. 考察(sigma を大きくしたときの影響)をコメントで記述

演習 7-3: しきい値処理の実践

演習 7-3(初級)

scikit-image のサンプル画像(coins)を使って、しきい値処理を実践してください。

タスク

  1. coins 画像を読み込み
  2. cv2.threshold を cv2.THRESH_BINARY で固定しきい値(120)による二値化
  3. cv2.threshold を cv2.THRESH_BINARY | cv2.THRESH_OTSU で大津の二値化、しきい値も表示
  4. 元の画像、固定しきい値の結果、大津の二値化の結果を 1 行 3 列で並べて表示
テンプレートコード
import cv2
import matplotlib.pyplot as plt
from skimage import data

# 1. coins 画像を読み込み

# 2. 固定しきい値(120)で二値化

# 3. 大津の二値化、最適しきい値を表示

# 4. 元の画像、固定しきい値、大津の二値化の 3 つを並べて表示

演習 7-4: 文字画像の二値化

演習 7-4(中級)

scikit-image のサンプル画像(page)は、スキャンしたページの画像で、左側が暗く右側が明るいという明るさのムラがあります。この画像に対して、以下の 3 通りで二値化を行い、結果を比較してください。

  • (a) 大津の二値化
  • (b) ガウシアンフィルタ + 大津の二値化
  • (c) 適応的しきい値処理 cv2.adaptiveThreshold(使い方・パラメータは公式ドキュメントを参照)
テンプレートコード
import cv2
import matplotlib.pyplot as plt
from skimage import data

# 1. page 画像を読み込む

# 2-a. 大津の二値化のみ

# 2-b. ガウシアンフィルタ + 大津の二値化

# 2-c. 適応的しきい値処理

# 3. 元画像と 3 通りの結果を 2×2 で並べて表示

7.9 まとめ

本章では、画像処理の基礎について学びました。

第 7 回のまとめ

画像データの基礎

  • デジタル画像は画素(ピクセル)の集まり
  • グレースケール(1チャンネル)とカラー画像(RGB: 3チャンネル)
  • Python では NumPy 配列として画像を扱う

画像の読み込みと表示

  • OpenCV (cv2):読み込み・保存・画像処理の実務標準ライブラリ
  • matplotlib:画像の表示
  • NumPy:画像データの数値計算
  • OpenCV は BGR 順序 で読み込むため、matplotlib で表示する前に cv2.cvtColor で RGB に変換する

色空間

  • RGB:赤・緑・青の3原色で表現(ニューラルネットワークの標準入力)
  • グレースケール:明るさのみで表現(計算量削減、形状認識)

機械学習のための画像前処理

  • リサイズ:ニューラルネットワークの固定サイズ入力に対応(224×224など)
  • 正規化:0-255 → 0-1 の変換(学習の安定化、必須の処理)
  • 標準化:平均0、標準偏差1への変換
  • データ拡張:左右反転、回転、明るさ調整で訓練データを増やす

しきい値処理

  • 固定しきい値:cv2.threshold に cv2.THRESH_BINARY を指定
  • 大津の二値化:cv2.THRESH_BINARY | cv2.THRESH_OTSU で最適なしきい値を自動決定
  • 物体と背景の分離、領域分割の前処理として重要

フィルタリング

  • ガウシアンフィルタ (cv2.GaussianBlur):ノイズ除去とエッジ保持のバランスが良い

機械学習で最も重要なポイント

  1. 正規化(0-1変換)は必須:img / 255.0
  2. リサイズ:モデルに合わせたサイズに統一
  3. データ拡張:訓練データのみに適用
  4. グレースケール変換:計算量削減が必要な場合

次回(第 8 回)は、古典的な特徴抽出手法(エッジ検出、HOG など)について学びます。