LINEA による線分検出(ソースコードと実行結果)

概要

LINEA は、Transformer(注意のしくみを使う深層学習モデル)にもとづく線分検出器である。線分の端点とその間の位置に注目する Line Attention により、Transformer 型でありながら実時間で動作する。このページでは、Windows 上での Python 3.12 の準備から、学習済みモデルの取得、動画の各フレームから線分を検出するプログラムの実行までを順に説明する。

目次

関連する外部ページ

サイト内の関連情報

第1章 Python開発環境,ライブラリ類

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

第2章 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' は、内部コマンドまたは外部コマンドとして認識されていません。」と表示される場合は、インストールが正常に完了していない。

第3章 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)を選択する.

第4章 必要なライブラリと LINEA のソースコードの取得

LINEA は、パッケージとして配布されているのではなく、ソースコード一式を取得して使う。そのため、取得に使う Git と、実行に必要なライブラリをインストールする。

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

次のコマンドを実行して、Git をインストールする。すでに Git を導入している場合は不要である。

winget install --id Git.Git -e --source winget --accept-source-agreements --accept-package-agreements

次のコマンドを実行して、システム領域にライブラリをインストールする(--no-user は、ユーザ領域ではなくシステム領域にインストールするためのオプションである)。PyTorch は CUDA 12.8 に対応した版をインストールする。

REM PyTorch(CUDA 12.8 対応版)を Python 3.12 の pip でインストールする
set "PYTHON_PATH=C:\Program Files\Python312"
"%PYTHON_PATH%\Scripts\pip" install --no-user -U torch torchvision --index-url https://download.pytorch.org/whl/cu128
"%PYTHON_PATH%\Scripts\pip" install --no-user -U opencv-python numpy pillow

次のコマンドを実行して、プログラムを置くディレクトリで LINEA のソースコードを linea という名前で取得し、LINEA が必要とするライブラリをインストールする。

REM LINEA のソースコードを linea という名前で取得する
git clone https://github.com/SebastianJanampa/LINEA.git linea

REM LINEA が必要とするライブラリをインストールする(-U は付けない。付けると PyTorch が CPU 版に入れ替わる)
"%PYTHON_PATH%\Scripts\pip" install --no-user -r linea\requirements.txt

ソースコードは、linea ディレクトリと同じ場所(linea の親ディレクトリ)に置いて実行する。学習済みモデルは、プログラムの実行時に自動でダウンロードされる。

第5章 LINEA による線分検出プログラム

概要

このプログラムは、動画の各フレームから線分を検出する。フレームを 640×640 に整えて LINEA に入力し、得られた線分のうち、信頼度が閾値以上のものを元画像に描画する。

主要技術

LINEA

Sebastian Janampa と Marios Pattichis が2025年に発表した線分検出モデル[1]。Deformable Line Attention(線分の端点と、端点の間の位置に注目する注意のしくみ)を導入し、COCO や Object365 のような大規模データセットでの事前学習を行わずに高い精度を得ている。従来のTransformer型の線分検出モデルより高速で、実時間での動画処理に使える。

HGNetv2 バックボーン

画像から特徴を取り出す部分(バックボーン)には HGNetv2 を用いる。LINEA には規模の異なる N・S・M・L の4種類のモデルがあり、このプログラムでは精度と速度のつり合いがよい LINEA-M(パラメータ数 13.5M)を用いる。

技術的特徴

学習済みモデルの自動取得

学習済みモデル linea_hgnetv2_m.pth は、公式リポジトリの配布先から自動でダウンロードされ、次回以降は保存されたものが使われる。

GPU と CPU の自動選択

CUDA が使える環境では GPU を、使えない環境では CPU を選ぶ。

信頼度による絞り込み

モデルは各線分に信頼度を付けて出力する。信頼度が 0.3 以上の線分だけを採用する。

実装の特色

動画のリアルタイム処理に対応し、次の機能を備える。

参考文献

[1] Janampa, S., & Pattichis, M. (2025). LINEA: Fast and Accurate Line Detection Using Scalable Transformers. IEEE International Conference on Image Processing (ICIP). https://arxiv.org/abs/2505.16264

ソースコード

"""
LINEAによる線分検出プログラム
特徴技術名: LINEA (Line detection using scalable transformers)
出典: Sebastian Janampa and Marios Pattichis. LINEA: Fast and Accurate Line Detection Using Scalable Transformers. IEEE International Conference on Image Processing (ICIP), 2025. arXiv:2505.16264
特徴機能: 線分の端点と端点の間の位置に注目するDeformable Line Attentionによる線分検出。大規模データセットでの事前学習を必要とせず、Transformer型でありながら実時間で動作する
学習済みモデル: linea_hgnetv2_m.pth(Wireframeデータセットで学習、HGNetv2バックボーンのLINEA-M、パラメータ数13.5M、https://github.com/SebastianJanampa/storage/releases/download/LINEA/linea_hgnetv2_m.pth)
方式設計:
  関連利用技術:
    - PyTorch(深層学習フレームワーク、テンソル演算とGPU加速処理)
    - torchvision(入力画像のリサイズと正規化)
    - OpenCV(画像処理、GUI表示、動画・カメラ入力処理)
    - NumPy(数値計算、配列操作)
    - Pillow(OpenCV画面への日本語テキスト描画)
  入力と出力:
    入力: 動画(ユーザは「0:動画ファイル,1:カメラ,2:サンプル動画」のメニューで選択.0:動画ファイルの場合はtkinterでファイル選択.1の場合はOpenCVでカメラが開く.2の場合はhttps://raw.githubusercontent.com/opencv/opencv/master/samples/data/vtest.aviを使用)
    出力: 処理結果が画像化できる場合にはOpenCV画面でリアルタイムに表示。OpenCV画面内に処理結果をテキストで表示。さらに、print()で処理結果を表示。プログラム終了時にprint()で表示した処理結果をresult.txtファイルに保存し、「result.txtに保存」したことをprint()で表示
  処理手順:
    1. 設定ファイルからLINEAを組み立て、学習済みモデルを読み込む
    2. フレームをRGBに変換し、640x640にリサイズして正規化
    3. LINEAで線分と信頼度を推論
    4. 元画像の座標系に戻された線分を信頼度で絞り込む
    5. 検出線分を元画像に描画して結果表示
  前処理: RGB変換、640x640へのリサイズ、平均と標準偏差による正規化
  後処理: 信頼度による絞り込み、線分座標の整数変換、結果可視化
  追加処理: フレーム数カウント、検出線分数の記録
  調整を必要とする設定値:
    - SCORE_THRESHOLD(0.3): 採用する線分の信頼度の下限
    - CONFIG_PATH/WEIGHTS_URL: 使用するモデルの規模(n, s, m, l から選ぶ)
将来方策: モデル規模の自動切り替えによる処理速度の調整
その他の重要事項: LINEAのソースコードをlineaという名前で取得し、その親ディレクトリでこのプログラムを実行する
前準備:
git clone https://github.com/SebastianJanampa/LINEA.git linea
pip install --no-user -U torch torchvision --index-url https://download.pytorch.org/whl/cu128
pip install --no-user -U opencv-python numpy pillow
pip install --no-user -r linea/requirements.txt
"""

import time
from datetime import datetime
import urllib.request
import tkinter as tk
from tkinter import filedialog

import cv2
import numpy as np
import torch
from torch import nn
import torchvision.transforms as T
from PIL import Image, ImageFont, ImageDraw

from linea.models import build_linea
from linea.util.slconfig import SLConfig

CONFIG_PATH = 'linea/configs/linea/linea_hgnetv2_m.py'
WEIGHTS_URL = 'https://github.com/SebastianJanampa/storage/releases/download/LINEA/linea_hgnetv2_m.pth'
SCORE_THRESHOLD = 0.3
FONT_PATH = 'C:/Windows/Fonts/meiryo.ttc'
FONT_SIZE = 20
MAIN_FUNC_DESC = '線分検出(左:入力画像/右:検出結果)'

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print(f'デバイス: {device}')

font = ImageFont.truetype(FONT_PATH, FONT_SIZE)
transforms = T.Compose([
    T.Resize((640, 640)),
    T.ToTensor(),
    T.Normalize(mean=[0.538, 0.494, 0.453], std=[0.257, 0.263, 0.273]),
])
frame_count = 0
results_log = []


class Linea(nn.Module):
    def __init__(self):
        super().__init__()
        cfg = SLConfig.fromfile(CONFIG_PATH)
        cfg.pretrained = False
        model, postprocessor = build_linea(cfg)
        state_dict = torch.hub.load_state_dict_from_url(
            WEIGHTS_URL, map_location='cpu', file_name='linea_hgnetv2_m.pth')
        model.load_state_dict(state_dict['model'], strict=True)
        self.model = model.deploy()
        self.postprocessor = postprocessor.deploy()

    def forward(self, images, orig_target_sizes):
        return self.postprocessor(self.model(images), orig_target_sizes)


print('LINEAのモデルを準備中...')
detector = Linea().to(device).eval()
print('LINEAのモデルの準備完了')


def draw_text(image, text):
    img_pil = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
    ImageDraw.Draw(img_pil).text((10, 10), text, font=font, fill=(0, 255, 0))
    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

    # RGBに変換し、640x640にリサイズして正規化した上でLINEAに入力
    height, width = frame.shape[:2]
    image = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
    im_data = transforms(image).unsqueeze(0).to(device)
    orig_size = torch.tensor([[width, height]]).to(device)
    with torch.no_grad():
        lines, scores = detector(im_data, orig_size)

    # 元画像の座標系に戻された線分を信頼度で絞り込む
    lines = lines[0].cpu().numpy()
    scores = scores[0].cpu().numpy()
    lines = lines[scores >= SCORE_THRESHOLD]

    detection_result = frame.copy()
    line_count = len(lines)
    for x1, y1, x2, y2 in lines:
        cv2.line(detection_result, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2)

    detection_result = draw_text(detection_result, f'フレーム: {frame_count}, 検出線分数: {line_count}')
    combined_frame = np.hstack([frame, detection_result])
    result = f'フレーム {frame_count}: 検出線分数 {line_count}'
    return combined_frame, result, current_time


print('入力ソースを選択してください:')
print('0: 動画ファイル')
print('1: カメラ')
print('2: サンプル動画')

choice = input('選択: ')

if choice == '0':
    root = tk.Tk()
    root.withdraw()
    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'
    print(f'サンプル動画をダウンロード中: {SAMPLE_URL}')
    urllib.request.urlretrieve(SAMPLE_URL, SAMPLE_FILE)
    cap = cv2.VideoCapture(SAMPLE_FILE)

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

print('\n=== 動画処理開始 ===')
print('操作方法:')
print('  q キー: プログラム終了')
try:
    while True:
        ret, frame = cap.read()
        if not ret:
            break

        processed_frame, result, current_time = video_frame_processing(frame)
        cv2.imshow(MAIN_FUNC_DESC, processed_frame)
        if choice == '1':
            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:
    print('\n=== プログラム終了 ===')
    cap.release()
    cv2.destroyAllWindows()
    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('\n処理結果をresult.txtに保存しました')