TECH BLOG

サムネ(Yolo4).png

2026.05.19

物体検知を使って滞在人数をカウントしてみる

YOLOを活用して、カメラ映像から滞在人数を自動カウントする仕組みを検証。物体検知と追従技術により、特殊センサー不要で空間の利用状況を可視化しています。実務でのAI活用を一歩進める取り組みです。

投稿者:かまわら

1. はじめに

「あの会議室、いつも予約で埋まってるけど本当に使ってるのかな?」
「お気に入りのカフェ、今行ったら座れるかな?」
こんなふうに、"空間が実際にどう使われているのか"が気になる場面は意外と多いものです。また、オフィスのレイアウト検討や商業施設でのイベントの集客分析など、より大きなスケールでも同じ課題があります。
これまでは感覚に頼るしかありませんでしたが、最近では既設カメラの映像から「人の動き」を自動でデータとして捉えることが可能になってきました。

今回は、物体検知モデル「YOLO」を活用し、映像から滞在人数をカウントする仕組みを検証してみました。
YOLO:https://docs.ultralytics.com/ja/

2. YOLOと物体追従(Tracking)の仕組み

滞在人数を把握するには、「人がいるかどうか」だけでなく、「その人がどこから来たのか」を捉える必要があります。

そのために、今回は以下の2つの技術を組み合わせて使用します。

① 物体検知(Object Detection)

YOLOは、映像の各フレームから人物の位置を高速に検出するモデルです。
ライブ映像でも遅延なく処理できるため、リアルタイムで人の存在を把握できます。

また、学習済みモデルをそのまま利用できるため、すぐに検証を始められる点も特徴です。
必要に応じて追加学習を行うことで、現場に合わせたカスタマイズも可能です。

文中(Yolo1).png

② 物体追従(Object Tracking)

カメラ1台で空間全体を把握できれば、物体検知だけでも滞在人数のカウントは可能です。ただし実際には、大規模空間やプライバシーの制約観点から、オフィスや商業施設全体をカバーするようにカメラを配置できるわけではありません。

そのため、オフィスや商業施設では、入口など限られたポイントにカメラを設置するケースが一般的です。この場合、「人が通過したこと」は分かっても、それが入室なのか退室なのかを区別できず、滞在人数を正確に把握することが難しくなります。

そこで必要になるのが、物体追従です。
検知した人物にIDを付与し、フレームをまたいで同一人物として追い続けることで、人物の移動方向を捉えられるようになります。

YOLOでは track モードを使うことで、ByteTrackなどのアルゴリズムによりこの追従が自動的に行われます。 これにより、単なる「人がいる」という情報だけでなく、「人の動き(入退場)」を把握することができ、その結果滞在人数の把握が可能になります。

  • model.track() の実行: 検知した枠に対して、ByteTrackなどの追従アルゴリズムを背後で走らせ、IDを保持し続けます。
  • persist=True 引数: これが「前のフレームの情報を引き継ぐ」という指示になり、画面から一時的に外れても同じIDとして再認識しやすくなります。

文中(Yolo2).png

3. 判定ラインによる人数カウントの検証

上記の「検知+追従」の技術を応用して、今回は特定のエリアの通過を判定するロジックを試してみました。単に「画面内に何人いるか」を数えるのではなく、判定エリアを設けることで滞在人数の算出を試みます。

映像内に仮想の 判定エリア(または扉ゾーン) を設定し、追従(Tracking)によって得られた各IDがそのラインをどの向きに通過したかを計算します。

  • 右側(外)から左側(内)へ通過: IN(入室)としてカウント
  • 左側(内)から右側(外)へ通過: OUT(退室)としてカウント

この「入った数(IN) - 出た数(OUT)」をリアルタイムに計算することで、常に「今、その空間に何人いるか」という滞在人数を可視化できるようにしました。

文中(Yolo3).png

4. 実際に使ってみた感想

今回の検証では、モデルの公開状況やライセンス、検証の安定性を考慮して、実績のある YOLOv9 を選択しました。

また、GPUなどの計算資源が必ずしも用意できない場合(低電力デバイスに実装など)を想定し、あえて一般的なCPU環境でどこまで実用に耐えうるかを重視して検証を行いました。

【検証結果】

実際に会議室や通路の映像で試してみたところ、以下のような手応えを得ることができました。

  • 精度: 人の重なりがない状況であれば、CPU環境でも十分に正確なカウントが可能です。ラインを跨ぐ判定(IN/OUT)もスムーズで、刻々と変化する滞在人数をリアルタイムに把握できる実用レベルに達していました。
  • 可視化のメリット: 検知したイベントをグラフ化することで、混雑のピークや人の出入りが激しい時間帯が一目で判別できます。単なる数字の羅列以上に、空間運用のヒントが得られるデータとしての有用性を実感しました。

output (4).gif

文中(Yolo5).png

【課題と改善の方向性】

今回のCPU環境+標準トラッカーという構成においては、 人同士が激しく重なる(遮蔽)シーンでは、IDの入れ替わりや一時的なロストが発生しやすく、結果として滞在人数のカウントにズレが生じることがあることも確認できました。

こうしたケースに対しては、現場の環境や予算に応じて、さらなる精度向上のアプローチも考えられます。

  1. ハードウェアによる強化(GPUの導入)
    推論速度を高めることで、より高精度なモデルを安定して動かせるようになり、混雑時でも人物をより正確に捉えられるようになります。

  2. より高度な追従アルゴリズム(StrongSORT)の活用
    StrongSORTのような高度なトラッカーを用いることで、追従の安定性を高めることができます。見た目の特徴による再識別や動きの予測、ノイズ抑制といった仕組みにより、一時的に見失った人物でも再び同一人物として捉えやすくなり、群衆の中でもIDを維持しやすくなります。

文中(Yolo6).png

構成サンプル(Python / Streamlit)

今回は検証用として、Streamlit 上で判定ラインを調整しながら人数カウントを確認できるシンプルなアプリのコードを記載します。人物検出には YOLOv9 の tracking 機能を利用しており、取得した Tracking ID をもとに、ライン通過時の移動方向から「入室 / 退出」をカウントしています。

import streamlit as st
import cv2
from ultralytics import YOLO

# タイトル
st.title("入退室カウントサンプル")

# YOLOv9モデル
model = YOLO("yolov9c.pt")

# 判定ライン位置
line_x = st.slider("判定ライン X座標", 0, 1200, 600)

# カウント
count_in = 0
count_out = 0

# IDごとの前回位置
track_state = {}

# 動画
cap = cv2.VideoCapture("sample.mp4")

while cap.isOpened():

    ret, frame = cap.read()
    if not ret:
        break

    # 人物追跡
    results = model.track(
        frame,
        persist=True,
        classes=[0],
        conf=0.2
    )

    # 検出結果
    if results[0].boxes.id is not None:

        ids = results[0].boxes.id.cpu().numpy().astype(int)
        boxes = results[0].boxes.xyxy.cpu().numpy()

        for tid, (x1, y1, x2, y2) in zip(ids, boxes):

            # 足元中心
            cx = (x1 + x2) / 2

            prev = track_state.get(tid)

            # 左→右
            if prev is not None:
                if prev < line_x and cx >= line_x:
                    count_in += 1

                elif prev > line_x and cx <= line_x:
                    count_out += 1

            track_state[tid] = cx

            # bbox描画
            cv2.rectangle(
                frame,
                (int(x1), int(y1)),
                (int(x2), int(y2)),
                (0, 255, 0),
                2
            )

    # 判定ライン
    cv2.line(
        frame,
        (line_x, 0),
        (line_x, frame.shape[0]),
        (255, 0, 255),
        3
    )

    # カウント表示
    st.write(
        f"IN: {count_in} / "
        f"OUT: {count_out} / "
        f"滞在人数: {count_in - count_out}"
    )

4. まとめ

YOLOを用いた一連の検証を通じ、特殊なセンサーを導入せずとも、既存のカメラ映像から人数のカウントが可能であることを確認できました。もちろん、群衆環境での精度維持など、実地運用における課題は残りますが、より高度な追従アルゴリズム(StrongSORT等)や最新モデルの活用によって、こうした課題の解消も進んでいます。

これらにより、空間の使われ方をデータとして捉え、より実践的な運用改善につなげることが可能になります。

今後は、こうした取り組みを社内でも活用しながら、実運用への展開を進めていきたいと考えています。