ポケモンスリープの睡眠データを簡単に読み取りたかった

皆さん寝てますか？私は毎晩すやすやしています。  
ポケモンスリープ(Pokémon Sleep)の睡眠データ、眺めるだけでもちょっと面白いですよね。
![Screenshot](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/e408c371-9da6-5f8d-0eaf-4edb2270a125.png)

アプリ内蔵の分析機能でも色々な情報が得られるんですが、どうせならアプリ外に睡眠データを持ち出したら何かできるかも…と思いませんか？  

世の中どこもかしこもAI、きっと簡単に読み取れると思うので試してみました。  
![Amazon Textract](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/a757096c-1467-f1a6-832d-275504789ca1.png)
なんかダメそう！[^1]  

[^1]: Amazon Textractは2023年8月時点で日本語非対応なので仕方ない。参考：[Amazon Textractのよくある質問](https://aws.amazon.com/jp/textract/faqs/)  

## 方針

さすがにスクリーンショットを文字認識APIに投げ込むだけでは無理そうなことが分かりました。仕方ないので以下の方針でチャレンジしてみます。  
1. 読み取りたいところを機械的に切り出す
1. 日付や数値は文字認識APIでデータを読み取る
1. グラフはピクセルを1つ1つ調べる

一気に泥臭くなった。  
色々なデータを読み取るのは大変そうなので、最低限、日付とグラフだけ取得することにしましょう。  

## 読み取りたいところを機械的に切り出す

Python+OpenCV(cv2)でやってみましょう。  
OpenCVでこの手の処理をするときは基本的に次のような流れでやるようです。
1. グレースケール化・二値化
1. 輪郭抽出
1. 切り出したい場所を示す輪郭を探す
1. 輪郭を含む矩形で切り出した画像を保存

出来上がったものは[こちら](https://github.com/Sakatoku/pkslp-reader)になります。  

https://github.com/Sakatoku/pkslp-reader

### グレースケール化・二値化

```cv2.cvtColor```および```cv2.threshold```を使います。  
ウルトラ上手に二値化できました！  

![debug_img001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/7624e818-72fd-47d7-769e-28b1fd70f179.png)

### 輪郭抽出

```cv2.findContours```を使います。  
```cv2.RETR_TREE```というオプションを使うと階層構造(どの輪郭の内側にどの輪郭があるか)まで取得できます。  
一番外側の輪郭や細々とした輪郭は不要なので、何階層目か・輪郭の面積でフィルタしてあげるとこのようになります。  

![debug_img002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/f10c3a74-2b92-92ad-2ae7-37367a969613.png)

### 切り出したい場所を示す輪郭を探す

画面レイアウトを見ながら「一番上にある輪郭が日付で一番面積が大きい輪郭がグラフかな…」みたいな泥臭いロジックを組みます。  
とりあえず正しく選択できてそうだけど、スクリーンショットの撮り方を変えたりスマホを機種変更したりするとどうなるかちょっと不安！  

![debug_img003.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/c7a6a573-fdac-b573-8a3e-89e7fa27ced9.png)

### 輪郭を含む矩形で切り出した画像を保存

```cv2.boundingRect```と```cv2.imwrite```を使います。  
cv2ではメモリ上の画像は多次元配列```numpy.ndarray```になっているので、配列の操作で画像を切り出せます。便利。  

![debug_img004.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/81bc478a-abc8-d92d-2e3c-699af1aef18e.png)

保存した画像は以下の通り。これは勝ったな！  

![debug_img005.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/4ae4b40d-50b1-4e70-56de-8ff14452196b.png)

![debug_img006.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/9fe74b09-b2f5-5a52-b6ed-1ee6b6c41080.png)

### One more thing

切り出しが簡単にできたので、後工程のためにグラフをもうちょっと細かく切り出しましょう。  
やることはだいたい同じです。グラフのあたりはコントラストが低いので二値化のパラメータを調整すると上手く輪郭抽出できそうです。  

![debug_img008.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/2f24b69e-8244-6145-dea2-5ff404c98dd1.png)

保存した画像は以下の通り。  

![debug_img009.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/43b12a95-5b96-25e0-dd82-738c168a16b3.png)

この画像の下には横軸ラベルしかないのでついでに切り出せそうですね。これも切り出して画像を保存しておけば就寝時間と起床時間を読み取れそうです。  

![debug_img010.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/b07f5177-2ae4-504c-b0c3-cc6b021b7ac1.png)

## 文字認識APIでデータを読み取れるか試してみる

保存した画像から日付や時間を読み取れるか手動で試してみましょう！ワクワク。  
今回は[Google Cloud Vision API](https://cloud.google.com/vision?hl=ja)のデモで試してみます。  

![demo](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/b933d197-30d9-725b-a98a-1ca028cb4aaa.png)

```raw
2 0 2 3 4 7 F 2 1 8  T H E
```

なんかダメそう！
【完】

## もうちょっと頑張るなら

数字の認識精度は高そうなので、さらに年・月・日の数字部分だけを切り出せばいい感じになりそうな気はします。  

ChatGPTにお願いすればいい感じに補正してくれないかなーというのも試してみたけど、自分のプロンプトエンジニアリング力では無理でした。  
「ローマ数字の"F"は10を表します[^2]」は頭いいなあ。  
![chatgpt](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/b90fcaf4-42b0-2a42-a700-80786b60535a.png)

[^2]: 表しません。  

【オワリ】