統計的背景モデルによる微細変化検出

概要

動画やウェブカメラの映像から微細な変化を検出するプログラムである。適応的混合ガウス背景モデル(MOG2)とCLAHE照明正規化を用いて、人の通過による明るさ変化や影の変化など、通常の動体検出では見落とされる小さな変化を捉える。感度調整機能やリアルタイム表示機能を備える。

目次

関連する外部ページ

サイト内の関連情報

第1章 プログラム利用ガイド

1. このプログラムの利用シーン

動画やウェブカメラの映像から、微細な変化を検出するためのソフトウェアである。人の通過による壁の明るさ変化、物体の移動や出現、影の変化など、通常の動体検出では見落とされるような小さな変化を捉えることができる。監視カメラの映像解析、実験環境での変化の記録、侵入検知システムなどに活用できる。

2. 主な機能

3. 基本的な使い方

  1. 起動と入力の選択

    プログラムを起動すると、入力ソースの選択画面が表示される。キーボードで0(動画ファイル)、1(ウェブカメラ)、2(サンプル動画)のいずれかを入力し、Enterキーを押す。0を選択した場合は、ファイル選択ダイアログが表示される。

  2. 初期化の待機

    最初の30フレームで通常の状態を学習する。この間、画面に「初期化中... n/30」と表示される。初期化が完了すると、変化検出が開始される。

  3. 感度の調整

    「感度調整」ウィンドウのスライダーをマウスで動かし、検出感度を調整する。値を小さくすると微細な変化まで検出し、大きくすると明確な変化のみを検出する。

  4. 終了方法

    映像が表示されている画面を選択した状態で、キーボードのqキーを押す。または「感度調整」ウィンドウの「終了」ボタンをクリックする。処理結果はresult.txtファイルに保存される。

4. 便利な機能

第2章 Python開発環境、ライブラリ類

ここでは、最低限の事前準備について説明する。機械学習や深層学習を行う場合は、NVIDIA CUDA、Visual Studio、Cursorなどを追加でインストールすると便利である。これらについては別ページ https://www.kkaneko.jp/cc/dev/aiassist.html で解説している。

第3章 Python 3.12 のインストール

Pythonのインストールを行い、Pythonのプログラムを実行する環境を整える。扱う環境は、Windows搭載パソコンである。金子研究室では、Python 3.12.10を推奨する。

[Windows での Python 3.12 のインストール手順を見るには、ここをクリック]

Windows での Python 3.12 のインストール

以下のいずれかの方法でPython 3.12をインストールする。Pythonがインストール済みの場合、この手順は不要である。

方法 1:winget によるインストール

インストールコマンドの実行方法

管理者権限コマンドプロンプトを起動する(手順:Windowsキーまたはスタートメニュー → cmd と入力 → 右クリック → 「管理者として実行」)。そして、コマンド全体をコマンドプロンプトにコピー&ペーストする。

--scope machine を指定することで、システム全体(全ユーザー向け)にインストールされる。このオプションの実行には管理者権限が必要である。インストール完了後、コマンドプロンプトを再起動するとPATHが反映される。

REM Python 3.12 をシステム領域にインストール
winget install --id Python.Python.3.12 -e --scope machine --silent --accept-source-agreements --accept-package-agreements --override "/quiet InstallAllUsers=1 PrependPath=1 Include_test=0 Include_pip=1 Include_launcher=1 InstallLauncherAllUsers=1 TargetDir=\"C:\Program Files\Python312\""
if not "%ERRORLEVEL%"=="0" ( color 0c & echo Python 3.12 のインストールに失敗しました & ping 127.0.0.1 -n 6 >nul & color )

REM Python と Scripts を PATH 先頭に追加
powershell -NoProfile -Command "$p='C:\Program Files\Python312'; $s=\"$p\Scripts\"; if(Test-Path $p){$k=[Microsoft.Win32.Registry]::LocalMachine.OpenSubKey('SYSTEM\CurrentControlSet\Control\Session Manager\Environment',$true); $c=$k.GetValue('Path','',[Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames); $t=$k.GetValueKind('Path'); $new=$c; if((';'+$new+';') -notlike \"*;$p;*\"){$new=$p+';'+$new}; if((';'+$new+';') -notlike \"*;$s;*\"){$new=$s+';'+$new}; if($new -ne $c){$k.SetValue('Path',$new,$t)}; $k.Close()}"

REM 現在のセッションにも反映(システムPATHを再取得して連結)
for /f "usebackq tokens=2,*" %A in (`reg query "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /v Path`) do set "PATH=%B"

REM pip / wheel の更新
python -m pip install --no-user -U pip wheel
if not "%ERRORLEVEL%"=="0" ( color 0c & echo pip / wheel の更新に失敗しました & ping 127.0.0.1 -n 6 >nul & color )

方法 2:インストーラーによるインストール

  1. Python公式サイト(https://www.python.org/downloads/)にアクセスし、「Download Python 3.x.x」ボタンからWindows用インストーラーをダウンロードする。
  2. ダウンロードしたインストーラーを実行する。
  3. 初期画面の下部に表示される「Add python.exe to PATH」にチェックを入れてから「Customize installation」を選択する。このチェックを入れ忘れると、コマンドプロンプトから python コマンドを実行できない。
  4. 「Install Python 3.xx for all users」にチェックを入れ、「Install」をクリックする。

インストールの確認

コマンドプロンプトで以下を実行する。

python --version

バージョン番号(例:Python 3.12.x)が表示されればインストール成功である。「'python' は、内部コマンドまたは外部コマンドとして認識されていません。」と表示される場合は、インストールが正常に完了していない。

第4章 Python の開発環境 Visual Studio Code のインストールと Python 用の設定

Python の開発環境Visual Studio Code(プログラムを編集するソフトウェア。以下、VS Code)を整える。

[Windows での Visual Studio Code のインストールと Python 用の設定手順を見るには、ここをクリック]

Windows での Visual Studio Code のインストールと Python 用の設定手順

1. VS Code と拡張機能のインストール

以下のコマンドにより,既存の VS Code を削除し,全ユーザー共有の設定で再インストールしたうえで,拡張機能(VS Code に機能を追加するソフトウェア)をまとめて導入する.

インストールコマンドの実行方法

管理者権限コマンドプロンプトを起動する(手順:Windows キーまたはスタートメニュー → cmd と入力 → 右クリック → 「管理者として実行」)。そして,コマンド全体をコマンドプロンプトにコピー&ペーストする。

インストールコマンド


REM ============================================================
REM Microsoft Visual Studio Code
REM ============================================================
REM Build Tools + Desktop development with C++(VCTools)+ 追加コンポーネント(一括)
REM 未インストール時: winget で新規インストール
REM インストール済み時: setup.exe modify でコンポーネント追加(バージョンは変更しない)
winget list --id Microsoft.VisualStudio.BuildTools 2>nul | findstr /i "BuildTools" >nul 2>&1
if %ERRORLEVEL% EQU 0 (
    for /f "usebackq delims=" %P in (`"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -products Microsoft.VisualStudio.Product.BuildTools -property installationPath`) do start /wait "" "C:\Program Files (x86)\Microsoft Visual Studio\Installer\setup.exe" modify --installPath "%P" --add Microsoft.VisualStudio.Workload.VCTools --add Microsoft.VisualStudio.Workload.MSBuildTools --add Microsoft.VisualStudio.Component.VC.CMake.Project --add Microsoft.VisualStudio.Component.VC.Llvm.Clang --add Microsoft.VisualStudio.Component.VC.Llvm.ClangToolset --add Microsoft.VisualStudio.Component.Windows11SDK.26100 --add Microsoft.VisualStudio.Component.VC.v143.x86.x64 --includeRecommended --quiet --norestart --nocache
    if not "%ERRORLEVEL%"=="0" ( color 0c & echo Build Tools のコンポーネント追加に失敗しました & ping 127.0.0.1 -n 6 >nul & color )
) else (
    winget install --scope machine --id Microsoft.VisualStudio.BuildTools -e --silent --disable-interactivity --force --accept-source-agreements --accept-package-agreements --override "--quiet --wait --norestart --nocache --add Microsoft.VisualStudio.Workload.VCTools --includeRecommended --add Microsoft.VisualStudio.Workload.MSBuildTools --add Microsoft.VisualStudio.Component.VC.CMake.Project --add Microsoft.VisualStudio.Component.VC.Llvm.Clang --add Microsoft.VisualStudio.Component.VC.Llvm.ClangToolset --add Microsoft.VisualStudio.Component.Windows11SDK.26100 --add Microsoft.VisualStudio.Component.VC.v143.x86.x64"
    if not "%ERRORLEVEL%"=="0" ( color 0c & echo Build Tools のインストールに失敗しました & ping 127.0.0.1 -n 6 >nul & color )
)

REM 全ユーザー共有の拡張機能フォルダ
if not exist "C:\ProgramData\vscode-extensions" mkdir "C:\ProgramData\vscode-extensions"
icacls "C:\ProgramData\vscode-extensions" /grant "Everyone:(OI)(CI)M" /T

REM スタートメニューのショートカットを --extensions-dir 付きで再作成
if exist "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Visual Studio Code" rmdir /s /q "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Visual Studio Code"
if exist "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Visual Studio Code.lnk" del "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Visual Studio Code.lnk"
powershell -NoProfile -Command "$s=New-Object -ComObject WScript.Shell; $lnk=$s.CreateShortcut('C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Visual Studio Code.lnk'); $lnk.TargetPath='C:\Program Files\Microsoft VS Code\Code.exe'; $lnk.Arguments='--extensions-dir \"C:\ProgramData\vscode-extensions\"'; $lnk.Save()"
REM ショートカットの検証
powershell -NoProfile -Command "$s=New-Object -ComObject WScript.Shell; $lnk=$s.CreateShortcut('C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Visual Studio Code.lnk'); Write-Host 'TargetPath:' $lnk.TargetPath; Write-Host 'Arguments:' $lnk.Arguments"

REM ファイル / フォルダ右クリックの「Code で開く」を登録
reg add "HKLM\SOFTWARE\Classes\*\shell\VSCode\command" /ve /d "\"C:\Program Files\Microsoft VS Code\Code.exe\" --extensions-dir \"C:\ProgramData\vscode-extensions\" \"%1\"" /f
reg add "HKLM\SOFTWARE\Classes\Directory\shell\VSCode\command" /ve /d "\"C:\Program Files\Microsoft VS Code\Code.exe\" --extensions-dir \"C:\ProgramData\vscode-extensions\" \"%1\"" /f
reg add "HKLM\SOFTWARE\Classes\Directory\Background\shell\VSCode\command" /ve /d "\"C:\Program Files\Microsoft VS Code\Code.exe\" --extensions-dir \"C:\ProgramData\vscode-extensions\" \"%V\"" /f

REM --extensions-dir 付きで起動する code.cmd ラッパを作成
REM (%* を echo で書くと対話的 cmd で失われるため、PowerShell で [char]37+'*' を書き出す)
powershell -NoProfile -Command "$pct=[char]37; $q=[char]34; $c='@echo off'+[char]13+[char]10+$q+'C:\Program Files\Microsoft VS Code\bin\code.cmd'+$q+' --extensions-dir '+$q+'C:\ProgramData\vscode-extensions'+$q+' '+$pct+'*'+[char]13+[char]10; [IO.File]::WriteAllText('C:\ProgramData\vscode-extensions\vscode.cmd',$c,[Text.Encoding]::ASCII)"

REM 拡張機能のインストール
set "CODE=C:\Program Files\Microsoft VS Code\bin\code.cmd"
"%CODE%" --extensions-dir "C:\ProgramData\vscode-extensions" --uninstall-extension GitHub.copilot
"%CODE%" --extensions-dir "C:\ProgramData\vscode-extensions" --uninstall-extension GitHub.copilot-chat
"%CODE%" --extensions-dir "C:\ProgramData\vscode-extensions" --install-extension ms-python.python
"%CODE%" --extensions-dir "C:\ProgramData\vscode-extensions" --install-extension ms-python.vscode-pylance
"%CODE%" --extensions-dir "C:\ProgramData\vscode-extensions" --install-extension ms-python.debugpy
"%CODE%" --extensions-dir "C:\ProgramData\vscode-extensions" --install-extension MS-CEINTL.vscode-language-pack-ja
"%CODE%" --extensions-dir "C:\ProgramData\vscode-extensions" --install-extension saoudrizwan.claude-dev
"%CODE%" --extensions-dir "C:\ProgramData\vscode-extensions" --install-extension rust-lang.rust-analyzer
"%CODE%" --extensions-dir "C:\ProgramData\vscode-extensions" --install-extension tamasfe.even-better-toml
"%CODE%" --extensions-dir "C:\ProgramData\vscode-extensions" --install-extension anthropic.claude-code
"%CODE%" --extensions-dir "C:\ProgramData\vscode-extensions" --install-extension almenon.arepl
"%CODE%" --extensions-dir "C:\ProgramData\vscode-extensions" --list-extensions --show-versions

REM settings.json を作成(自動更新オフ、Python、Claude Code 設定)
if not exist "%APPDATA%\Code\User" mkdir "%APPDATA%\Code\User"
python -c "import json,os;data={'update.mode':'none','update.enableWindowsBackgroundUpdates':False,'extensions.autoUpdate':False,'python.defaultInterpreterPath':r'C:\Program Files\Python312\python.exe','claudeCode.environmentVariables':[{'name':'ANTHROPIC_API_KEY','value':'not-needed'},{'name':'ANTHROPIC_AUTH_TOKEN','value':'ollama'},{'name':'ANTHROPIC_BASE_URL','value':'http://localhost:11434'},{'name':'ANTHROPIC_MODEL','value':'glm-4.7-flash'},{'name':'CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC','value':'1'}]};p=os.path.join(os.environ['APPDATA'],'Code','User','settings.json');open(p,'w',encoding='utf-8').write(json.dumps(data,indent=4));print('Done:',p)"

REM 自動更新の抑止ポリシー(settings.json に加えて、レジストリ側でも明示的にオフ)
reg add "HKLM\SOFTWARE\Policies\Microsoft\VSCode" /v "UpdateMode" /t REG_SZ /d "none" /f
echo === セットアップ完了 ===

2. Python インタプリタの選択

同一マシンに複数の Python がインストールされている場合,VS Code で使用する Python 本体(インタプリタ:Python プログラムを解釈・実行するソフトウェア)を選択する必要がある.

  1. コマンドパレット(コマンド名で機能を呼び出す VS Code の入力欄)を開く(Ctrl+Shift+P
  2. Python: Select Interpreter と入力する
  3. 表示される一覧から,使用する Python(例:C:\Program Files\Python312\python.exe)を選択する.

必要なライブラリをシステム領域にインストール

管理者権限コマンドプロンプトを起動する (手順:Windowsキーまたはスタートメニュー → cmd と入力 → 右クリック → 「管理者として実行」)。

起動したコマンドプロンプトで次を実行する。--no-user は、ユーザ領域ではなくシステム領域へインストールするためのオプションである。

pip install -U --no-user opencv-python numpy pillow

第5章 プログラムコードの説明

1. 概要

このプログラムは、動画フレームから微細な変化領域を検出する。各画素の色を複数のガウス分布の混合として表現し、統計的背景モデルを継続的に更新することで、照明変化や影などの微細な明るさ変化を検出する。

2. 主要技術

適応的混合ガウス背景モデル(MOG2)

各画素をK個のガウス分布の混合として表し、オンライン更新を行う背景モデリング手法である。StaufferとGrimsonが1999年に提案した手法[1]を、Zivkovicが2004年に改良したものがMOG2である[2]。MOG2は、画素ごとに必要なガウス分布の個数を自動的に選ぶ点が元の手法と異なる。背景が単純な画素では分布の個数が減り、木の葉の揺れのように背景が複雑な画素では個数が増えるため、検出の精度と処理速度がともに向上する[3]。各ガウス分布の重みωと標準偏差σの比を計算し、この比が大きい成分ほど安定した背景である可能性が高いと判断される。新しいフレームの各画素について、統計的なしきい値判定により背景か前景かを分類する。OpenCVでは cv2.createBackgroundSubtractorMOG2 として実装されている[4]

適応的ヒストグラム均等化(CLAHE)

Zuiderveldが1994年に開発した照明正規化手法である[5]。画像を矩形領域(タイル)に分割し、各領域でヒストグラム均等化を実行する。ヒストグラムのクリップ限界を設定することでノイズの過度な増幅を防ぎ、タイル間の境界は双線形補間で滑らかに接続する。

3. 技術的特徴

4. 実装の特色

このプログラムは動画のリアルタイム処理に対応し、以下の特徴を持つ。

入力と初期化

検出処理と後処理

インターフェースと出力

5. 参考文献

[1] Stauffer, C., & Grimson, W. E. L. (1999). Adaptive background mixture models for real-time tracking. Proceedings. 1999 IEEE Computer Society Conference on Computer Vision and Pattern Recognition, 2, 246-252. https://ieeexplore.ieee.org/document/784637

[2] Zivkovic, Z. (2004). Improved adaptive Gaussian mixture model for background subtraction. Proceedings of the 17th International Conference on Pattern Recognition (ICPR 2004), 2, 28-31. https://ieeexplore.ieee.org/document/1333992

[3] Zivkovic, Z., & van der Heijden, F. (2006). Efficient adaptive density estimation per image pixel for the task of background subtraction. Pattern Recognition Letters, 27(7), 773-780. https://www.sciencedirect.com/science/article/pii/S0167865505003521

[4] OpenCV. How to Use Background Subtraction Methods. https://docs.opencv.org/4.x/d1/dc5/tutorial_background_subtraction.html

[5] Zuiderveld, K. (1994). Contrast limited adaptive histogram equalization. In P. S. Heckbert (Ed.), Graphics Gems IV (pp. 474-485). Academic Press Professional.

6. ソースコード

# プログラム名: 統計的背景モデル(MOG2)による微細変化検出プログラム
# 特徴技術名: 適応的混合ガウス背景モデル MOG2(Zivkovic 2004)
# 出典: Zivkovic, Z. (2004). Improved adaptive Gaussian mixture model for background subtraction. ICPR 2004.
# 特徴機能: 各画素を混合ガウスでモデル化し、画素ごとに必要な成分数を自動的に選ぶ。ω/σに基づく背景集合と、標準偏差で正規化した距離によるマッチ判定により、照明変化に頑健な微細変化検出を実現する。
# 学習済みモデル: 使用なし
# 方式設計:
#   - 関連利用技術:
#     - OpenCV: 画像処理ライブラリ(背景差分、形態学的処理、輪郭検出、色空間変換、CLAHE)
#     - NumPy: 数値計算ライブラリ(配列演算)
#     - tkinter: GUIライブラリ(感度調整インターフェース)
#     - Pillow: 日本語フォント描画
#     - CLAHE: 局所的適応的ヒストグラム均等化による照明正規化
#   - 入力と出力: 入力: 動画(ユーザは「0:動画ファイル,1:カメラ,2:サンプル動画」のメニューで選択.0:動画ファイルの場合はtkinterでファイル選択.1の場合はOpenCVでカメラが開く.2の場合はhttps://raw.githubusercontent.com/opencv/opencv/master/samples/data/vtest.aviを使用)、出力: OpenCV画面でリアルタイム表示(変化領域を赤色オーバーレイ)、各フレームごとにFPSと変化率をprint()表示、終了時にresult.txtに保存
#   - 処理手順:
#     1. 入力フレームの取得とガウシアンフィルタ適用
#     2. 照明正規化処理(LabのLチャネルにCLAHE)
#     3. MOG2による背景判定(ω/σ降順の累積重みTで背景集合を定義)
#     4. 標準偏差で正規化した距離によるマッチ判定(感度パラメータで制御)
#     5. 形態学的処理によるノイズ除去
#     6. 最小領域フィルタによる小領域除去
#   - 前処理、後処理:
#     - 前処理: ガウシアンフィルタによるノイズ除去、CLAHE照明正規化
#     - 後処理: 形態学的オープニング・クロージング、最小領域フィルタ
#   - 追加処理:
#     - 適応的背景更新: 学習率αに基づくMOG2更新
#     - 統計的しきい値: 標準偏差で正規化した距離のしきい値(varThreshold)
#     - バウンディングボックス描画: 検出領域の可視化と領域情報の表示
#   - 調整を必要とする設定値:
#     - sensitivity(基本感度): 変化検出のしきい値を制御(0.5-3.0、既定値1.5)
#     - min_change_area(最小変化領域): ノイズ除去のための最小面積(10-200ピクセル、既定値50)
#     - learning_rate(学習率α): 背景モデルの更新速度(0.001-0.1、既定値0.01)
# 将来方策: sensitivity値の自動調整機能。初期30フレームの標準偏差分布を分析し、ヒストグラムの分散から背景の複雑さを定量化。分散値に基づいてsensitivity値を0.5-3.0の範囲で自動設定
# その他の重要事項: 微細な影や明るさ変化も検出可能な設計
# 前準備: pip install -U --no-user opencv-python numpy pillow

import cv2
import tkinter as tk
from tkinter import filedialog, Scale, Label, Button
import numpy as np
import time
import threading
import urllib.request
from datetime import datetime
from PIL import Image, ImageDraw, ImageFont

# 設定値(調整可能なパラメータ)
SENSITIVITY = 1.5        # 変化検出の感度(0.5-3.0、値が大きいほど感度が低い)
MIN_CHANGE_AREA = 50     # 最小変化領域(ピクセル、小さいノイズを除去)
LEARNING_RATE = 0.01     # 背景モデルの学習率α(0.001-0.1、値が大きいほど背景の更新が速い)
BUFFER_SIZE = 30         # 背景モデル初期化用のフレーム数
GAUSSIAN_KERNEL = 5      # ガウシアンフィルタのカーネルサイズ(3,5,7など奇数)
MORPH_KERNEL = 3         # 形態学的処理のカーネルサイズ(3,5,7など奇数)
CLAHE_CLIP_LIMIT = 2.0   # CLAHEのクリップリミット(1.0-4.0)
CLAHE_GRID_SIZE = 8      # CLAHEのグリッドサイズ(通常8)

# MOG2設定
K_COMPONENTS = 5     # 混合成分数の上限(3〜5が一般的)
BG_RATIO_T = 0.7     # 背景とみなす累積重みのしきい値T(0.7〜0.9)
INIT_VAR = 225.0     # 初期分散(15^2)
MIN_VAR = 64.0       # 分散の下限(8^2)

# 日本語フォント設定
FONT_PATH = 'C:/Windows/Fonts/meiryo.ttc'
FONT_SIZE = 20
font = ImageFont.truetype(FONT_PATH, FONT_SIZE)

# グローバル変数
sensitivity = SENSITIVITY
min_area = MIN_CHANGE_AREA
learning_rate = LEARNING_RATE
running = True
frame_count = 0
results_log = []

# CLAHEオブジェクトを事前に作成
clahe = cv2.createCLAHE(clipLimit=CLAHE_CLIP_LIMIT, tileGridSize=(CLAHE_GRID_SIZE, CLAHE_GRID_SIZE))

# 背景モデル(MOG2)
back_sub = cv2.createBackgroundSubtractorMOG2(
    history=BUFFER_SIZE, varThreshold=SENSITIVITY ** 2, detectShadows=False)
back_sub.setNMixtures(K_COMPONENTS)
back_sub.setBackgroundRatio(BG_RATIO_T)
back_sub.setVarInit(INIT_VAR)
back_sub.setVarMin(MIN_VAR)


def draw_japanese_text(img, text, position, color=(0, 255, 0)):
    """OpenCV画像(BGR)に日本語テキストを描画する"""
    img_pil = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
    ImageDraw.Draw(img_pil).text(position, text, font=font, fill=color[::-1])
    return cv2.cvtColor(np.array(img_pil), cv2.COLOR_RGB2BGR)


def video_frame_processing(frame):
    global frame_count
    current_time = time.time()
    frame_count += 1

    # ガウシアンフィルタによるノイズ除去
    blurred = cv2.GaussianBlur(frame, (GAUSSIAN_KERNEL, GAUSSIAN_KERNEL), 0)
    # Lab色空間に変換し、Lチャンネルに対してCLAHEを適用(照明正規化)
    lab = cv2.cvtColor(blurred, cv2.COLOR_BGR2Lab)
    lab[:, :, 0] = clahe.apply(lab[:, :, 0])

    # 背景モデルの更新と前景マスク生成
    back_sub.setVarThreshold(sensitivity ** 2)
    fg_mask = back_sub.apply(lab, learningRate=learning_rate)

    # 初期化期間中は検出結果を表示しない
    if frame_count <= BUFFER_SIZE:
        msg = f'初期化中... {frame_count}/{BUFFER_SIZE}'
        return draw_japanese_text(frame.copy(), msg, (10, 30)), msg, current_time

    # 形態学的処理
    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (MORPH_KERNEL, MORPH_KERNEL))
    opened = cv2.morphologyEx(fg_mask, cv2.MORPH_OPEN, kernel)
    closed = cv2.morphologyEx(opened, cv2.MORPH_CLOSE, kernel)

    # 最小領域フィルタとバウンディングボックス
    contours, _ = cv2.findContours(closed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    filtered_mask = np.zeros_like(closed)
    bounding_boxes = []

    for contour in contours:
        if cv2.contourArea(contour) >= min_area:
            cv2.drawContours(filtered_mask, [contour], -1, 255, -1)
            bounding_boxes.append(cv2.boundingRect(contour))

    # 変化領域を赤色でオーバーレイ
    overlay = np.zeros_like(frame)
    overlay[:, :, 2] = filtered_mask
    output = cv2.addWeighted(frame, 0.7, overlay, 0.3, 0)

    # バウンディングボックスを描画
    for x, y, w, h in bounding_boxes:
        cv2.rectangle(output, (x, y), (x + w, y + h), (0, 255, 0), 2)

    # 変化率計算
    change_pixels = cv2.countNonZero(filtered_mask)
    change_rate = (change_pixels / (frame.shape[0] * frame.shape[1])) * 100

    # テキスト表示
    output = draw_japanese_text(output, f'変化率: {change_rate:.2f}%', (10, 30))
    output = draw_japanese_text(output, f'感度: {sensitivity:.1f}', (10, 60))
    output = draw_japanese_text(output, f'検出領域数: {len(bounding_boxes)}', (10, 90))

    result = f'変化率: {change_rate:.2f}%, 領域数: {len(bounding_boxes)}'

    return output, result, current_time


def update_sensitivity(value):
    global sensitivity
    sensitivity = float(value)


def stop_processing():
    global running
    running = False
    root.quit()


def request_gui_stop():
    root.after(0, root.quit)


def video_processing_thread(cap):
    global running
    MAIN_FUNC_DESC = '微細変化検出'
    try:
        prev_t = time.perf_counter()
        while running:
            ret, frame = cap.read()
            if not ret:
                break

            processed_frame, result, current_time = video_frame_processing(frame)
            cv2.imshow(MAIN_FUNC_DESC, processed_frame)

            now_t = time.perf_counter()
            dt = now_t - prev_t
            fps = (1.0 / dt) if dt > 0 else 0.0
            prev_t = now_t
            result = f'FPS: {fps:.2f}, {result}'

            if is_camera:
                print(datetime.fromtimestamp(current_time).strftime('%Y-%m-%d %H:%M:%S.%f')[:-3], result)
            else:
                print(frame_count, result)

            results_log.append(result)

            if cv2.waitKey(1) & 0xFF == ord('q'):
                break
    finally:
        running = False
        cap.release()
        cv2.destroyAllWindows()
        request_gui_stop()


# ガイダンス表示
print('\n=== 統計的背景モデル(MOG2)による微細変化検出プログラム ===')
print('概要: 動画から微細な明るさ変化を含むすべての変化を検出します')
print('操作方法:')
print('  q キー: プログラム終了')
print('  感度調整ウィンドウで検出感度を調整可能')
print('注意事項:')
print('  初期化に30フレーム必要です')
print('  検出結果は自動的にresult.txtに保存されます')

root = tk.Tk()
root.withdraw()

print('\n0: 動画ファイル')
print('1: カメラ')
print('2: サンプル動画')

choice = input('選択: ')
is_camera = (choice == '1')

if choice == '0':
    path = filedialog.askopenfilename()
    if not path:
        exit()
    cap = cv2.VideoCapture(path)
elif choice == '1':
    cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
    if not cap.isOpened():
        cap = cv2.VideoCapture(0)
    cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
else:
    SAMPLE_URL = 'https://raw.githubusercontent.com/opencv/opencv/master/samples/data/vtest.avi'
    SAMPLE_FILE = 'vtest.avi'
    urllib.request.urlretrieve(SAMPLE_URL, SAMPLE_FILE)
    cap = cv2.VideoCapture(SAMPLE_FILE)

if not cap.isOpened():
    print('動画ファイル・カメラを開けませんでした')
    exit()

# 感度調整ウィンドウを作成
control_window = tk.Toplevel(root)
control_window.title('感度調整')
control_window.geometry('300x200')

label = Label(control_window, text='Sensitivity (感度)')
label.pack(pady=10)

scale = Scale(control_window, from_=0.5, to=3.0, resolution=0.1,
              orient='horizontal', length=250, command=update_sensitivity)
scale.set(sensitivity)
scale.pack(pady=10)

value_label = Label(control_window, text='')
value_label.pack()

stop_button = Button(control_window, text='終了', command=stop_processing)
stop_button.pack(pady=10)


def update_value_label():
    if running:
        value_label.config(text=f'現在の値: {sensitivity:.1f}')
        control_window.after(100, update_value_label)


update_value_label()

# 動画処理を別スレッドで開始
video_thread = threading.Thread(target=video_processing_thread, args=(cap,))
video_thread.daemon = True
video_thread.start()

print('\n=== 動画処理開始 ===')
print('操作方法:')
print('  q キー: プログラム終了')

# tkinterのメインループ
root.mainloop()

# スレッドの終了を待つ
running = False
video_thread.join()

print('\n=== プログラム終了 ===')
if results_log:
    with open('result.txt', 'w', encoding='utf-8') as f:
        f.write('=== 処理結果 ===\n')
        f.write(f'処理フレーム数: {frame_count}\n')
        f.write('\n')
        f.write('\n'.join(results_log))
    print('処理結果をresult.txtに保存しました')

第6章 実験・研究スキルの基礎:Windowsで学ぶ微細変化検出実験

1. 実験・研究のスキル構成要素

実験や研究を行うには、以下の5つの構成要素を理解する必要がある。

1.1 実験用データ

このプログラムでは、動画ファイルまたはウェブカメラの映像を実験用データとして使用する。

1.2 実験計画

何を明らかにするために実験を行うのかを定める。

計画例

1.3 プログラム

実験を実施するためのツールである。このプログラムは適応的混合ガウス背景モデル(MOG2)とCLAHE照明正規化を使用している。

1.4 プログラムの機能

このプログラムは複数のパラメータで微細変化検出を制御する。

入力パラメータ

出力情報

スライダーの動作

1.5 検証(結果の確認と考察)

プログラムの実行結果を観察し、パラメータの影響を考察する。

基本認識

観察のポイント

2. 間違いの原因と対処方法

動画ファイルが開けない
  • 原因:ファイルパスが正しくない、またはコーデックが対応していない
  • 対処方法:選択2(サンプル動画)で動作を確認してから、自分の動画ファイルを試す
ウェブカメラが開けない
  • 原因:カメラが他のアプリケーションで使用中、またはアクセス許可がない
  • 対処方法:他のカメラアプリを終了し、カメラのアクセス許可を確認する
初期化が完了しない(30フレームで止まる)
  • 原因:動画が30フレーム未満、またはカメラ映像が取得できていない
  • 対処方法:カメラの接続を確認する。動画の場合は別のファイルを試す
感度を変えても検出結果が変化しない
  • 原因:元々変化がほとんどないシーン、または変化幅が大きすぎるか小さすぎる
  • 対処方法:感度パラメータを0.5から3.0まで大きく変化させて観察する。動きのあるシーンで試す
明らかな変化が検出されない
  • 原因:感度パラメータの値が大きすぎる、または背景モデルが変化に追従している
  • 対処方法:感度パラメータを0.5程度に設定する(値を小さくして敏感にする)。それでも検出されない場合は、変化が緩やかすぎて背景として学習されている可能性がある
画面全体が変化領域として検出される
  • 原因:感度パラメータの値が小さすぎる、またはカメラの揺れ
  • 対処方法:感度パラメータを1.5~2.0程度に設定する(値を大きくして反応を鈍くする)。カメラを固定する
小さなノイズが大量に検出される
  • 原因:最小変化領域の設定が小さすぎる
  • 対処方法:MIN_CHANGE_AREAを100~200に増やす。ソースコード内のパラメータを変更して実験する
照明が変化すると誤検出が増える
  • 原因:照明変化が急激すぎて背景モデルが追従できていない
  • 対処方法:これは正常な動作である。学習率(LEARNING_RATE)を0.02~0.05に上げると追従が速くなるが、本来の変化も見逃しやすくなるトレードオフがある

3. 実験レポートのサンプル

感度パラメータが微細変化検出に与える影響

実験目的

屋内環境で人の通過による微細な変化を検出しながら、ノイズによる誤検出を最小化するための感度設定を見つける。

実験計画

最小変化領域をxxxピクセルに固定し、感度パラメータを変化させて最適値を探す。

実験方法

プログラムを実行し、スライダーを操作しながら以下の基準で評価する。

実験結果

感度値 検出領域数 変化率(%) 正検出 誤検出 見逃し 総合評価
xxx xxx xxx xxx xxx xxx xxx
xxx xxx xxx xxx xxx xxx xxx
xxx xxx xxx xxx xxx xxx xxx
xxx xxx xxx xxx xxx xxx xxx
xxx xxx xxx xxx xxx xxx xxx

考察

結論

(例文)本実験の屋内環境においては、感度値xxx~xxxが最もバランスの取れた設定であった。微細な変化も含めてすべて記録したい場合はxxx、明確な変化のみを検出したい場合はxxxが適切である。また、初期化フェーズ(最初のxxxフレーム)で背景モデルが適切に構築されることが重要であり、カメラを固定し、人が映らない状態で開始する必要性が確認できた。実験を通じて、照明条件や環境に応じた適切なパラメータ調整の重要性を理解できた。