手検出やジェスチャー認識で PC を操作する方法

手検出やジェスチャー認識で PC を操作することは多くの方が想像するところではないでしょうか？この場合、HID (Human Interface Device)として BLE Peripheral の実装を行いたくなります。しかしながら、私自身のように普段は別の分野の業務に従事する方や、こうした話が持ち込まれる機械学習エンジニアにとって突然 HID Over GATT を実装を行うのは骨が折れることだと思います。（マイコン向けの micro python などで記述された実装は簡単にヒットするが、デスクトップ環境の実装の記事が少なく、dbus-api として提供されるようになった bluez の背景を追うことや SerialPortProfile (SPP) の存在が私を混乱させた。）

私自身は当時 tech demo のために設けられた一週間のうちに実現できず、その後１年ほど少しずつ情報を集めて BLE マウスのエミュレートの実現に至っています。この話は別案件においても応用が効くように思うので、私の実装の公開を行い、この記事では実装を修正するための要所を記載しようと思います。（去年１年は余裕がなくまとめられなかった）

https://github.com/naru-hiyoko/ble_vision_ai.git

## 利用されるソフトウェア

おそらく当時下記の内容で動作させていたと思います。<br>
手検出（ローカライゼーション）には mediapipe の [hand pose estimator](https://google.github.io/mediapipe/solutions/hands.html) を利用しています。手姿勢の推定の前段に detection (localization) を行う箇所があるので、この部分を利用しています。

* ubuntu (20.04)
* bluez 5.4
* python 3.8 系
* mediapipe 確認中
* opencv 確認中

## 実装修正の勘所

ここでは今後アプリケーションに応じた実装を記述する方法を記載します。
実際は GATT における `Service`, `Characteristics` の記述と `Application` 部分の簡単な編集で済む話になっています。

### Application 部分

リポジトリには３つのアプリケーションの実装が入っています。（ようだ）
まずは example_gatt_server のデモを起動できると理解が進むと思います。
実際に GATT アプリケーションを利用（デバッグ）する手段としてお勧めなのは
GattBrowser([android](https://play.google.com/store/apps/details?id=com.renesas.ble.gattbrowser&hl=en&gl=US)/[ios](https://apps.apple.com/jp/app/gattbrowser/id1163057977))になります。ただし、HID サービス (hand_gesture_mouse_server, multitap_gatt_server) は macos/ios のセキュリティの理由で表示されず利用できません。


* [example_gatt_server.py](https://github.com/naru-hiyoko/ble_vision_ai/blob/main/ble_app/example_gatt_server.py)
  * GATT のサンプル実装デモ
  * ４つのサービスが動作する (`BatteryService`, `DeviceInfoService`, `HeartRateService`, `TestService`)
* [hand_gesture_mouse_server.py](https://github.com/naru-hiyoko/ble_vision_ai/blob/main/ble_app/hand_gesture_mouse_server.py)
  * カメラから手の位置を検出してマウスポインタが追従するデモ
  * windows 推奨（macos, ios では動かない）
* [multitap_gatt_server.py](https://github.com/naru-hiyoko/ble_vision_ai/blob/main/ble_app/multitap_gatt_server.py)
  * digitizer (タッチパネル)のデモ
  * windows 推奨で５秒に一回タップ操作が走る

上記のファイル内で編集が必要な点は１つで以下の `__init__` 部分になります。
`example_gatt_server` では `HeartRateService`, `BatteryService`, `DeviceInfoSerivce`, `TestService` の４つのサービスが登録されていることが分かりますが、このうち `HeartRateService`, `TestService` にあたる部分を自作のアプリケーション用に用意する流れになります。（`BatteryService`, `DeviceInfoSerivice` もダミー実装になっているが、動作する点だけ注意が必要になっています）

```python
class Application(dbus.service.Object):
    """
    org.bluez.GattApplication1 interface implementation
    """
    def __init__(self, bus):
        self.path = '/'
        self.services = []
        dbus.service.Object.__init__(self, bus, self.path)
        self.add_service(HeartRateService(bus, 0))
        self.add_service(BatteryService(bus, 1))
        self.add_service(DeviceInfoService(bus, 2))
        self.add_service(TestService(bus, 3))
```

[L94](https://github.com/naru-hiyoko/ble_vision_ai/blob/main/ble_app/example_gatt_server.py#L94) 付近でアドバタイズを行う Agent を登録しているので、GATT Browser や windows のデバイスの登録での検索にデバイスが `AIBot` として表示されて接続ができる状態になります。

## `Service`/`Characteristics` 部分

`Service`/`Characteristics` を記述するためには既存の [hand_gesture_mouse_service.py](https://github.com/naru-hiyoko/ble_vision_ai/blob/main/ble_app/services/hand_gesture_mouse_service.py) のファイルをコピーして編集していくと早いように思います。

注目が必要な箇所はいくつかあるのですが、[Report Description](https://github.com/naru-hiyoko/ble_vision_ai/blob/main/ble_app/services/hand_gesture_mouse_service.py#L69)は absolute mouse と同じものを使っています。こうしたバイト列は何らかの手段で入手する必要があるのですが、[参考](https://github.com/naru-hiyoko/ble_vision_ai#references)内のリンク先から引っ張ってくる以外に現実的な方法が見えてきません。また、[self.PropertiesChanged](https://github.com/naru-hiyoko/ble_vision_ai/blob/main/ble_app/services/hand_gesture_mouse_service.py#L187)でマウスのボタン押下状態、x、y を送信するのですが、どうやら x, y のそれぞれの範囲は 0 ~ 127 の整数値で指定していることに気づくかと思います。つまり、` [dbus.Byte(1), dbus.Byte(0x00), dbus.Byte(64), dbus.Byte(0x00), dbus.Byte(64)]` とした場合は左ボタンを押下した状態で画面中央付近にポイントが移動します。そして、直後に` [dbus.Byte(0), dbus.Byte(0x00), dbus.Byte(64), dbus.Byte(0x00), dbus.Byte(64)]` とすることでクリックの挙動が再現されています。

## まとめ

一番手間取ったのは[relative_mouse_service](https://github.com/naru-hiyoko/ble_vision_ai/blob/main/ble_app/services/relative_mouse_service)、[absolute_mouse_service](https://github.com/naru-hiyoko/ble_vision_ai/blob/main/ble_app/services/absolute_mouse_service.py) それぞれの `notify_report`内の `value` の範囲の理解でした。absolute_mouse_service は実際には x, y 位置が 0 ~ 127 の範囲になっていて、2 byte で表現する理解でいたので気づくのに時間がかかりました。この話を塩漬けにしたのは、いつか調査、整理してからと思ったからなのですが、プライベートの時間も限られているので、現状の材料で一度まとめました。 HID はダメですが、BLE Peripheral はブラウザ(WASM) から今後使われていくと思うので期待しています。