夏じゃないけど）ゴーストを見つけるロボ

# うちの監視ロボ、人には見えない('_')を検知するらしいんです
### これが実際に検知した写真...
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189936/2ab65062-352a-753f-53b1-bbf2a783e7a0.png)
### あなたにも見えるでしょうか。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189936/502e307a-7388-76bf-1b9e-4f3085bf19a5.png)
きゃーーーーー　　((((>_<)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189936/842c0612-e6aa-cac2-97e2-d9921d2a7d26.png)
うちの部屋にもーーーー　　　((((>_<)

## さて。今回作ったクラウドの構成です。
この構成ならクレジットカードが不要で、ずっと無料で使える IBM Cloud ライトプラン。オブジェクトストレージに画像を保管して、Cloud Foundryで可視化します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189936/85d3f946-e465-62b4-6013-ae120561d223.png)

## その１．画像を保存するオブジェクトストレージを準備します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189936/831817b7-e78c-1a13-dda7-3a1630e38cea.png)

### (1) IBM Cloud の Object Storage 無料のライトプランを選びます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189936/532789ad-615f-a7fc-1475-42f7d8ee7641.png)

バケット（入れ物）のパラメータを指定して[作成]を実行（ロケーションは東京 jp-tok）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189936/3012f289-abb7-de3e-a279-3609be15123e.png)

### (2) ファイルをバケットにアップロードできるか試します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189936/dcec98cf-bfef-1266-688b-7e59a02bdfb8.png)

WEBブラウザでオブジェクトストレージのファイルにアクセスします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189936/f8698c3f-5a70-d680-f6af-73e8da6c83c0.png)


画像をダウンロードできました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189936/23783e9c-6020-d9b0-0802-296a72320d40.png)

### (3) ロボットから画像をアップロードするための「サービス資格情報」を作成します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189936/de3e4547-f937-15b5-9bbe-e73d129ba953.png)

作成した「サービス資格情報」は、画像アップロードに使うのでメモします
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189936/f15f110a-7395-8327-1871-155a9d5b6ee9.png)

### 画像ダウンロードできない？　
> それなら続きはこちら→ <https://qiita.com/1Kano/items/4144be24c5168f9356f7>


## その２．「ゴーストセンサー・ロボ」から、オブジェクトストレージへ画像をアップロードします

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189936/0a4b1a37-1c9d-8844-a16d-2062a9bd9b23.png)

### めっちゃハマったところ(T_T)
アップロードの方法は幾つか用意されています。

1. ロボットに入っているラズパイは[**swiftコマンド**]で動かしていた。
2. オブジェクトストレージが[s3互換]になったので[**AWS-CLIのs3コマンド**]で動作確認した。
3. 途中、pythonスクリプトには[**AWS SDK for Python**]が良さげなので[boto3]を使った。
4. AWSの[boto3]ではなく、IBM Cloudの[**IBM COS SDK for Python**]があるじゃない。早速[ibm-cos-sdk]をダウンロード。あれー？[ibm_boto3]ファイルアップロード動かないよー、説明書足りないー。実行するとエラー。
6. しゃーない。[**cURL**]でやろっと。動いた、pythonで組むなら[**import requests**]か。で、動いた。

### 動作確認ずみのpythonスクリプト

1. ラズパイが5秒おきにカメラ撮影する
2. 画像ファイルは、[openCV]で顔を検知する
3. もしも画像ファイルに顔が見つからなければ(1に戻って5秒待つ)
3. IBM Cloud の[認証トークン]をもらって、オブジェクトストレージへのアクセスを許可してもらう
4. [認証トークン]を使って、オブジェクトストレージに画像をアップロードする(1に戻る)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189936/707f0126-0962-5975-a426-0802e27a71ca.png)


```
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import cv2
import time
import requests
import json
import os

def run_capture_camera():
    # Haar-like特徴分類器ファイル（顔検出版）の読み込み。
    face_cascade = cv2.CascadeClassifier("/usr/share/opencv/haarcascades/haarcascade_frontalface_default.xml")

    # USBカメラで画像を取得する。
    time.sleep( 0.2 )
    capture = cv2.VideoCapture(0)
    rtn, frame = capture.read()

    # カメラ画像を保存する
    if( rtn == True ):
        cv2.imwrite( "capture_input.jpg", frame )

        # 画像ファイルをグレースケールに変換する。
        gray = cv2.cvtColor( frame, cv2.COLOR_BGR2GRAY )
        # グレースケール画像から顔を検知する。
        faces = face_cascade.detectMultiScale( gray )
        for rect in faces:
            # 検知した顔を四角で囲む（rect[*]:0=x, 1=y, 2=width, 3=height）。
            color = (0,255,0)    # 緑色(BGR)。
            thickness = 2        # 枠線の太さ
            cv2.rectangle( frame, tuple(rect[0:2]), tuple(rect[0:2]+rect[2:4]), color, thickness )
        # 顔検知した画像ファイルを保存する。
        cv2.imwrite( "capture_output.jpg", frame )

    # 終了処理（ストリームを解放）
    capture.release()
    cv2.destroyAllWindows()

    # 顔を検知したか否かを返す
    if( len(faces) > 0):
        return True
    else:
        return False

def run_upload_file():
    # トークンを取得するための IBM Cloud 認証用エンドポイント
    auth_endpoint = "https://iam.cloud.ibm.com/identity/token"
    # ファイルをアップロードするときの IBM Cloud サービス用エンドポイント
    # （ラズパイからインターネット経由でアクセスするのでパブリック側エンドポイントを指定）
    service_endpoint = "https://s3.jp-tok.cloud-object-storage.appdomain.cloud"
    # IBM Cloud Object Storage のサービス資格情報で取得するapikeyは、OSの環境変数に登録しておき、コードには書かない。
    # $ export IBM_APIKEY=ddZgpr*********************************jGkB3
    ibm_apikey = os.environ['IBM_APIKEY']

    # IBM Cloud Object Storage 認証トークンを取得する
    headers = {
        'Accept': 'application/json',
        'Content-Type': 'application/x-www-form-urlencoded',
    }
    data = {
        'apikey': ibm_apikey,
        'response_type': 'cloud_iam',
        'grant_type': 'urn:ibm:params:oauth:grant-type:apikey'
    }
    response = requests.post(auth_endpoint, headers=headers, data=data)
    output = response.json()
    ibm_access_token = output['access_token']
    # トークン取得をデバッグするときに検査したいとき
    print("TOKEN STATUS:" + str(response))
    #print("TEXT  :" + json.dumps(output, indent=4))
    #print("TOKEN :" + str(ibm_access_token))

    # IBM Cloud Object Storage に、カメラ画像ファイルをアップロードする。
    # この例では[/robocamera]がバケット名、ghostpicture.jpgがファイル名(オブジェクトのキー名)です。
    headers = {
        'Authorization': 'bearer ' + ibm_access_token,
        'Content-Type': 'image/jpeg',
    }
    f = open('capture_output.jpg', 'rb')
    img_data = f.read()
    f.close()
    response = requests.put(service_endpoint + '/robocamera/ghostpicture.jpg', headers=headers, data=img_data)
    # トークン取得をデバッグするときに検査したいとき
    print("UPLOAD STATUS:" + str(response))

if __name__=="__main__":

    try:
        while True: # 永久に繰り返し
            rtn = run_capture_camera()
            if( rtn == True ): 
                print("顔を検知したのでクラウドに保存しました")
                run_upload_file()
            else:
                print("No face, no upload")
            time.sleep(5)
    except KeyboardInterrupt: # CTRL+Cで終了
        print "Exit"

    sys.exit()

```

### cURLとpythonでファイルアップロードするなら
> 　それなら続きはこちら→ <https://qiita.com/1Kano/items/c3c7e0400dd2384f8058>

### IBM Cloud Object Storage SDK
> 参考 <https://ibm.github.io/ibm-cos-sdk-python/>




## その３．IBM Cloud Cloud Foundry に画像可視化アプリを準備します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189936/57553f24-0fbf-9bc4-51b0-b89696e71c4d.png)


### (1) IBM Cloud Foundry を準備します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189936/ab059f28-f473-3984-a914-f509f8776f72.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189936/07199b9b-28a0-2100-2e8a-29294dba26e8.png)
アプリのURLをメモして、WEBブラウザで[Hello world!]と空のアプリが起動するのを確認する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189936/223dd821-8e30-ca36-ff01-e07a5b460de7.png)

### (2) 自分PCのアプリ開発環境を準備します（Windowsの場合）
### (2)-1. [**IBM Cloud CLI**] を自分PCにダウンロード。

> 参考 <https://cloud.ibm.com/docs/cli?topic=cloud-cli-install-ibmcloud-cli&locale=ja>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189936/b2f00502-1909-ea17-617d-958344e6b917.png)

### (2)-2. Windows PC の IBM Cloud コマンドで IBM Cloud API にログインする

```
E:\>ibmcloud login
API エンドポイント: https://cloud.ibm.com
地域: us-south

Email> （自分のIBM ID）

Password>　（自分のIBM IDのパスワード）
認証中です...
OK

ターゲットのアカウント 個人利用（鹿野） (9d7f******************830)

API エンドポイント:      https://cloud.ibm.com
地域:                    us-south
ユーザー:                （自分のIBM IDが表示されます）
アカウント:              個人利用（鹿野） (9d7f********************830)
リソース・グループ:      リソース・グループがターゲットになっていません。'ibmcloud target -g RESOURCE_GROUP' を使用してください
CF API エンドポイント:
組織:
スペース:
```

### (2)-3. Windows PC の IBM Cloud コマンドで Cloud Foundry の組織やリソースを登録する

```
E:\>ibmcloud target --cf
ターゲットの Cloud Foundry (https://api.ng.bluemix.net)

ターゲットの組織 ＜自分の組織名＞

ターゲットのスペース ＜自分のスペース名＞

API エンドポイント:      https://cloud.ibm.com
地域:                    us-south
ユーザー:                ＜自分のユーザー名（IBM id）＞
アカウント:              個人利用（自分） (9d7f****************e830)
リソース・グループ:      リソース・グループがターゲットになっていません。'ibmcloud target -g RESOURCE_GROUP' を使用してください
CF API エンドポイント:   https://api.ng.bluemix.net (API バージョン: 2.142.0)
組織: 　　　　　　　　　 ＜自分の組織名＞ 
スペース:                ＜自分のスペース名＞

```

### (2)-4. 空っぽの Cloud Foundry アプリを作ってみる
ファイルを２つ作ります。

E:\cfproject
　　├　[manifest.yml] デプロイするアプリのPathや、
　　│　　　　　　　　　デプロイ先のプロジェクト/リソースを指定します
　　└\test
　　　　└　[index.html] テスト用HTML

以下、サンプル
[manifest.yml]

```
---
applications:
- name: projectx2
  path: E:\cfproject\test
  random-route: true
  instances: 1
```

[index.html]

```
<!doctype html>
<html lang="jp">
  <head>
    <meta charset="utf-8">
    <title>CF デプロイ テスト</title>
  </head>
  <body>
    <h1>この画面が表示されたら成功</h1>
  </body>
</html>
```

### (2)-5. Cloud Foundry アプリをパブリックURLにデプロイしてみる
[manifest.yml]があるディレクトリでIBM CLIを実行する

```
E:\cfproject>ibmcloud  cf push

'cf push' を起動しています...

　　＜中略＞

Waiting for app to start...

name:              projectx2
requested state:   started
routes:            projectx2.mybluemix.net
last uploaded:     Sat 30 Nov 14:35:11 JST 2019
stack:             cflinuxfs3
buildpacks:        staticfile

type:            web
instances:       1/1
memory usage:    64M
start command:   $HOME/boot.sh
     state     since                  cpu    memory         disk          details
#0   running   2019-11-30T05:35:30Z   0.2%   13.8M of 64M   22.6M of 1G

E:\cfproject> _
```

WEBブラウザで Cloud Foundry アプリにアクセスしてみる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189936/084b9428-4430-c577-e4d5-3b38a5c617f9.png)

### Cloud Foundry アプリが動かない？　
続きはこちら→「IBM Cloud Foundry トラブルシューティング」（予定）


## (3) 画像を可視化するCloud Foundry アプリを作ります
自分の好きな言語を選びます。私の場合は Angularフレームワークも勉強するので言語は Type Script です。ランタイムは node.js です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189936/e2f5baea-03e6-4383-d307-c6b4e7bbb142.png)

### (3)-1. 自分PCの環境にAngular CLIを準備します
- Windows 10
- Visual Studio Code (Windows版) 1.39.2
- C:\Users\Kano>ng --version

> 参考　<https://angular.jp/cli>  ※Angular CLIのインストールを参照方

```
     _                      _                 ____ _     ___
    / \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
   / △ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
  / ___ \| | | | (_| | |_| | | (_| | |      | |___| |___ | |
 /_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
                |___/

Angular CLI: 8.1.1
Node: 10.16.0
OS: win32 x64
Angular:
...

Package                      Version
------------------------------------------------------
@angular-devkit/architect    0.801.1
@angular-devkit/core         8.1.1
@angular-devkit/schematics   8.1.1
@schematics/angular          8.1.1
@schematics/update           0.801.1
rxjs                         6.4.0
```

### (3)-2. Angularプロジェクトを新規作成します
Windowsコマンドプロンプトを開いて、プロジェクトを作成するフォルダに移動して[ng new プロジェクト名]を実行する。

```
E:\Source\Angular> ng new projectx
? Would you like to add Angular routing? Yes
? Which stylesheet format would you like to use? CSS
CREATE projectx/angular.json (3441 bytes)
CREATE projectx/package.json (1281 bytes)
CREATE projectx/README.md (1025 bytes)

　以下、projectxディレクトリにひな形のソースファイルがつくられる、たくさん。

CREATE projectx/e2e/tsconfig.json (214 bytes)
CREATE projectx/e2e/src/app.e2e-spec.ts (637 bytes)
CREATE projectx/e2e/src/app.po.ts (251 bytes)

added 1082 packages from 1045 contributors and audited 17199 packages in 127.449s

E:\Source\Angular> cd projectx
E:\Source\Angular\projectx> _
```
作成された新規プロジェクトのフォルダ・ファイル
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189936/5851f18f-408d-0abe-60b7-ee78cf869558.png)
[manifest.yml]は自分で作成します。私の場合（↓）こうなりました

```
---
applications:
- name: projectx2
  path: E:\Source\Angular\projectx\dist\projectx
  random-route: true
  disk_quota: 512M
  memory: 64M
  instances: 1
  buildpacks:
    - https://github.com/cloudfoundry/staticfile-buildpack.git
```

### (3)-3. Angularでアプリを作成
大幅省略しますが、下記はCloud Foundry アプリがオブジェクトストレージの画像を参照する最小限のコンポーネント、ファイル名は[app.component.html]です。[index.html]の app-root から呼ばれるコンポーネントです。

```
<!--The content below is only a placeholder and can be replaced.-->
<div style="text-align:center">

<div class="box1">
    <h1>ゴーストセンサー</h1>
</div>
<img width=95% src="https://robocamera.s3.jp-tok.cloud-object-storage.appdomain.cloud/ghostpicture.jpg">

</div>
<h2>KANO 2019/11/30</h2>

<router-outlet></router-outlet>
```

### (3)-4. Angularでアプリをビルドします
アプリをビルドすると \dist\フォルダに新しいビルド済みフォルダが再作成されます

```

E:\Source\Angular\projectx>ng build

chunk {main} main-es2015.js, main-es2015.js.map (main) 9.79 kB [initial] [rendered]
chunk {polyfills} polyfills-es2015.js, polyfills-es2015.js.map (polyfills) 251 kB [initial] [rendered]

　　＜中略＞

chunk {styles} styles-es5.js, styles-es5.js.map (styles) 16.8 kB [initial] [rendered]
chunk {vendor} vendor-es5.js, vendor-es5.js.map (vendor) 3.81 MB [initial] [rendered]
Date: 2019-11-30T06:31:27.033Z - Hash: c76ae6e4ddd7162549d0 - Time: 6327ms

E:\Source\Angular\projectx> _
```

### (3)-5. Angular で作ったアプリを Cloud Foundry にデプロイします
デプロイは、再び、Windowsコマンドプロンプトで [ibmcloud cf push] です。

```
E:\Source\Angular\projectx>ibmcloud cf push

'cf push' を起動しています...

Pushing from manifest to org <IBM ID> / space KANO as <IBM ID>...
Using manifest file E:\Source\Angular\projectx\manifest.yml

　　＜中略＞

type:            web
instances:       1/1
memory usage:    64M
start command:   $HOME/boot.sh
     state     since                  cpu    memory         disk            details
#0   running   2019-11-30T06:37:58Z   0.2%   10.5M of 64M   22.6M of 512M

E:\Source\Angular\projectx> _
```

## その４．遊ぶ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189936/062673cd-2aa5-da82-87ed-d905dbe44cba.png)


### (1) WEBブラウザでアプリにアクセス。
ほらほら、うちのロボが心霊(-.-)を認識したって...
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189936/ac845789-0559-61d2-ff7f-7eb14db0dc4d.png)

### (2) ちゃんと顔認識しますよ
20才の時の自分写真
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189936/74d5847a-1038-32d5-2b3d-0b4f9ef493bb.png)

### (3) 自宅警備ロボ化してみようかな
自宅内のリアルタイム写真をインターネットに公開しちゃダメ。Basic認証でWEBを保護しよう。オブジェクトストレージ側も公開制限をしなきゃね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189936/17e54420-2771-8760-8e9a-1ca75f8c2fc5.png)

## 工夫したこと

オブジェクトストレージの無料条件は25GBまで保存できるなど凄いのだが「最大 2,000 PUT要求/月」とある。自宅警備カメラが1分毎に画像をアップロードすると 44,640回（= 60分 x 24時間 x 31日）。最大PUT回数足りない！毎分アップロードできないー!!となったので、ラズパイが1分間隔で撮影した画像は、侵入者を検知した画像のみ、オブジェクトストレージに保存することとした（検索の効率も良いし）。

## まとめ

IBM Cloud ライトプランの無料で出来る範囲の構成を作ってみました。クラウドサービスがどんどん新旧更新されているので、最新のドキュメントを見つけることに想定より時間がかかってしまいましたが、動いたときの達成感は良かった（燃え尽き感もあるが）。

本記事に含まれるもの
1. IBM Cloud Object Storage
2. IBM Cloud Foundry
3. Raspberry Pi 3
4. Python
5. Angular TypeScript