📋 このステップで学ぶこと
- デジタル画像の表現(ピクセル、解像度、チャンネル)
- RGB、RGBA、グレースケールの理解
- PIL/Pillow、OpenCV、matplotlibでの画像読み込み
- 画像の基本操作(リサイズ、クロップ、回転、反転)
- 色空間変換(RGB↔HSV)
- NumPy配列としての画像処理
練習問題: 5問
🎯 1. デジタル画像とは何か
STEP 1で「コンピュータにとって画像は数字の羅列」と学びました。このセクションでは、その「数字」がどのように構成されているかを詳しく見ていきます。
1-1. ピクセル(画素)とは?
デジタル画像は、ピクセル(画素)の集合です。「ピクセル」とは「Picture Element(画像要素)」の略で、画像を構成する最小単位です。
📷 例え話:モザイクアート
デジタル画像は、小さなタイルを並べたモザイクアートのようなものです。
1つのタイル = 1ピクセル
・近くで見ると、タイルの集まりに見える
・遠くから見ると、1枚の絵に見える
タイルの数 = 解像度
・タイルが多い(高解像度)→ 細かい絵が描ける
・タイルが少ない(低解像度)→ 粗い絵になる
【ピクセルの基本】
ピクセル(Pixel)= Picture Element(画像要素)の略
・画像を構成する「最小単位」
・1ピクセル = 1つの点
・各ピクセルは「色」の情報を持つ
例:
・白いピクセル:RGB(255, 255, 255)
・黒いピクセル:RGB(0, 0, 0)
・赤いピクセル:RGB(255, 0, 0)
1-2. 解像度(Resolution)とは?
解像度は、画像の「幅」×「高さ」のピクセル数を表します。解像度が高いほど、細かい表現が可能になります。
【解像度の例】
・640×480 = 307,200ピクセル(VGA)
→ 約30万ピクセル
・1920×1080 = 2,073,600ピクセル(フルHD)
→ 約207万ピクセル(1080pとも呼ばれる)
・3840×2160 = 8,294,400ピクセル(4K)
→ 約829万ピクセル(フルHDの4倍)
高解像度のメリット:細かい表現が可能、拡大しても綺麗
高解像度のデメリット:データサイズが大きい、処理に時間がかかる
🎯 画像サイズとファイルサイズの違い
この2つは別物なので注意が必要です。
解像度(画像サイズ):ピクセル数(例:1920×1080)
ファイルサイズ:圧縮後のバイト数(例:500KB)
JPEG、PNGなどの形式は圧縮されているため、ファイルサイズは小さくなります。
プログラムで読み込むと、メモリ上では「解像度×チャンネル数」分のサイズになります。
🎨 2. 色の表現 – RGB、RGBA、グレースケール
コンピュータはどのように「色」を表現しているのでしょうか?主な3つの形式を学びましょう。
2-1. RGB(Red, Green, Blue)
最も一般的な色の表現方法です。光の三原色(赤・緑・青)を混ぜ合わせて、様々な色を表現します。
🔴🟢🔵 RGBの仕組み
各ピクセルが、赤(R)、緑(G)、青(B)の3つの値を持ちます。
それぞれの値は 0〜255 の256段階(8ビット)です。
【RGBの色の例】
各色の値は 0(暗い)〜 255(明るい)の範囲
■ 基本の色
・赤: RGB(255, 0, 0) ← 赤だけMAX
・緑: RGB(0, 255, 0) ← 緑だけMAX
・青: RGB(0, 0, 255) ← 青だけMAX
■ 混ぜ合わせた色
・黄色: RGB(255, 255, 0) ← 赤+緑
・紫: RGB(255, 0, 255) ← 赤+青
・水色: RGB(0, 255, 255) ← 緑+青
■ 白と黒
・白: RGB(255, 255, 255) ← 全部MAX(光を全部混ぜると白)
・黒: RGB(0, 0, 0) ← 全部0(光がないと黒)
■ 灰色
・灰色: RGB(128, 128, 128) ← 全部同じ値で灰色になる
なぜRGB(255, 255, 255)が白になるのか、疑問に思うかもしれません。絵の具を混ぜると黒っぽくなりますよね。
💡 光の混色と絵の具の混色の違い
RGBは「光」の混色(加法混色)
・光を混ぜると、どんどん明るくなる
・全部混ぜると → 白
・ディスプレイ、プロジェクターなどで使用
絵の具は「色素」の混色(減法混色)
・色素を混ぜると、どんどん暗くなる
・全部混ぜると → 黒っぽくなる
・印刷物(CMYK)で使用
2-2. RGBA(RGB + Alpha)
RGBに透明度(Alpha)を加えたものです。Webデザインや画像合成でよく使われます。
【RGBAとは】
RGB + アルファチャンネル(透明度)
・R(赤): 0〜255
・G(緑): 0〜255
・B(青): 0〜255
・A(透明度):0〜255
├─ 0 = 完全に透明(見えない)
├─ 128 = 半透明
└─ 255 = 不透明(普通の状態)
【用途】
・PNG画像(透過PNG)
・UI/グラフィックデザイン(ボタンの半透明効果など)
・画像合成(背景と人物を重ねるなど)
2-3. グレースケール(Grayscale)
白〜黒の濃淡だけで表現した画像です。「白黒写真」のようなものです。
⚪⚫ グレースケールとは
1ピクセル = 1つの値(0〜255)で表現
・0 = 黒
・128 = 灰色(中間)
・255 = 白
【RGB → グレースケール変換式】
単純な平均ではなく、人間の目の感度を考慮した重み付け平均を使う
Gray = 0.299×R + 0.587×G + 0.114×B
なぜこの重みなのか?
・人間の目は「緑」に最も敏感
・次いで「赤」、「青」の順
・この重みで変換すると、人間の目に自然に見える
💡 なぜグレースケールを使うのか?
メリット:
・データサイズが1/3:RGBは3チャンネル、グレースケールは1チャンネル
・処理が速い:計算量が減る
・色が不要なタスク:エッジ検出、文字認識など
使用例:
・文字認識(OCR)
・エッジ検出
・古典的CV手法(SIFT、HOGなど)
ただし、現代の深層学習ではRGBを直接使うことが多いです。色の情報も有用な特徴になるからです。
2-4. チャンネル(Channel)について
「チャンネル」は画像の「次元」のようなものです。RGBなら3チャンネル、グレースケールなら1チャンネルです。
【チャンネルとは】
画像データの「層」のこと
・グレースケール:1チャンネル(H × W × 1)または(H × W)
・RGB: 3チャンネル(H × W × 3)
・RGBA: 4チャンネル(H × W × 4)
例:640×480のRGB画像
→ 640 × 480 × 3 = 921,600個の値
→ NumPy配列では shape=(480, 640, 3)
※注意:(高さ, 幅, チャンネル)の順番!(幅, 高さではない)
| 形式 |
チャンネル数 |
値の範囲 |
主な用途 |
| グレースケール |
1 |
0〜255 |
文字認識、エッジ検出 |
| RGB |
3 |
各0〜255 |
一般的な画像処理、深層学習 |
| RGBA |
4 |
各0〜255 |
透過画像、画像合成 |
💻 3. Pythonでの画像読み込み
Pythonには、画像を扱うための複数のライブラリがあります。それぞれの特徴を理解して、適切に使い分けましょう。
3-1. 主要ライブラリの比較
| ライブラリ |
特徴 |
チャンネル順序 |
主な用途 |
| PIL/Pillow |
シンプル、初心者向け |
RGB |
基本的な画像操作 |
| OpenCV |
高速、機能豊富 |
BGR(注意!) |
コンピュータビジョン |
| matplotlib |
可視化に強い |
RGB |
画像表示、グラフ作成 |
⚠️ 超重要:OpenCVはBGR順序!
OpenCVは歴史的な理由で、BGR(青、緑、赤)の順序で画像を読み込みます。
一般的なRGB順序とは逆なので、色がおかしくなる原因になります。
対処法:OpenCVで読み込んだら、すぐにRGBに変換する習慣をつけましょう。
3-2. PIL/Pillow – シンプルで使いやすい
最初に学ぶならPillowがおすすめです。直感的で分かりやすい書き方ができます。
ステップ1:ライブラリのインポート
# PIL(Pillow)からImageクラスをインポート
# Image:画像を読み込み・保存・加工するためのクラス
from PIL import Image
# NumPyもインポート(配列操作に使用)
import numpy as np
ステップ2:画像の読み込み
# Image.open()で画像ファイルを開く
# 引数:画像ファイルのパス(文字列)
img = Image.open(‘sample.jpg’)
# この時点ではまだ「PIL Imageオブジェクト」であり、
# NumPy配列ではないことに注意
ステップ3:画像情報の確認
# size属性で画像のサイズを取得
# 注意:Pillowは (幅, 高さ) の順番で返す
print(img.size) # 出力例: (640, 480)
# mode属性で色モードを確認
# ‘RGB’ = カラー画像
# ‘L’ = グレースケール
# ‘RGBA’ = 透過あり
print(img.mode) # 出力例: ‘RGB’
ステップ4:NumPy配列への変換
# np.array()でNumPy配列に変換
# これにより、ピクセル値を直接操作できるようになる
img_array = np.array(img)
# 配列の形状を確認
# NumPyは (高さ, 幅, チャンネル) の順番
print(img_array.shape) # 出力例: (480, 640, 3)
完成コード(実際に入力して試す場合)
以下のコードをGoogle Colabで実行できます。
# PIL/Pillowで画像を読み込む完全なコード
from PIL import Image
import numpy as np
# サンプル画像をダウンロード
import urllib.request
url = “https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg”
urllib.request.urlretrieve(url, “sample.jpg”)
# 画像を読み込み
img = Image.open(‘sample.jpg’)
# 画像情報を表示
print(f”サイズ(幅×高さ): {img.size}”)
print(f”色モード: {img.mode}”)
# NumPy配列に変換
img_array = np.array(img)
print(f”配列の形状(高さ, 幅, チャンネル): {img_array.shape}”)
print(f”データ型: {img_array.dtype}”)
# 画像を表示(Jupyter/Colabの場合)
img.show() # または display(img)
実行結果:
サイズ(幅×高さ): (1200, 800)
色モード: RGB
配列の形状(高さ, 幅, チャンネル): (800, 1200, 3)
データ型: uint8
3-3. OpenCV – 高速で機能豊富
コンピュータビジョンの実務では、OpenCVが最もよく使われます。C++で実装されているため高速です。
ステップ1:ライブラリのインポート
# cv2はOpenCVのPythonバインディング
# opencv-pythonをインストールすると使える
import cv2
# NumPyも必要
import numpy as np
ステップ2:画像の読み込み
# cv2.imread()で画像を読み込む
# 重要:OpenCVはBGR順序で読み込む!
img_bgr = cv2.imread(‘sample.jpg’)
# 読み込みに失敗するとNoneが返る
if img_bgr is None:
print(“画像の読み込みに失敗しました”)
else:
print(“読み込み成功”)
ステップ3:BGR → RGB変換(重要!)
# OpenCVはBGR順序で読み込むため、
# 他のライブラリ(matplotlib等)と組み合わせる場合は
# RGB順序に変換する必要がある
# cv2.cvtColor()で色空間を変換
# cv2.COLOR_BGR2RGB:BGRからRGBへ変換
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
# これでmatplotlibで正しい色で表示できる
ステップ4:グレースケールで読み込む方法
# 第2引数で読み込みモードを指定できる
# cv2.IMREAD_GRAYSCALE:グレースケールで読み込み
img_gray = cv2.imread(‘sample.jpg’, cv2.IMREAD_GRAYSCALE)
# グレースケールは2次元配列になる
print(img_gray.shape) # 出力例: (480, 640)
完成コード(実際に入力して試す場合)
# OpenCVで画像を読み込む完全なコード
import cv2
import numpy as np
import matplotlib.pyplot as plt
# サンプル画像をダウンロード
import urllib.request
url = “https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg”
urllib.request.urlretrieve(url, “sample.jpg”)
# 画像を読み込み(BGR順序)
img_bgr = cv2.imread(‘sample.jpg’)
# RGB順序に変換
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
# グレースケールでも読み込み
img_gray = cv2.imread(‘sample.jpg’, cv2.IMREAD_GRAYSCALE)
# 画像情報を表示
print(f”カラー画像の形状: {img_rgb.shape}”)
print(f”グレースケール画像の形状: {img_gray.shape}”)
# matplotlibで表示
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
axes[0].imshow(img_rgb)
axes[0].set_title(‘RGB Image’)
axes[0].axis(‘off’)
axes[1].imshow(img_gray, cmap=’gray’)
axes[1].set_title(‘Grayscale Image’)
axes[1].axis(‘off’)
plt.tight_layout()
plt.show()
✅ OpenCVの特徴まとめ
・高速:C++で実装、最適化されている
・機能豊富:フィルタ、エッジ検出、物体検出など多数
・NumPy配列:直接NumPy配列として扱える
・BGR順序:注意が必要!読み込んだらすぐRGBに変換する習慣を
3-4. どのライブラリを使うべき?
💡 ライブラリの使い分けガイド
🎨 PIL/Pillow を使う場面:
・シンプルな画像操作(リサイズ、クロップなど)
・プロトタイピング、学習目的
・PyTorchのデータ拡張(torchvision.transforms)
🚀 OpenCV を使う場面:
・本格的なコンピュータビジョン開発
・リアルタイム処理(動画、カメラ入力)
・高度な画像処理(フィルタ、エッジ検出、特徴抽出)
📊 matplotlib を使う場面:
・画像の可視化、表示
・Jupyter Notebook での開発
・グラフと画像を組み合わせた表示
このコースでは、OpenCVを中心に学びます(CV業界の標準)
🔧 4. 画像の基本操作
画像のリサイズ、クロップ、回転、反転など、基本操作を学びましょう。これらは深層学習のデータ拡張でも頻繁に使われます。
4-1. リサイズ(Resize)
画像の大きさを変更する操作です。深層学習では、モデルの入力サイズに合わせるために必須です。
OpenCVでのリサイズ
# cv2.resize()で画像をリサイズ
# 第1引数:元の画像
# 第2引数:(新しい幅, 新しい高さ) のタプル
import cv2
# 画像読み込み
img = cv2.imread(‘sample.jpg’)
# 320×240にリサイズ
img_resized = cv2.resize(img, (320, 240))
print(f”元のサイズ: {img.shape}”)
print(f”リサイズ後: {img_resized.shape}”)
アスペクト比を保持してリサイズ
単純にリサイズすると、画像が歪む可能性があります。アスペクト比(縦横比)を保持する方法も覚えておきましょう。
# アスペクト比を保持してリサイズする関数
def resize_keeping_aspect_ratio(img, new_width):
“””
幅を指定して、アスペクト比を保持してリサイズする
引数:
img: 元の画像
new_width: 新しい幅
戻り値:
リサイズされた画像
“””
# 元の画像のサイズを取得
height, width = img.shape[:2]
# アスペクト比を計算
aspect_ratio = height / width
# 新しい高さを計算(アスペクト比を維持)
new_height = int(new_width * aspect_ratio)
# リサイズ
return cv2.resize(img, (new_width, new_height))
完成コード(リサイズ)
# 画像のリサイズを試すコード
import cv2
import matplotlib.pyplot as plt
# 画像読み込み
img = cv2.imread(‘sample.jpg’)
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 固定サイズにリサイズ(歪む可能性あり)
img_fixed = cv2.resize(img_rgb, (224, 224))
# アスペクト比を保持してリサイズ
height, width = img_rgb.shape[:2]
new_width = 400
new_height = int(height * (new_width / width))
img_aspect = cv2.resize(img_rgb, (new_width, new_height))
# 表示
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
axes[0].imshow(img_rgb)
axes[0].set_title(f’Original ({img_rgb.shape[1]}x{img_rgb.shape[0]})’)
axes[0].axis(‘off’)
axes[1].imshow(img_fixed)
axes[1].set_title(f’Fixed Size (224×224)’)
axes[1].axis(‘off’)
axes[2].imshow(img_aspect)
axes[2].set_title(f’Keep Aspect ({new_width}x{new_height})’)
axes[2].axis(‘off’)
plt.tight_layout()
plt.show()
4-2. クロップ(Crop)- 切り抜き
画像の一部を切り抜く操作です。NumPyのスライスを使うのが最もシンプルです。
# NumPy配列のスライスでクロップ
# 書式:img[y1:y2, x1:x2]
# 注意:(y, x)の順番!(数学の(x, y)とは逆)
import cv2
img = cv2.imread(‘sample.jpg’)
# 左上の座標(100, 50)から、右下(400, 300)までを切り抜き
# y方向:50〜300、x方向:100〜400
img_cropped = img[50:300, 100:400]
print(f”切り抜き後のサイズ: {img_cropped.shape}”)
中央クロップ(Center Crop)
画像の中央部分を指定サイズで切り抜く方法です。深層学習のテストデータ前処理でよく使います。
# 中央から224×224を切り抜く
def center_crop(img, crop_size):
“””
画像の中央から指定サイズを切り抜く
引数:
img: 元の画像
crop_size: 切り抜きサイズ(正方形)
戻り値:
切り抜かれた画像
“””
height, width = img.shape[:2]
# 中央の座標を計算
top = (height – crop_size) // 2
left = (width – crop_size) // 2
# 切り抜き
return img[top:top+crop_size, left:left+crop_size]
# 使用例
img_center = center_crop(img, 224)
4-3. 回転(Rotation)
画像を回転させる操作です。90度単位の回転と、任意角度の回転があります。
90度単位の回転(高速)
# cv2.rotate()で90度単位の回転
import cv2
img = cv2.imread(‘sample.jpg’)
# 時計回りに90度回転
img_90 = cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE)
# 180度回転
img_180 = cv2.rotate(img, cv2.ROTATE_180)
# 反時計回りに90度回転(= 時計回りに270度)
img_270 = cv2.rotate(img, cv2.ROTATE_90_COUNTERCLOCKWISE)
任意角度の回転
# cv2.warpAffine()で任意角度の回転
import cv2
img = cv2.imread(‘sample.jpg’)
height, width = img.shape[:2]
# 回転の中心点
center = (width // 2, height // 2)
# 回転行列を取得
# 引数:(中心点, 角度(反時計回りが正), スケール)
rotation_matrix = cv2.getRotationMatrix2D(center, 45, 1.0)
# アフィン変換で回転
img_rotated = cv2.warpAffine(img, rotation_matrix, (width, height))
# 注意:回転すると、角の部分が切れてしまう
# 黒い部分(背景)が見える
4-4. 反転(Flip)
画像を左右または上下に反転させる操作です。データ拡張で最も基本的なテクニックです。
# cv2.flip()で反転
import cv2
img = cv2.imread(‘sample.jpg’)
# 水平反転(左右反転)
# 第2引数:1 = 水平反転
img_h_flip = cv2.flip(img, 1)
# 垂直反転(上下反転)
# 第2引数:0 = 垂直反転
img_v_flip = cv2.flip(img, 0)
# 両方向反転(180度回転と同じ)
# 第2引数:-1 = 両方向反転
img_both_flip = cv2.flip(img, -1)
🎯 回転・反転の用途
データ拡張:
・水平反転だけでデータを2倍に増やせる
・回転を組み合わせれば4倍、8倍に
・モデルが様々な向きに対応できるようになる
画像補正:
・傾いて撮影された画像を修正
・スキャンした文書の向き補正
完成コード(基本操作まとめ)
# 画像の基本操作をまとめて試すコード
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 画像読み込み
img = cv2.imread(‘sample.jpg’)
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 1. リサイズ
img_resized = cv2.resize(img_rgb, (224, 224))
# 2. 中央クロップ
height, width = img_rgb.shape[:2]
crop_size = min(height, width)
top = (height – crop_size) // 2
left = (width – crop_size) // 2
img_cropped = img_rgb[top:top+crop_size, left:left+crop_size]
img_cropped = cv2.resize(img_cropped, (224, 224))
# 3. 回転
center = (width // 2, height // 2)
rotation_matrix = cv2.getRotationMatrix2D(center, 30, 1.0)
img_rotated = cv2.warpAffine(img_rgb, rotation_matrix, (width, height))
# 4. 水平反転
img_flipped = cv2.flip(img_rgb, 1)
# 表示
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
axes[0, 0].imshow(img_rgb)
axes[0, 0].set_title(‘Original’)
axes[0, 0].axis(‘off’)
axes[0, 1].imshow(img_resized)
axes[0, 1].set_title(‘Resized (224×224)’)
axes[0, 1].axis(‘off’)
axes[0, 2].imshow(img_cropped)
axes[0, 2].set_title(‘Center Cropped’)
axes[0, 2].axis(‘off’)
axes[1, 0].imshow(img_rotated)
axes[1, 0].set_title(‘Rotated (30°)’)
axes[1, 0].axis(‘off’)
axes[1, 1].imshow(img_flipped)
axes[1, 1].set_title(‘Flipped (Horizontal)’)
axes[1, 1].axis(‘off’)
axes[1, 2].axis(‘off’)
plt.tight_layout()
plt.show()
🌈 5. 色空間変換
RGB以外にも、様々な色空間があります。タスクに応じて適切な色空間を選ぶことで、処理が効率的になります。
5-1. 主要な色空間
【色空間の種類】
■ RGB(Red, Green, Blue)
・最も一般的
・光の三原色
・ディスプレイの表示に使用
■ HSV(Hue, Saturation, Value)
・色相(H)、彩度(S)、明度(V)で表現
・特定の色を検出するのに便利
・照明の影響を受けにくい
■ グレースケール
・白〜黒の1チャンネル
・処理が軽い
■ Lab(Lightness, a, b)
・人間の知覚に近い
・色の差を測定するのに適している
■ YCrCb
・輝度(Y)と色差(Cr, Cb)
・動画圧縮(JPEG、MPEG)で使用
5-2. HSV色空間の詳細
HSVは色検出でよく使われる色空間です。RGB より直感的に「色」を扱えます。
🌈 HSVの3要素
H(Hue:色相) – 「何色か」
・0〜180(OpenCVの場合)または 0〜360度
・0度付近 = 赤
・60度付近 = 黄色
・120度付近 = 緑
・180度付近 = 青紫
S(Saturation:彩度) – 「色の鮮やかさ」
・0〜255(OpenCVの場合)
・0 = 無彩色(灰色)
・255 = 最も鮮やか
V(Value:明度) – 「明るさ」
・0〜255(OpenCVの場合)
・0 = 黒
・255 = 最も明るい
RGB → HSV変換
# cv2.cvtColor()で色空間を変換
import cv2
# 画像読み込み(BGR)
img_bgr = cv2.imread(‘sample.jpg’)
# BGR → HSV変換
img_hsv = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2HSV)
# HSVの各チャンネルを分離
h, s, v = cv2.split(img_hsv)
print(f”Hue(色相)の範囲: {h.min()} 〜 {h.max()}”)
print(f”Saturation(彩度)の範囲: {s.min()} 〜 {s.max()}”)
print(f”Value(明度)の範囲: {v.min()} 〜 {v.max()}”)
💡 HSVの用途
色検出が簡単:
・「赤い物体を検出」→ Hueが0〜10または170〜180の範囲
・RGBだと、赤はR成分だけでなくG,Bの組み合わせも考慮が必要
照明補正:
・明度(V)だけを調整すれば、色味を変えずに明るさを調整できる
次のSTEP 3で、HSVを使った色検出とトラッキングを実装します。
5-3. 色空間変換の一覧
# OpenCVでの色空間変換
import cv2
img_bgr = cv2.imread(‘sample.jpg’)
# よく使う変換
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB) # BGR → RGB
img_gray = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2GRAY) # BGR → グレースケール
img_hsv = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2HSV) # BGR → HSV
img_lab = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2LAB) # BGR → Lab
# 逆変換
img_bgr_back = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2BGR) # RGB → BGR
img_bgr_from_hsv = cv2.cvtColor(img_hsv, cv2.COLOR_HSV2BGR) # HSV → BGR
# OpenCVには100種類以上の変換フラグがある
# cv2.COLOR_* で確認可能
📊 6. NumPy配列としての画像処理
Pythonでは、画像はNumPy配列として扱われます。NumPyの操作を理解すると、画像処理が自在にできます。
6-1. 画像の配列構造
# 画像の配列構造を確認
import cv2
import numpy as np
img = cv2.imread(‘sample.jpg’)
# 配列の形状
print(f”shape: {img.shape}”) # (高さ, 幅, チャンネル)
# 出力例: (480, 640, 3)
# データ型
print(f”dtype: {img.dtype}”) # uint8 = 0〜255の整数
# 出力例: uint8
# 総要素数
print(f”size: {img.size}”) # 高さ × 幅 × チャンネル
# 出力例: 921600
6-2. ピクセル値へのアクセス
# ピクセル値へのアクセス方法
import cv2
img = cv2.imread(‘sample.jpg’)
# 特定のピクセルの値を取得
# img[y, x] の順番(行, 列)
pixel = img[100, 200]
print(f”ピクセル(y=100, x=200)の値: {pixel}”)
# 出力例: [123 145 89] (BGR値)
# 特定のチャンネルの値を取得
blue = img[100, 200, 0] # 青チャンネル
green = img[100, 200, 1] # 緑チャンネル
red = img[100, 200, 2] # 赤チャンネル
print(f”B={blue}, G={green}, R={red}”)
6-3. 画像の一部を変更
# 画像の一部を変更する
import cv2
import numpy as np
img = cv2.imread(‘sample.jpg’)
# 重要:元の画像を保護するためにコピーを作成
img_copy = img.copy()
# 左上100×100ピクセルを赤色に変更
# OpenCVはBGR順序なので、赤は(0, 0, 255)
img_copy[0:100, 0:100] = [0, 0, 255]
# 特定のチャンネルだけを変更
# 青チャンネルをゼロに(黄色っぽくなる)
img_copy[:, :, 0] = 0
6-4. 画像の正規化
深層学習では、画像データを正規化することが重要です。学習の安定化と高速化に役立ちます。
# 正規化の方法
import numpy as np
# 元の画像(uint8, 0〜255)
img = cv2.imread(‘sample.jpg’)
# 方法1:0〜1に正規化
# float32に変換してから255で割る
img_normalized = img.astype(np.float32) / 255.0
print(f”範囲: {img_normalized.min()} 〜 {img_normalized.max()}”)
# 出力: 範囲: 0.0 〜 1.0
# 方法2:平均0、標準偏差1に標準化
# ImageNetで学習したモデルを使う場合はこれを使う
mean = np.array([0.485, 0.456, 0.406]) # ImageNetの平均(RGB)
std = np.array([0.229, 0.224, 0.225]) # ImageNetの標準偏差(RGB)
img_standardized = (img_normalized – mean) / std
💡 なぜ正規化するのか?
学習の安定化:勾配爆発・消失を防ぐ
収束の高速化:学習が速くなる
転移学習:事前学習モデルと同じ正規化が必要
ImageNetで事前学習されたモデル(ResNet、EfficientNetなど)を使う場合は、
必ず同じ正規化パラメータ(mean, std)を使用してください。
📝 練習問題
問題1:画像の配列構造(基礎)
以下のコードを実行したとき、出力される形状(shape)を答えてください。
import cv2
# 640×480のRGB画像を読み込み
img = cv2.imread(‘sample.jpg’) # 640×480の画像
print(img.shape)
# グレースケールで読み込み
img_gray = cv2.imread(‘sample.jpg’, cv2.IMREAD_GRAYSCALE)
print(img_gray.shape)
解答:
1つ目の出力(カラー画像):
(480, 640, 3)
・高さ480、幅640、チャンネル3(BGR)
・NumPyは(高さ, 幅, チャンネル)の順序
2つ目の出力(グレースケール):
(480, 640)
・高さ480、幅640
・グレースケールはチャンネルが1つなので、2次元配列
補足:
・画像ファイルが「640×480」と言う場合は「幅×高さ」の順番
・NumPy配列は「高さ×幅」の順番なので、(480, 640)になる
問題2:BGR vs RGB(基礎)
OpenCVで読み込んだ画像をmatplotlibで表示すると、色がおかしくなります。なぜでしょうか?また、どう修正すれば良いですか?
解答:
原因:
・OpenCVは
BGR順序で画像を読み込む
・matplotlibは
RGB順序を期待している
→ 赤と青が入れ替わって表示される
修正方法:
import cv2
import matplotlib.pyplot as plt
# OpenCVで読み込み(BGR順序)
img_bgr = cv2.imread(‘sample.jpg’)
# RGB順序に変換
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
# matplotlibで表示
plt.imshow(img_rgb)
plt.axis(‘off’)
plt.show()
ポイント:
・OpenCVで読み込んだら、すぐにRGBに変換するのがベストプラクティス
・保存する際は、再度BGRに戻す必要がある
問題3:画像の明るさ調整(中級)
画像全体を「50」だけ明るくするコードを書いてください。ただし、ピクセル値が255を超えないように注意してください。
解答:
import cv2
import numpy as np
# 画像読み込み
img = cv2.imread(‘sample.jpg’)
# 方法1:np.clipを使う(推奨)
# int型に変換してから加算し、0〜255にクリップ
img_bright = np.clip(img.astype(int) + 50, 0, 255).astype(np.uint8)
# 方法2:cv2.addを使う(自動でクリップされる)
img_bright2 = cv2.add(img, np.full(img.shape, 50, dtype=np.uint8))
# 保存
cv2.imwrite(‘bright.jpg’, img_bright)
説明:
・
単純な加算はNG:img + 50だとオーバーフローする
・
np.clip:値を0〜255の範囲にクリップ
・
int型に変換:uint8のままだと、255+50=49になる(オーバーフロー)
・
cv2.add:OpenCVが自動でクリップしてくれる
よくある間違い:
# ❌ これはダメ(オーバーフローする)
img_bright = img + 50
# uint8(0〜255)の範囲を超えると…
# 200 + 50 = 250 ✅
# 250 + 50 = 44 ❌(256で割った余り)
問題4:データ拡張の実装(応用)
以下の変換を組み合わせた、データ拡張関数を実装してください。
- 画像を224×224にリサイズ
- 50%の確率で水平反転
- 0〜30度の範囲でランダムに回転
解答:
import cv2
import numpy as np
import random
def augment_image(img):
“””
データ拡張パイプライン
引数:
img: 入力画像(BGR)
戻り値:
拡張された画像
“””
# 1. リサイズ(224×224)
img_aug = cv2.resize(img, (224, 224))
# 2. 50%の確率で水平反転
if random.random() < 0.5:
img_aug = cv2.flip(img_aug, 1)
# 3. 0〜30度のランダム回転
angle = random.uniform(0, 30)
height, width = img_aug.shape[:2]
center = (width // 2, height // 2)
rotation_matrix = cv2.getRotationMatrix2D(center, angle, 1.0)
img_aug = cv2.warpAffine(img_aug, rotation_matrix, (width, height))
return img_aug
# 使用例
img = cv2.imread('sample.jpg')
# 複数の拡張画像を生成
for i in range(5):
img_aug = augment_image(img)
cv2.imwrite(f'augmented_{i}.jpg', img_aug)
print(f'拡張画像{i}を保存しました')
ポイント:
・データ拡張は
学習時のみ適用
・テスト時は
中央クロップのみ(ランダム要素なし)
・次のSTEP 4で、より詳しく学習します
問題5:HSVによる色検出(応用)
画像から「赤い物体」だけを抽出するコードを書いてください。HSV色空間を使い、赤の範囲を指定してマスクを作成してください。
解答:
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 画像読み込み
img = cv2.imread(‘sample.jpg’)
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# BGR → HSV変換
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# 赤の範囲を定義(HSVで)
# 赤は0度付近と180度付近に2箇所ある(色相環で赤は端にある)
lower_red1 = np.array([0, 100, 100]) # 0〜10度
upper_red1 = np.array([10, 255, 255])
lower_red2 = np.array([160, 100, 100]) # 160〜180度
upper_red2 = np.array([180, 255, 255])
# マスクを作成
# cv2.inRange():範囲内のピクセルを白(255)、範囲外を黒(0)に
mask1 = cv2.inRange(img_hsv, lower_red1, upper_red1)
mask2 = cv2.inRange(img_hsv, lower_red2, upper_red2)
# 2つのマスクを結合
mask = cv2.bitwise_or(mask1, mask2)
# マスクを適用して赤い部分だけ抽出
result = cv2.bitwise_and(img_rgb, img_rgb, mask=mask)
# 結果を表示
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
axes[0].imshow(img_rgb)
axes[0].set_title(‘Original’)
axes[0].axis(‘off’)
axes[1].imshow(mask, cmap=’gray’)
axes[1].set_title(‘Mask’)
axes[1].axis(‘off’)
axes[2].imshow(result)
axes[2].set_title(‘Red Objects Only’)
axes[2].axis(‘off’)
plt.tight_layout()
plt.show()
説明:
・
HSVで色検出が簡単な理由:Hue(色相)が色を直接表すため
・
赤が2箇所:Hueは0〜180度の範囲で、赤は0度と180度付近にある
・
inRange:範囲内のピクセルを白(255)、範囲外を黒(0)にする
他の色の範囲(参考):
# 緑
lower_green = np.array([40, 40, 40])
upper_green = np.array([80, 255, 255])
# 青
lower_blue = np.array([100, 40, 40])
upper_blue = np.array([140, 255, 255])
# 黄色
lower_yellow = np.array([20, 100, 100])
upper_yellow = np.array([30, 255, 255])
📝 STEP 2 のまとめ
✅ このステップで学んだこと
・デジタル画像は、ピクセルの集合である
・RGB:光の三原色(赤、緑、青)で色を表現
・グレースケール:白〜黒の1チャンネルで表現
・HSV:色相、彩度、明度で表現(色検出に便利)
・PIL/Pillow:シンプル、初心者向け、RGB順序
・OpenCV:高速、機能豊富、BGR順序(注意!)
・matplotlib:可視化に便利、RGB順序
・基本操作:リサイズ、クロップ、回転、反転
・NumPy配列:画像はNumPy配列として扱われる
・正規化:深層学習では必須の前処理
💡 重要ポイント
⚠️ OpenCVのBGR順序:
これは頻繁に混乱の原因になります。
読み込んだらすぐにcv2.cvtColor(img, cv2.COLOR_BGR2RGB)でRGBに変換する習慣をつけましょう。
📐 配列の順序:
NumPyは(高さ, 幅, チャンネル)の順番です。
画像ファイルの「640×480」は「幅×高さ」なので、混同しないように注意。
🎯 次のステップの準備
次のSTEP 3では、「OpenCVによる画像処理」を学びます。
学習内容:
・フィルタリング(ブラー、シャープニング)
・エッジ検出(Sobel、Canny)
・モルフォロジー変換(膨張、収縮)
・色検出とトラッキング
これらの古典的CV技術は、深層学習でも前処理として頻繁に使われます!