まばたき(瞬目)を動画から計測する — 回数・閉眼時間・間隔を研究データにする方法

まばたき検出
📌 準備: OpenCV の環境構築がまだの方はこちら → C++ 環境構築ガイド / C#(OpenCvSharp)セットアップ

まばたき(瞬目)を動画から計測する — 回数・閉眼時間・間隔を研究データにする方法

まばたき(瞬目)は、作業への集中や会話の有無、画面を見ているかどうかで回数が変わることが知られています。そのため心理学・認知科学・人間工学の実験で、従属変数や統制変数として記録したい場面があります。

計測の方法は、大きく分けて次の3つです。

方法 しくみ 向いている場面
眼電図(EOG) 目の周りに電極を貼り、眼球と瞼の動きに伴う電位を測る 時間分解能が要る実験室の研究
赤外線のアイトラッカー 専用機で瞳孔と瞼を追う 視線と同時に記録したい場合
一般的なカメラの映像 顔を撮影し、画像解析で目の開閉を判定する 被験者に装着物を付けたくない・人数を増やしたい・既存の録画を使いたい

本記事は3つ目、カメラ映像からの計測について、実験を設計するときに決めておくべきことをまとめます。


1. 何を指標にするか

映像から取り出せるのは「各フレームで目が開いているか閉じているか」です。そこから次の3つを計算します。

指標 定義 単位 注意
まばたきの回数(瞬目率) 閉→開の切り替わりの数を計測時間で割る 回 / 分 判定できなかったフレームの扱いを決めておく
閉眼時間 1回のまばたきで目が閉じていた時間 ms フレームレートが分解能の上限になる(§2)
まばたきの間隔 あるまばたきの開始から次の開始まで 秒 回数より分布(中央値・ばらつき)を見ると情報が多い

参考までに、健常者150人を対象にした Bentivoglio らの研究(Movement Disorders, 1997)では、平均の瞬目率は安静時 17回/分、会話中 26回/分、読書中 4.5回/分と報告されています。同じ人でも課題によって数倍変わるので、課題(何をしている間に測るか)を揃えることが、計測方法と同じくらい重要です。

左右の目を別々に判定できる場合は、「両目とも閉じたら1回」とするか「片目でも閉じたら1回」とするかも先に決めます。片目だけ閉じるウインクや、片側の判定失敗が混ざるため、本記事のスクリプトは両目とも閉じた区間を数えています。


2. フレームレート — 閉眼時間の分解能を決める

まばたきは数百ミリ秒以下の短い動作です。フレームレートが低いと、閉じている瞬間が1フレームしか写らないか、写らずに終わります。

フレームレート 1フレームの長さ 閉眼時間の分解能
9 fps 約111 ms 「1フレーム=111ms」刻み。短いまばたきは写らないことがある
15 fps 約67 ms 回数は取れるが、閉眼時間の比較には粗い
30 fps 約33 ms 回数・閉眼時間とも実用的。一般的な Web カメラで出せる
60 fps 以上 約17 ms 以下 閉眼時間の細かな差を見たい場合

当社が手元の9fpsの動画で検証したときは、まばたきの多くが1〜2フレームしか写っていませんでした(OpenCV でまばたきを検出する(EAR 方式)と、その限界)。閉眼時間を指標にするなら 30fps 以上を確保し、カメラの設定値ではなく実際に記録された fps を確認してください。照明が暗いと、カメラが露光時間を伸ばして fps を自動で落とすことがあります。


3. カメラ位置・照明・被験者の条件

項目 決め方
カメラの位置 顔の正面、目の高さ付近。画面を見る課題なら、モニターの上端か下端に固定
写る大きさ 顔の幅が画面幅の3〜5割程度。小さく写ると瞼の動きが解析できない
照明 顔の正面から均一に。背後に窓がある逆光は避ける。照明の明滅(フリッカー)にも注意
メガネ 着用のまま測るか外すかを条件として揃える。レンズへの映り込みで判定が乱れることがある
顔の向き 大きく横を向くと判定できないフレームが増える。課題中に顔を動かす必要があるかを確認
計測時間 課題の長さに合わせる。慣れによる変化を見るなら、前半と後半に分けて集計できる長さに

いずれも「結果に効く条件」なので、被験者間・条件間で揃え、論文の方法の節に書ける形で記録しておきます。


4. 録画を必ず残す

判定結果(CSV)だけでなく、元の映像も保存しておくことを強くすすめます。

  • 判定の妥当性を確かめられる: 一部の区間を目視で数え、自動判定と突き合わせられます。判定できなかったフレームが多い被験者の原因(顔の向き・照明・メガネ)も、映像を見れば分かります
  • 解析をやり直せる: 指標の定義(両目/片目、閉眼の最小フレーム数など)を後から変えても、映像があれば再計算できます
  • 査読への備え: 計測の手続きを問われたときに、条件と結果を示せます

ただし映像は顔の写った個人データです。研究倫理審査で、撮影・保存・廃棄の扱いを計画に含めておいてください。


5. CSV から回数・閉眼時間・間隔を出す

計測ソフトが「1フレーム1行」の CSV を書き出す場合の集計例です。ここでは当社の「まばたき検出アプリ(Windows版)」の出力形式(created_at に時刻、eyeStatusLeft / eyeStatusRight に 1: 閉 / 2: 開 / -1: 判定不能)を前提にしています(カメラ入力版・ファイル入力版とも同じ列構成です)。列名を合わせれば他のソフトの出力にも使えます。

# まばたき検出アプリの result.csv(1フレーム1行)から、回数・閉眼時間・間隔を集計する
# 使い方:
#   python summarize_blinks.py result.csv        ← カメラ入力で記録した CSV(created_at の時刻を使う)
#   python summarize_blinks.py result.csv 30     ← 録画ファイルを解析した CSV(動画の fps を渡す)
#   ※ 録画ファイルの解析では created_at が「解析した時刻」になるため、フレーム番号 ÷ fps で時間を出す
import csv
import sys
from datetime import datetime
from statistics import mean, median

CLOSED, OPEN = 1, 2  # eyeStatusLeft / eyeStatusRight の値(1: 閉 / 2: 開 / -1: 判定不能)


def parse_time(s):
    # 例: 2026-11-06-10-15-30.123456(先頭に ' が付く出力もあるので取り除く)
    s = s.lstrip("'")
    fmt = "%Y-%m-%d-%H-%M-%S.%f" if "." in s else "%Y-%m-%d-%H-%M-%S"
    return datetime.strptime(s, fmt)


def main():
    if len(sys.argv) < 2:
        print("usage: python summarize_blinks.py result.csv [video_fps]")
        return
    video_fps = float(sys.argv[2]) if len(sys.argv) > 2 else None
    with open(sys.argv[1], newline="", encoding="utf-8-sig") as f:
        rows = list(csv.DictReader(f))
    if not rows:
        print("データがありません")
        return

    frames = []  # (時刻[秒], 両目とも閉じているか, 判定できたか)
    t0 = None
    for i, r in enumerate(rows):
        left, right = int(r["eyeStatusLeft"]), int(r["eyeStatusRight"])
        valid = left in (CLOSED, OPEN) and right in (CLOSED, OPEN)
        if video_fps:
            t = i / video_fps
        else:
            ts = parse_time(r["created_at"])
            t0 = t0 or ts
            t = (ts - t0).total_seconds()
        frames.append((t, valid and left == CLOSED and right == CLOSED, valid))

    # 両目とも閉じている区間を1回のまばたきとして取り出す
    blinks, start = [], None
    for t, closed, valid in frames:
        if closed and start is None:
            start = t
        elif not closed and start is not None:
            blinks.append((start, t))
            start = None

    total_s = frames[-1][0] - frames[0][0]
    valid_ratio = sum(1 for _, _, v in frames if v) / len(frames)
    durations_ms = [(e - s) * 1000 for s, e in blinks]
    intervals_s = [blinks[i + 1][0] - blinks[i][0] for i in range(len(blinks) - 1)]

    print(f"計測時間        : {total_s:.1f} 秒({len(frames)} フレーム・判定できた割合 {valid_ratio:.1%})")
    print(f"まばたき回数    : {len(blinks)} 回(1分あたり {len(blinks) / total_s * 60:.1f} 回)" if total_s > 0 else "計測時間が0秒です")
    if durations_ms:
        print(f"閉眼時間        : 平均 {mean(durations_ms):.0f} ms / 中央値 {median(durations_ms):.0f} ms / 最長 {max(durations_ms):.0f} ms")
    if intervals_s:
        print(f"まばたきの間隔  : 平均 {mean(intervals_s):.2f} 秒 / 中央値 {median(intervals_s):.2f} 秒")


if __name__ == "__main__":
    main()
実行例(テスト用の CSV・30fps・20秒):
  計測時間        : 20.0 秒(600 フレーム・判定できた割合 99.8%)
  まばたき回数    : 4 回(1分あたり 12.0 回)
  閉眼時間        : 平均 83 ms / 中央値 83 ms / 最長 133 ms
  まばたきの間隔  : 平均 4.11 秒 / 中央値 4.33 秒

ポイントは3つです。

  • 録画ファイルを解析した CSV では、時刻の列が「解析した時刻」になります。 実時間ではないので、動画の fps を渡してフレーム番号から時間を出します
  • 判定できた割合を必ず一緒に出します。この値が低い被験者・区間は、回数が実際より少なく出ている可能性があります
  • 閉眼時間は「閉じたフレーム数 × 1フレームの長さ」なので、fps の刻みでしか出ません(30fps なら 33ms 刻み)
  • アプリの CSV には、アプリ自身が数えた回数の列(blinkCount)もあります。このスクリプトは閉眼時間・間隔を出すために「両目とも閉じた区間」で数え直しているため、アプリ画面の回数と1〜2回ずれることがあります(数え方の定義の違いです)

6. 計測前のチェックリスト

  • [ ] 課題(何をしている間に測るか)と計測時間を決めた
  • [ ] 指標の定義(両目/片目・判定不能フレームの扱い)を決めた
  • [ ] 実際に記録される fps を確認した(閉眼時間を見るなら 30fps 以上)
  • [ ] カメラの位置・写る大きさ・照明を固定し、記録した
  • [ ] メガネの扱いを条件として揃えた
  • [ ] 元の映像を保存する設定にし、倫理審査の計画に含めた
  • [ ] 試し撮りで「判定できた割合」と、目視との一致を確かめた

7. 開閉の判定方式で、数えられる回数が変わる

指標や撮影条件を決めても、最後に結果を左右するのは「目が開いているか閉じているか」の判定です。顔の特徴点から目の形を測る方式(EAR など)は、顔が小さく写ると特徴点が瞼を追えないことがあります。当社が試した実装では、顔の幅169pxの動画で閉じた目を「開いている」と判定し続け、4回のまばたきを1回も数えられませんでした。

当社のまばたき検出アプリ(Windows版)に使っているまばたき検出SDKは、目の画像をディープラーニングのモデルに入れ、左右の目の開閉を1フレームずつ判定する方式です。同じ動画をこのSDKで解析すると4回すべてを数え、3本の合計では22回中21回、誤検出は0でした。当社の動作確認用の動画3本での結果で、条件によって変わります。(比較の詳細)。

どの方式を使う場合も、本番と同じ撮影条件で録画した動画を何本か解析し、目視で数えた回数と突き合わせてから本計測に入ることをおすすめします。


まとめ

  • カメラ映像からの計測は、装着物なしで人数を増やせるのが利点。取り出せるのは「各フレームの開閉」で、そこから回数・閉眼時間・間隔を計算する
  • 瞬目率は課題で数倍変わる(安静 17 / 会話 26 / 読書 4.5 回/分という報告がある)。課題を揃えるのが先
  • 閉眼時間の分解能は fps で決まる。30fps 以上を、設定値ではなく実測で確認する
  • 判定結果だけでなく元の映像を残す。妥当性の確認・再解析・査読への備えになる
  • 集計では「判定できた割合」を一緒に出す。録画ファイルの解析では fps から時間を出す

🎓 まばたきを研究で計測したい方へ

スワローインキュベートのまばたき検出アプリ(Windows版)は、長時間計測・録画・1フレーム1行の CSV 出力に対応した研究・検証向けのアプリです。Web カメラで計測するカメラ入力版と、録画済みの動画を解析するファイル入力版の2本があり(別売り)、どちらも買い切りで 一般 33,000円/学術機関 16,500円(税込・1アプリ1ライセンスあたり)です。

→ まばたき検出アプリ(Windows版)の詳細・ご購入
→ 試用版アプリ(ブラウザ版)で精度を試す(無料)

タイトルとURLをコピーしました