こんにちは! DataIntelligenceチームの福岡です。
この連載では、以下の要領でオフィスにいる人数の遠隔自動取得に挑戦します!
- オフィスの様子を設置したカメラで撮影&ダウンロード
- 画像から人だけを検出。人数をカウント
- 上記を自動で定期的に実行する
- 他サービスと連携して結果をリアルタイムに反映する
連載第一回目となる今回は、クラウドカメラサービス『ソラカメ』と物体検出モデルYOLOv8を使って、1と2を実装します。
取り組みの背景
弊社はハイブリッド勤務を採用しており、社員はリモートワークか出社を自由に選択できます。 そのため、午前中に在宅勤務やお客様のところに訪問して、午後から出社している社員もいます。
人が少ない方が集中できる!という方もいれば、大勢いるときに出社したい方もいます。ハイブリッド勤務になったことで、いつ誰が出社しているか把握するための出社管理システムが導入されました。 ですが、申請し忘れてしまうこともしばしば...
なので、自動で人数をカウントできるシステムがあったらな~と思ったのが、今回の実験のモチベーションです!
作業の流れ
作業は次のような流れです。
- ソラカメ(後述)を設置
- ソラカメから指定した時間の画像を取得
- 画像に写っている人を検出・カウント
順にみていきましょう。
1. ソラカメを設置
ソラカメはソラコム社が提供するクラウドカメラサービスです。インターネットと電源につないでポンと置くだけで24時間のクラウド録画ができます。
クラウド保存期間や料金は下記をご覧ください。
Soracom Cloud Camera Services ソラカメ - IoT プラットフォーム SORACOM
ソラカメは各種APIを提供しているため、他システムとの連携も可能です。 今回は、「指定した時刻の録画画像を取得する」処理に関連するAPIを利用します。
早速設置します。 オフィスの出社人数をカウントしたいので、全体が写るようにいい感じに設置します。
2. ソラカメから指定した時間の画像を取得
APIを使って、指定した時間の録画画像をダウンロードします。 ソラカメのAPIリファレンスを参考にします。
事前準備として、先に以下2点を実施します。
- 必要なライブラリをインストール
- API認証で用いるメールアドレスやパスワードの定義
#必要なパッケージのインストール import requests import json import time from pathlib import Path from datetime import datetime, timezone, timedelta #メールアドレス等の定義 email ="E_MAIL@ADDRES" password = "PASSWORD" X_Cybozu_API_Token= "MY_API_TOKUN" Content_Type = "application/json"
次に時刻の指定回りの準備を行います。 ソラカメ APIの時間指定はUNIXタイム(エポックミリ秒)で行います。 現在の時刻を取得して、UNIXタイムに変換するクラスを用意します。
#現在時刻を取得し、UNIXタイムに変換するクラス class TimeConverter: def __init__(self): # 現在の日時を取得 now = datetime.now() # 各部分を取得 self.year = now.year self.month = now.month self.day = now.day self.hour = now.hour self.minute = now.minute self.second = int(now.second) # 小数点以下を切り捨てるためにintにキャスト # 現在時刻のUNIXタイムを計算 self.get_image_time = self.japan_time_to_unix_time_milliseconds() def japan_time_to_unix_time_milliseconds(self): # 日本時間のタイムゾーンオフセット(UTC+9時間)を作成 jst_offset = timezone(timedelta(hours=9)) # 日本時間のdatetimeオブジェクトを作成 japan_time = datetime(self.year, self.month, self.day, self.hour, self.minute, self.second, tzinfo=jst_offset) # UNIXタイム(エポックミリ秒)に変換 unix_time_ms = int(japan_time.timestamp() * 1000) return unix_time_ms
ここまでで準備完了。お疲れさまでした。
ではソラカメのAPIを使っていきましょう! 指定した時間の録画画像の取得が目的なので、ダウンロード用URLを出力するクラスを用意します。
#指定した時刻の画像を取得するためのクラス class SoracomAPI: def __init__(self, email, password): self.base_url = "https://api.soracom.io/v1/" self.UserInfo_url = "auth/" self.email = email self.password = password self.headers = {'Content-Type': 'application/json'} self.authenticate() #API実行には認証が必要 def authenticate(self): data = { 'email': self.email, 'password': self.password } resp_UserInfo = requests.post(self.base_url + self.UserInfo_url, headers=self.headers, data=json.dumps(data)) self.api_key = resp_UserInfo.json()["apiKey"] self.token = resp_UserInfo.json()["token"] self.headers.update({ 'X-Soracom-API-Key': self.api_key, 'X-Soracom-Token': self.token }) #画像エクスポートに必要なデバイスIDを返すAPI def get_device_id(self): url = "sora_cam/devices" url = self.base_url + url resp = requests.get(url, headers=self.headers) resp = json.loads(resp.content) device_Id = resp[0]['deviceId'] return device_Id #エクスポートを開始するAPI def start_image_export(self,get_image_time): device_id = self.get_device_id() url = 'sora_cam/devices/{}/images/exports'.format(device_id) url = self.base_url + url data = { 'time': get_image_time } resp = requests.post(url, headers=self.headers, data=json.dumps(data)) return resp #ダウンロード用URLを取得するAPI def get_image_URL(self): url = 'sora_cam/devices/images/exports' url = self.base_url + url params = {'device_id': self.get_device_id() , "limit":1, #最新の一枚を取得 "sort":"desc" } resp = requests.get(url, headers=self.headers, params=params) return resp
試してみましょう。
#現在時刻をUNIXタイムに変換 test = TimeConverter() test.get_image_time #1690881969000 #2023-08-01 18:26:09 のUNIXタイム表記
この時間の録画画像をダウンロードしましょう。
soracom = SoracomAPI(email,password)
img = soracom.start_image_export(1690881969000)
これで、
print(img.json()[0]["url"])
とすれば画像のURLを取得でき、そこからダウンロードが可能です(注:一定時間が経過するとダウンロード不可になります)。
こんな感じで、ダウンロードできます。
※情報保護のため一部加工しています。
3. 取得した画像から、映っている人を検出・カウント
ここまでで画像の用意ができました。 この画像に何人写っているかを検出・カウントしましょう!
今回は個人的な興味から、Ultralytics社が提供する物体検出モデルYOLOの最新版 YOLOv8を使用します。 数ある物体検出モデルの中でも、少ないコードで高精度のモデルを簡単に実装できます。贅沢ですね。
早速試してみましょう。 まずは環境構築です。
!git clone https://github.com/ultralytics/ultralytics %cd ultralytics
!pip install -r requirements.txt
これだけで準備OK。簡単です。
YOLOv8が使えるようになったか確かめてみましょう。 デモ画像で実際に物体検出ができるか試してみます。
from ultralytics import YOLO #v8シリーズの中でも一番性能の良い8xを使用 model = YOLO("yolov8x.pt")
result = model("https://ultralytics.com/images/bus.jpg", save=True)
save=Trueにすることで、推論結果をultralytics/runs/detect/predictに保存します。
いい感じですね。
では、先ほどダウンロードした画像に適用してみましょう!!
ただし、今回はオフィス内の人数だけに興味があるので、人だけを検出できるように引数を調整します。 また、閾値も変えてみましょう。
Configuration - Ultralytics YOLOv8 Docs
上記ドキュメントを参照すると、
識別項目はclasses = [学習に用いられたデータセット内でのラベル]、 閾値はconf =で設定できるようです。
personのラベルが何番か確認します。 ひとまずprint(result) で推論結果の中身を見てみましょう。
[ultralytics.engine.results.Results object with attributes: boxes: ultralytics.engine.results.Boxes object keypoints: None masks: None names: {0: 'person', 1: 'bicycle', 2: 'car', 3: 'motorcycle', 4: 'airplane', 5: 'bus', 6: 'train', 7: 'truck', 8: 'boat', 9: 'traffic light', 10: 'fire hydrant', 11: 'stop sign', 12: 'parking meter', 13: 'bench', 14: 'bird', 15: 'cat', 16: 'dog', 17: 'horse', 18: 'sheep', 19: 'cow', 20: 'elephant', 21: 'bear', 22: 'zebra', 23: 'giraffe', 24: 'backpack', 25: 'umbrella', 26: 'handbag', 27: 'tie', 28: 'suitcase', 29: 'frisbee', 30: 'skis', 31: 'snowboard', 32: 'sports ball', 33: 'kite', 34: 'baseball bat', 35: 'baseball glove', 36: 'skateboard', 37: 'surfboard', 38: 'tennis racket', 39: 'bottle', 40: 'wine glass', 41: 'cup', 42: 'fork', 43: 'knife', 44: 'spoon', 45: 'bowl', 46: 'banana', 47: 'apple', 48: 'sandwich', 49: 'orange', 50: 'broccoli', 51: 'carrot', 52: 'hot dog', 53: 'pizza', 54: 'donut', 55: 'cake', 56: 'chair', 57: 'couch', 58: 'potted plant', 59: 'bed', 60: 'dining table', 61: 'toilet', 62: 'tv', 63: 'laptop', 64: 'mouse', 65: 'remote', 66: 'keyboard', 67: 'cell phone', 68: 'microwave', 69: 'oven', 70: 'toaster', 71: 'sink', 72: 'refrigerator', 73: 'book', 74: 'clock', 75: 'vase', 76: 'scissors', 77: 'teddy bear', 78: 'hair drier', 79: 'toothbrush'} orig_img: array([[[79, 89, 86], [79, 89, 86], [80, 90, 87], ..., [75, 89, 87], [75, 89, 87], [75, 89, 87]], [[79, 89, 86], [79, 89, 86], [80, 90, 87], ..., [75, 89, 87], [75, 89, 87], [75, 89, 87]], [[80, 90, 87], [80, 90, 87], [80, 90, 87], ...
nemes: をみると、どうやら'person' は0番になっているようです。
result[0].names[0] #'person'
引数に classes=[0] を追加します。
さらに閾値も変更します。 今回はconf= 0.3にしてみます。
#imageフォルダを作成して、推論したい画像を入れておく result = model("image/target1.png", save=True, classes=[0], conf=0.3)
推論の結果です。
すごい精度です! ほぼ正確に人だけ検出できています。
※画像加工は推論の後からしています。
最後に、検出した人数を取得しましょう。 resultの中身を参考にして、
result[0].boxes.data.shape[0] #7
とすればOKです。
いかがでしたか? 今回は、
・ソラカメを使って遠隔で画像を取得
・画像から人だけを検出、人数をカウント
を実施しました!
オフィスの人口密度を計算したり、ある施設の時間ごとの来場者数の推移を見たり、なんていう応用もできそうで楽しいですね。
今回はURLから手動で画像を取得し、モデルに渡しています。 画像の取得と推論は定期的に自動で行えたらより便利ですね。
また、業務にkintoneやLINEなどを使っている方もいらっしゃると思います(弊社はkintoneです)。 普段使っているツールと連携してリアルタイムで結果を表示する、といった使い方も考えられます!
続編ではこれらを踏まえ、さらに使い勝手を良くしてきます! 乞うご期待!