新4年生用

【Python入門2】Pythonの基本とOpenCV入門

はじめに

このテキストは、Python初心者むけのOpenCVライブラリを使った画像処理・コンピュータビジョンの基礎講座です。新大学4年生を対象とし、C言語の基礎知識はあるものの、Pythonはほとんど触れたことがない方向けです。Windows 11環境のSpyderを使用して、PCに搭載されたウェブカメラを活用した実践的な内容となっています。

前回はSpyderをインストールし、Pythonの開発環境を整えるところまでやりました。

今回はPythonの基本構文を説明した後、OpenCVを少しだけ取り扱います。すべて覚えようとせず、こういうことが出来るということだけでも覚えておいてもらうだけで、後々役に立つと思います。

はじめに前回の解説の演習でインストールしたMatplotlibが入っていない人は「python -m pip install matplotlib」でインストールしてください。コマンドプロンプトではなくAnaconda promptを使用します。

学習目標

  • Python言語の基本的な構文を理解する
  • OpenCVライブラリの基本的な機能を理解し実装できる

所要時間: 約1時間
必要環境:

  • Windows 11
  • Spyder (Python IDE)

目次

  1. Python基礎入門
    • Pythonの基本構文
    • 変数と型
    • 制御構造(条件分岐、ループ)
    • 関数の定義と利用
  2. OpenCV入門
    • OpenCVとは
    • インストール方法
    • 基本的な画像読み込みと表示
  3. 画像処理の基礎
    • 色空間と色変換

1. Python基礎入門

Pythonの基本構文

Pythonは読みやすく書きやすい高級言語です。C言語と比較すると、セミコロンが不要で、ブロックはインデントで表現します。

#これはコメントです
print("Hello, World!") # 出力関数
# 変数の宣言(型宣言は不要)
message = "OpenCVを学ぼう"
print(message)

変数と型

Pythonでは変数の型宣言は不要です。代入される値によって自動的に型が決まります。

リストと辞書

Pythonには便利なデータ構造が組み込まれています。

# リスト(配列に相当)
colors = ["red", "green", "blue"]
print(colors[0])      # "red"を出力
colors.append("yellow")  # 要素の追加

# 辞書(キーと値のペア)
person = {
    "name": "鈴木",
    "age": 22,
    "student": True
}
print(person["name"])  # "鈴木"を出力

制御構造

条件分岐とループの書き方です。

# if文
x = 10
if x > 5:
    print("xは5より大きい")
elif x == 5:
    print("xは5に等しい")
else:
    print("xは5より小さい")

# for文(リストの各要素に対して処理)
for color in colors:
    print(color)

# range関数を使ったfor文
for i in range(5):  # 0から4まで
    print(i)

# while文
count = 0
while count < 5:
    print(count)
    count += 1

関数の定義と利用

関数はdefキーワードで定義します。

# 関数の定義
def greet(name):
    return "こんにちは、" + name + "さん"

# 関数の呼び出し
message = greet("田中")
print(message)  # "こんにちは、田中さん"

# デフォルト引数
def calculate_area(width, height=10):
    return width * height

area1 = calculate_area(5, 8)  # 40
area2 = calculate_area(5)     # 50(heightにはデフォルト値10が使用される)

Python演習

以下の簡単な演習で、Pythonの基本を確認しましょう。この演習をSpyderで実行してみてください。

# 演習1: 1から10までの数字を出力し、その合計を表示する
total = 0
for i in range(1, 11):  # 1から10まで
    print(i)
    total += i

print("合計:", total)

# 演習2: リストの操作
numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5]
print("リスト:", numbers)
print("最大値:", max(numbers))
print("最小値:", min(numbers))
print("合計:", sum(numbers))
print("ソート後:", sorted(numbers))

2. OpenCV入門

OpenCVとは

OpenCV(Open Source Computer Vision Library)は、画像処理やコンピュータビジョンのためのオープンソースライブラリです。画像の読み込み・表示から、高度な画像処理、機械学習まで幅広い機能を提供しています。

OpenCVによるカメラ制御や映像処理は研究や就職後に使う機会が多いと思いますので、一度触れておくと後から楽だと思います。

基本的な画像読み込みと表示

OpenCVを使って画像を読み込み、表示する基本的な方法を学びましょう。まず、任意の画像ファイル(例:sample.jpg)をプロジェクトフォルダに保存してください。

import cv2
import matplotlib.pyplot as plt

# 画像の読み込み
img = cv2.imread('sample.jpg')

# OpenCVはBGR形式で読み込むため、RGB形式に変換
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

# Matplotlib で画像を表示
plt.imshow(img_rgb)
plt.title('Sample Image')
plt.axis('off')  # 軸を非表示
plt.show()

# OpenCVのウィンドウで表示
cv2.imshow('OpenCV Window', img)
cv2.waitKey(0)  # キー入力を待つ
cv2.destroyAllWindows()  # ウィンドウを閉じる

注意点:

  • OpenCVは画像をBGR形式(青、緑、赤の順)で読み込みます
  • Matplotlibは画像をRGB形式(赤、緑、青の順)で表示します
  • cv2.waitKey(0)は何かキーが押されるまで待機します
  • cv2.destroyAllWindows()はすべてのOpenCVウィンドウを閉じます

画像のプロパティを表示

import cv2

# 画像の読み込み
img = cv2.imread('sample.jpg')

# 画像が正しく読み込まれたか確認
if img is None:
    print("画像を読み込めませんでした")
else:
    # 画像の高さ、幅、チャンネル数を取得
    height, width, channels = img.shape
    print(f"画像サイズ: {width}x{height}")
    print(f"チャンネル数: {channels}")
    
    # データ型
    print(f"データ型: {img.dtype}")
    
    # 表示
    cv2.imshow('Image Properties', img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

3. 画像処理の基礎

色空間と色変換

OpenCVでは様々な色空間(カラーモデル)間の変換が可能です。

import cv2
import matplotlib.pyplot as plt

# 画像の読み込み
img = cv2.imread('sample.jpg')

# グレースケールに変換
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# HSV色空間に変換
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

# 表示用にRGBに変換
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

# Matplotlibで表示
plt.figure(figsize=(12, 4))

plt.subplot(131)
plt.imshow(img_rgb)
plt.title('RGB')
plt.axis('off')

plt.subplot(132)
plt.imshow(gray, cmap='gray')
plt.title('Grayscale')
plt.axis('off')

plt.subplot(133)
plt.imshow(cv2.cvtColor(hsv, cv2.COLOR_HSV2RGB))
plt.title('HSV')
plt.axis('off')

plt.tight_layout()
plt.show()

問題: 画像の二値化とエッジ検出の組み合わせ

以下の手順で画像処理を行うプログラムを作成してください:

  1. カラー画像を読み込む
  2. グレースケールに変換する
  3. 適切な閾値で二値化処理を行う
  4. 二値化した画像に対してCannyエッジ検出を適用する
  5. 元の画像、グレースケール画像、二値化画像、エッジ検出結果を2×2のグリッドで表示する

オプション課題: 二値化の閾値をトラックバーで調整できるようにする

ヒント: cv2.imshow()関数を使って複数の画像を表示する際は、それぞれ異なるウィンドウ名を指定する必要があります。また、cv2.waitKey(0)は一度だけ呼び出せば十分です。

解答例

import cv2
import numpy as np

def main():
    # 画像を読み込む
    img = cv2.imread('sample.jpg')  # 適切な画像ファイルパスを指定してください
    if img is None:
        print("画像を読み込めませんでした")
        return

    # グレースケールに変換
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # カラー画像をグレースケールに変換

    # 二値化処理
    threshold_value = 127  # 初期閾値
    _, binary = cv2.threshold(gray, threshold_value, 255, cv2.THRESH_BINARY)  # 固定閾値で二値化

    # Cannyエッジ検出
    edges = cv2.Canny(binary, 50, 150)  # 二値化画像にCannyエッジ検出を適用

    # 2x2グリッドで表示するための準備
    # 画像のサイズを統一する
    height, width = gray.shape[:2]
    img_resized = cv2.resize(img, (width, height))

    # 上半分の画像を結合(元画像とグレースケール)
    top_row = np.hstack((img_resized, cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR)))  # グレースケールを3チャンネルに変換して結合

    # 下半分の画像を結合(二値化とエッジ検出)
    bottom_row = np.hstack((cv2.cvtColor(binary, cv2.COLOR_GRAY2BGR), cv2.cvtColor(edges, cv2.COLOR_GRAY2BGR)))  # 1チャンネルを3チャンネルに変換

    # 上下の行を結合して2x2グリッドを作成
    result = np.vstack((top_row, bottom_row))

    # 結果を表示
    cv2.imshow('Processing Steps', result)  # 全ての処理結果を1つのウィンドウに表示

    # キー入力を待つ
    cv2.waitKey(0)
    cv2.destroyAllWindows()

if __name__ == "__main__":
    main()

オプション課題の解答例(トラックバーによる閾値調整)

import cv2
import numpy as np

def update_threshold(val):
    # トラックバーの値が変更されたときに呼び出される関数
    global img, gray

    # 新しい閾値で二値化
    _, binary = cv2.threshold(gray, val, 255, cv2.THRESH_BINARY)  # トラックバーの値を閾値として使用

    # Cannyエッジ検出を適用
    edges = cv2.Canny(binary, 50, 150)  # 二値化画像にエッジ検出を適用

    # 2x2グリッドで表示
    height, width = gray.shape[:2]
    img_resized = cv2.resize(img, (width, height))

    # 上半分と下半分の画像を作成
    top_row = np.hstack((img_resized, cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR)))  # グレースケールを3チャンネルに変換
    bottom_row = np.hstack((cv2.cvtColor(binary, cv2.COLOR_GRAY2BGR), cv2.cvtColor(edges, cv2.COLOR_GRAY2BGR)))  # 1チャンネルを3チャンネルに変換

    # 結合して表示
    result = np.vstack((top_row, bottom_row))
    cv2.imshow('Interactive Processing', result)  # 結果を表示

def main():
    global img, gray

    # 画像を読み込む
    img = cv2.imread('sample.jpg')  # 適切な画像ファイルパスに変更してください
    if img is None:
        print("画像を読み込めませんでした")
        return

    # グレースケールに変換
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # カラー画像をグレースケールに変換

    # ウィンドウを作成
    cv2.namedWindow('Interactive Processing')

    # トラックバーを作成
    cv2.createTrackbar('Threshold', 'Interactive Processing', 127, 255, update_threshold)  # 閾値調整用のトラックバー

    # 初期表示
    update_threshold(127)  # 初期閾値で表示

    # キー入力を待つ
    cv2.waitKey(0)
    cv2.destroyAllWindows()

if __name__ == "__main__":
    main()

解説

基本的な解答例では、以下の処理を行っています:

  1. cv2.imread()でカラー画像を読み込む
  2. cv2.cvtColor()でBGRからグレースケールに変換
  3. cv2.threshold()で固定閾値(127)を使って二値化
  4. cv2.Canny()で二値化画像からエッジを検出
  5. np.hstack()とnp.vstack()を使って4つの画像を2×2グリッドに配置

オプション課題では、以下の追加機能を実装しています:

  1. cv2.createTrackbar()でスライダーを作成し、閾値を0〜255の範囲で調整できるようにしています
  2. スライダーの値が変更されるたびにupdate_threshold()関数が呼び出され、画像処理が更新されます
  3. グローバル変数を使用して元の画像とグレースケール画像を保持し、閾値が変更されるたびに再利用しています

このプログラムを実行すると、閾値を調整することで、様々な明るさの部分がどのように二値化されるか、そしてエッジ検出にどのような影響を与えるかを視覚的に確認できます。

まとめ

今回はPythonの基本的な構文の理解と、OpenCVの基本を少し取り扱いました。

次回はOpenCVについて、もう少し実践的な内容を学びます。