OpenCVの特徴的な機能

【概要】 OpenCVには,色空間変換,局所的コントラスト強調,動体追跡,幾何学的変換,形状解析,多重解像度解析,パターン検出,適応的二値化,セグメンテーション等の多彩な機能がある.これらの機能は,照明変動への対応,動き検出,形状認識など,実用的な画像処理に有効である.

【目次】

  1. Python のインストールと必要なPythonライブラリのインストール
  2. 色空間変換
  3. 画像の色空間を変換する.BGRからHSVへの変換は,色検出にも有効である.

  4. CLAHE
  5. 局所的コントラスト強調による適応的ヒストグラム平衡化により,画像の視認性を大幅に向上させる.

  6. オプティカルフロー
  7. 連続画像間の動きを検出し,リアルタイムな動体追跡とモーション解析を実現.

  8. アフィン変換
  9. 画像に対する平行移動,回転,拡大縮小などの変換である.

  10. モルフォロジー演算
  11. ノイズ除去,形状解析にも有用な場合がある.

  12. 画像ピラミッド
  13. 異なる解像度の画像を階層的に生成し,スケール不変な画像解析を可能する基礎技術である.

  14. テンプレートマッチング
  15. 画像内の特定パターンを検出する.

  16. 適応的閾値処理
  17. 局所的な明るさに基づく二値化により,照明変動に頑健な処理を実現する.

  18. GrabCut画像セグメンテーション
  19. グラフカットを用いた前景と背景の分離である.

  20. カラー画像表示
  21. パソコンカメラ表示

【サイト内の関連情報】

OpenCV について [PDF], [パワーポイント]

ドクセルの URL: https://www.docswell.com/s/6674398749/51X845-2022-02-18-085434

Python のインストールと必要なPythonライブラリのインストール(Windows上)

  1. Python のインストール

    注:既にPython(バージョン3.12を推奨)がインストール済みの場合は,この手順は不要である.

    winget(Windowsパッケージマネージャー)を使用してインストールを行う

    1. Windowsで,コマンドプロンプト管理者権限で起動する(例:Windowsキーを押し,「cmd」と入力し,「管理者として実行」を選択)
    2. winget(Windowsパッケージマネージャー)が利用可能か確認する:
      winget --version
      
    3. Pythonのインストール(下のコマンドにより Python 3.12 がインストールされる).
      winget install --scope machine Python.Launcher
      winget install --scope machine Python.Python.3.12
      
  2. 必要なPythonライブラリのインストール
    1. Windowsで,コマンドプロンプト管理者権限で起動する(例:Windowsキーを押し,「cmd」と入力し,「管理者として実行」を選択)
    2. 以下のコマンドを実行し,必要なライブラリをインストールする.
      pip install -U numpy opencv_python
      

【関連する外部ページ】

【サイト内の関連ページ】

色空間変換

概要

色空間変換とは,デジタル画像の色表現を異なる色空間へと変換する画像処理である.

特徴

BGRからHSVへの変換を行うことで,色相(Hue),彩度(Saturation),明度(Value)を独立させることができる.これは,照明条件が変化する環境下での解析に適する.

使用されている技術

ソースコード: BGRからHSV色空間への変換

BGRからHSV色空間への変換プログラムである.OpenCVのcv2.cvtColor関数で色空間変換を行い,色相・彩度・明度を分離する.urllib.requestで画像を取得し,cv2.imshowで表示する.照明変動の影響を抑制できる特徴がある.


import cv2
import urllib.request

# 画像のダウンロード
url = "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/fruits.jpg"
urllib.request.urlretrieve(url, "fruits.jpg")

# 色空間変換
img = cv2.imread("fruits.jpg")
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
cv2.imshow('HSV', hsv)
cv2.waitKey(0)
cv2.destroyAllWindows()

ソースコード: BGRからL*a*b*色空間への変換

BGRからL*a*b*色空間への変換プログラムである.L*a*b*色空間は,L*が輝度,a*とb*が色度を表す.OpenCVのcv2.cvtColor関数で色空間変換を行い,輝度と色度成分を分離する.urllib.requestで画像を取得し,cv2.imshowで表示する.色度成分a*とb*は照明変動の影響を受けにくい特徴がある.


import cv2
import urllib.request

# 画像のダウンロード
url = "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/fruits.jpg"
urllib.request.urlretrieve(url, "fruits.jpg")

# 色空間変換
img = cv2.imread("fruits.jpg")
lab = cv2.cvtColor(img, cv2.COLOR_BGR2Lab)
cv2.imshow('L*a*b*', lab)
cv2.waitKey(0)
cv2.destroyAllWindows()

CLAHE

概要

CLAHE(適応的ヒストグラム平衡化)は,画像の局所領域のコントラストを強調し,画像全体の視認性を向上させる画像処理手法である.

使用されている技術

ソースコード: CLAHEによるコントラスト強調(輝度成分のみ)

CLAHE(適応的ヒストグラム平衡化)のプログラムである.OpenCVのcreateCLAHE関数を使用し,局所的なコントラスト強調を行う.clipLimitとtileGridSizeのパラメータにより細部の強調度を制御し,画像の視認性を向上させる.


import cv2
import urllib.request

url = "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/fruits.jpg"
urllib.request.urlretrieve(url, "fruits.jpg")

img = cv2.imread("fruits.jpg", 0)
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
cl1 = clahe.apply(img)
cv2.imshow('CLAHE', cl1)
cv2.waitKey(0)
cv2.destroyAllWindows()

ソースコード: CLAHEによるコントラスト強調(カラー画像)

BGRからL*a*b*色空間に変換し,L*成分にCLAHEを適用する.その後BGRに戻して表示する.CLAHEは局所的なコントラスト強調を行う手法である.


import cv2
import urllib.request

# 画像のダウンロード
url = "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/fruits.jpg"
urllib.request.urlretrieve(url, "fruits.jpg")

# 色空間変換
img = cv2.imread("fruits.jpg")
lab = cv2.cvtColor(img, cv2.COLOR_BGR2Lab)

# L*a*b*の各チャネルを分離
l, a, b = cv2.split(lab)

# L*成分にCLAHEを適用
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
cl1 = clahe.apply(l)

# チャネルを結合
lab_clahe = cv2.merge([cl1, a, b])

# BGRに戻す
bgr = cv2.cvtColor(lab_clahe, cv2.COLOR_Lab2BGR)
cv2.imshow('Result', bgr)
cv2.waitKey(0)
cv2.destroyAllWindows()

オプティカルフロー

概要

オプティカルフローは,連続する画像フレーム間の動きを検出する技術である.動体追跡,モーション解析に用いられる.

使用されている技術

ソースコード(2つの画像に関する処理)

連続する画像フレーム間の動きを検出するプログラムである.Farneback法でDense Optical Flowを算出し,cv2.cartToPolarで動きの大きさと角度を算出する.検出した動きはHSV色空間でベクトル場として表示する.


import cv2
import numpy as np
import urllib.request

url1 = "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/rubberwhale1.png"
url2 = "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/rubberwhale2.png"
urllib.request.urlretrieve(url1, "frame1.png")
urllib.request.urlretrieve(url2, "frame2.png")

prev_img = cv2.imread('frame1.png', cv2.IMREAD_GRAYSCALE)
next_img = cv2.imread('frame2.png', cv2.IMREAD_GRAYSCALE)

# オプティカルフローの計算
flow = cv2.calcOpticalFlowFarneback(prev_img, next_img, None, 0.5, 3, 15, 3, 5, 1.2, 0)

# フロー可視化用のHSV画像を生成
hsv = np.zeros((prev_img.shape[0], prev_img.shape[1], 3), dtype=np.uint8)
hsv[..., 1] = 255

# フローの大きさと角度を計算
mag, ang = cv2.cartToPolar(flow[..., 0], flow[..., 1])
hsv[..., 0] = ang * 180 / np.pi / 2
hsv[..., 2] = cv2.normalize(mag, None, 0, 255, cv2.NORM_MINMAX)

# HSVからBGRに変換して表示
bgr = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)

# 結果を表示
cv2.imshow('Optical Flow', bgr)
cv2.waitKey(0)
cv2.destroyAllWindows()

ソースコード(動画に関する処理.引数で指定可能)


import cv2
import numpy as np
import urllib.request
import sys
import os

def print_usage():
    print("Usage:")
    print("  No argument: Download and use sample video (vtest.avi)")
    print("  Number (0,1,...): Use webcam with specified device number")
    print("  Filename: Use specified video file")

# 入力ソースの決定
if len(sys.argv) == 1:
    # 引数なし: サンプル動画をダウンロードして使用
    print("Using sample video (vtest.avi)")
    video_url = "https://github.com/opencv/opencv/raw/master/samples/data/vtest.avi"
    urllib.request.urlretrieve(video_url, "vtest.avi")
    cap = cv2.VideoCapture("vtest.avi")
elif sys.argv[1].isdigit():
    # 数字: WebCamを使用
    device_num = int(sys.argv[1])
    print(f"Using webcam device {device_num}")
    cap = cv2.VideoCapture(device_num)
else:
    # その他: ファイル名として解釈
    filename = sys.argv[1]
    if not os.path.exists(filename):
        print(f"Error: File '{filename}' not found")
        print_usage()
        exit()
    print(f"Using video file: {filename}")
    cap = cv2.VideoCapture(filename)

# 最初のフレームの読み込みとグレースケール変換
ret, prev_frame = cap.read()
if not ret:
    print("Error opening video source")
    print_usage()
    exit()
prev_gray = cv2.cvtColor(prev_frame, cv2.COLOR_BGR2GRAY)

# HSV画像の準備
hsv = np.zeros_like(prev_frame)
hsv[..., 1] = 255

while True:
    # 次のフレームの読み込み
    ret, next_frame = cap.read()
    if not ret:
        break
    next_gray = cv2.cvtColor(next_frame, cv2.COLOR_BGR2GRAY)

    # オプティカルフローの計算
    flow = cv2.calcOpticalFlowFarneback(prev_gray, next_gray, None, 0.5, 3, 15, 3, 5, 1.2, 0)

    # フローの大きさと角度を計算
    mag, ang = cv2.cartToPolar(flow[..., 0], flow[..., 1])
    hsv[..., 0] = ang * 180 / np.pi / 2
    hsv[..., 2] = cv2.normalize(mag, None, 0, 255, cv2.NORM_MINMAX)

    # HSVからBGRに変換して表示
    bgr = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
    cv2.imshow('Optical Flow', bgr)

    # 'q'キーを押すと終了
    if cv2.waitKey(30) & 0xFF == ord('q'):
        break

    # 現在のフレームを次のループのprev_frameとして設定
    prev_gray = next_gray

cap.release()
cv2.destroyAllWindows()

アフィン変換

概要

アフィン変換は,画像の幾何学的変換を行う手法である.平行移動,回転,拡大縮小と,その複合的な変換を実現する.

使用されている技術

ソースコード

アフィン変換のプログラムである.cv2.getAffineTransformで3点のペアから変換行列を生成し,cv2.warpAffineで画像を変換する.平行移動・回転・拡大縮小などの幾何学的変換を行う.画像範囲内での変換を保証するようにしている.


import cv2
import numpy as np
import urllib.request

url = "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/fruits.jpg"
urllib.request.urlretrieve(url, "fruits.jpg")

img = cv2.imread("fruits.jpg")
rows, cols, ch = img.shape

# 変換元の点を画像サイズ内に制限
pts1 = np.float32([[min(50, cols-1), min(50, rows-1)],
                  [min(200, cols-1), min(50, rows-1)],
                  [min(50, cols-1), min(200, rows-1)]])

# 変換先の点を画像サイズ内に制限
pts2 = np.float32([[min(10, cols-1), min(100, rows-1)],
                  [min(200, cols-1), min(50, rows-1)],
                  [min(100, cols-1), min(250, rows-1)]])

M = cv2.getAffineTransform(pts1, pts2)
dst = cv2.warpAffine(img, M, (cols, rows))
cv2.imshow('Affine Transform', dst)
cv2.waitKey(0)
cv2.destroyAllWindows()

モルフォロジー演算

概要

モルフォロジー演算は,画像の形状に基づく処理を行う演算である.ノイズ除去,形状解析に有用である.

使用されている技術

ソースコード

収縮処理を実装したプログラムである.画像サイズに応じたkernelを生成し,cv2.erode関数で処理を行う.形状の縮小とノイズ除去が可能である.


import cv2
import numpy as np
import urllib.request

url = "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/fruits.jpg"
urllib.request.urlretrieve(url, "fruits.jpg")

img = cv2.imread("fruits.jpg", 0)

# kernelサイズを画像サイズに応じて設定
kernel_size = max(3, min(5, img.shape[0]//100))
kernel = np.ones((kernel_size, kernel_size), np.uint8)

erosion = cv2.erode(img, kernel, iterations=1)
cv2.imshow('Erosion', erosion)
cv2.waitKey(0)
cv2.destroyAllWindows()

画像ピラミッド

概要

画像ピラミッドとは,異なる解像度の画像を生成し,多重解像度解析を可能とする手法である.解像度の階層化により,スケール不変な解析を実現する.ピラミッドダウン/アップでは,解像度を下げる/上げる操作を行い,画像の多重スケール解析に用いられる.

ソースコード

画像ピラミッド処理を実装したプログラムである.cv2.pyrDownとcv2.pyrUpにより画像の解像度を階層的に変更する.低解像度化と高解像度化の双方向の解像度変換を行う.


import cv2
import urllib.request

url = "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/fruits.jpg"
urllib.request.urlretrieve(url, "fruits.jpg")

img = cv2.imread("fruits.jpg")
lower = cv2.pyrDown(img)
higher = cv2.pyrUp(lower)
cv2.imshow('Lower Resolution', lower)
cv2.imshow('Higher Resolution', higher)
cv2.waitKey(0)
cv2.destroyAllWindows()

テンプレートマッチング

概要

テンプレートマッチングは,画像内での特定パターンの検出を行う手法である.パターン検出により,画像内の類似部分を特定する.

ソースコード

テンプレートマッチングを実装したプログラムである.相関法により画像とテンプレートの類似度を計算し,正規化相互相関係数を用いた検出を実現する.cv2.matchTemplateと正規化相互相関係数(TM_CCOEFF_NORMED)を用いて画像内の類似パターンを検出する.閾値0.8以上の類似度を持つ領域を白色矩形で表示する.


import cv2
import numpy as np
import urllib.request

url = "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/smarties.png"
urllib.request.urlretrieve(url, "smarties.png")
img = cv2.imread("smarties.png", cv2.IMREAD_COLOR)  # カラー画像として読み込み

# テンプレート領域
template_x, template_y = 350, 50
template_w, template_h = 60, 60
template = img[template_y:template_y+template_h, template_x:template_x+template_w].copy()  # copyで切り出し

# 表示用画像のコピー作成
img_display = img.copy()

# テンプレートマッチング
result = cv2.matchTemplate(img, template, cv2.TM_CCOEFF_NORMED)

# スコア0.8以上の位置を緑色で表示
locations = np.where(result >= 0.8)
for y, x in zip(*locations):
    _ = cv2.rectangle(img_display, (x, y), (x+template_w, y+template_h), (0,255,0), 2)

# テンプレート位置を赤色で表示
_ = cv2.rectangle(img_display, (template_x,template_y), (template_x+template_w,template_y+template_h), (0,0,255), 2)

cv2.imshow('Template Matching', img_display)
cv2.waitKey(0)
cv2.destroyAllWindows()

適応的閾値処理

概要

適応的閾値処理は,画像の局所的な明るさに基づいて二値化を行う手法である.局所的な二値化により,照明変動の影響を抑制する.適応的二値化では局所領域ごとに閾値を計算し二値化を行う.

ソースコード

適応的閾値処理のプログラムである.cv2.adaptiveThresholdを用いて局所的な明るさに基づく二値化を行う.画像サイズに応じたブロックサイズを設定し,ガウシアン重み付けによる局所閾値で処理する.


import cv2
import urllib.request

url = "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/sudoku.png"
urllib.request.urlretrieve(url, "sudoku.png")
img = cv2.imread("sudoku.png", 0)

# 画像サイズに応じたブロックサイズの設定
blockSize = max(3, min(img.shape[0], img.shape[1]) // 50)
if blockSize % 2 == 0:
   blockSize += 1  # ブロックサイズは奇数である必要がある

thresh = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
                            cv2.THRESH_BINARY, blockSize, 2)

cv2.imshow('Adaptive Threshold', thresh)
cv2.waitKey(0)
cv2.destroyAllWindows()

GrabCut画像セグメンテーション

概要

GrabCutは,画像の前景と背景を半自動的に分離するアルゴリズムである.グラフカットアルゴリズムにより,エネルギー最小化に基づくセグメンテーションを実現する.

ソースコード

GrabCutアルゴリズムによる画像セグメンテーションのプログラムである.マスク値が2(背景推定)または0(背景確定)の領域を背景として除去し,前景領域を抽出する.cv2.grabCutと65次元特徴空間のモデルを用いて前景と背景を分離する.グラフカットベースの最適化により,5回の反復で領域分割を行う.


import cv2
import numpy as np
import urllib.request

url = "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/messi5.jpg"
urllib.request.urlretrieve(url, "messi5.jpg")
img = cv2.imread("messi5.jpg")
mask = np.zeros(img.shape[:2], np.uint8)
bgdModel = np.zeros((1, 65), np.float64)
fgdModel = np.zeros((1, 65), np.float64)
rect = (50, 50, 450, 290)
cv2.grabCut(img, mask, rect, bgdModel, fgdModel, 5, cv2.GC_INIT_WITH_RECT)
mask2 = np.where((mask == 2) | (mask == 0), 0, 1).astype('uint8')
img = img * mask2[:, :, np.newaxis]
cv2.imshow('GrabCut', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

カラー画像表示

概要

カラー画像を読み込み,画像幅が1024ピクセルを超える場合には,画像幅に応じて1/2倍または1/4倍で,INTER_AREA法による面積平均を用いた縮小処理を行う.

ソースコード

画像の縮小のプログラムである.OpenCVの画像読み込み(imread)によりBGRカラー形式で画像データを取得し,cv2.resizeとINTER_AREA法で縮小処理を行う.


import cv2
import urllib.request

# 画像のダウンロード
url = "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/fruits.jpg"
urllib.request.urlretrieve(url, "fruits.jpg")

# 画像表示
img = cv2.imread("fruits.jpg")
height, width = img.shape[:2]

# 幅を1024以下にするための1/2または1/4倍リサイズ
if width > 1024:
   scale_factor = 0.5 if width / 2 > 1024 else 0.25
   new_width = int(width * scale_factor)
   new_height = int(height * scale_factor)
   resized_img = cv2.resize(img, (new_width, new_height),
                           interpolation=cv2.INTER_AREA)
else:
   resized_img = img

cv2.imshow('Resized Image', resized_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

パソコンカメラ表示

概要

パソコンのカメラから映像を取得し,映像幅に応じて,INTER_AREA法で,1/2倍または1/4倍の縮小を行う.

ソースコード

PCカメラの画像の映像処理のプログラムである.cv2.VideoCaptureでカメラからフレームを取得し,INTER_AREA法で縮小処理を行う.qキーの入力で処理を終了する.


import cv2
# カメラのキャプチャ
cap = cv2.VideoCapture(0)
while cap.isOpened():
   ret, frame = cap.read()
   if not ret:
       break
   height, width = frame.shape[:2]

   # 幅を1024以下にするための1/2または1/4倍リサイズ
   if width > 1024:
       scale_factor = 0.5 if width / 2 > 1024 else 0.25
       new_width = int(width * scale_factor)
       new_height = int(height * scale_factor)
       resized_frame = cv2.resize(frame, (new_width, new_height),
                                interpolation=cv2.INTER_AREA)
   else:
       resized_frame = frame
   cv2.imshow('Webcam', resized_frame)
   if cv2.waitKey(1) & 0xFF == ord('q'):
       break
cap.release()
cv2.destroyAllWindows()