ソラコム　S+cameraを使用して簡単な画像処理をローカル環境でやってみた

ソラコムのS+cameraを利用して簡単な画像処理のテストをしてみました。
動かすまでに多少引っかかるところがあったので、自分用のまとめもかねてシェアします。

やってみたこと
・ローカルPCに開発環境の構築
・S+cameraで撮影した画像をダウンロードしてローカルPCで画像処理のアルゴリズムを試してみる
ここから下は今回は未実装
・S+cameraで使用できるようにソースコードを訂正する
・S+cameraにソースコードをデプロイする
・実際に画像処理アルゴリズムを動かしてみる

使用した機材と環境
・windows10
・[S+camera](https://soracom.jp/soracom_plus/camera_basic/)
・anaconda3

方法
最低限の動作確認は終わっている前提で進めていきます。最低限の動作確認が終わっていない場合はこちらの記事を参考にして動作確認を行ってください。
[SORACOMのs+camera試してみました。](https://qiita.com/shinfrom/items/4a3bbe132196aea7d80b)
[SORACOM レシピ：IoTカメラで計測機器のデータを可視化したい](https://soracom.jp/recipes_index/3967/)

まずは開発環境から用意していきます。
soracom推奨の開発環境を用意してもよいのですが、今回はanacondaを使用しました。
なぜかというと、推奨環境を用意することがそこまで簡単ではないこと、実験する際には実験のために必要なモジュールがあれば十分だからです。
anacondaを使用したのは、画像処理の途中でデバッグを行う際にディスプレイに表示して確認したいと考えたからです。例えばwindowsでwslを使用してPythonの環境を構築することも可能なのですが、ディスプレイで表示して確認することが大変だからです。

ローカルPCにanacondaをインストールします。
今回はopencvを使用したいので、opencvのモジュールをインストールします。
anacondaを利用してビルド済パッケージを使用すると、素早く行えます。

anacondaを開いて、左側のenvironmentsをクリックして開発環境のセットに入ります。
その画面で下側にあるCREATEを押して開発環境を作成していきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244155/6377e3cc-0ec6-de69-1995-7965a1d29868.png)
名前を付けて、Pythonを選択することで仮想環境が作成されます。私は最新のPythonを選択しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244155/e358d22d-0e0c-a298-0900-8094995eb010.png)
作成した仮想環境を選択して、Open Terminalを選択することでコンソールを開きます。
今回はopencvを使用したいので、コンソールからインストールしていきます。
画像を扱うためのモジュールimageもインストールしておきます。

```console
pip install opencv-python
pip install image
```

インストールが終了したら、anacondaからjupyterlabを開きます。
jupyterlabを開いたら、自分が把握できる任意の場所にフォルダを作成します。
フォルダの中には、テスト用のソースコードを作成します。テスト用の画像も置いておくと便利です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244155/20803223-19cb-5633-5df2-3b593cc6e67e.png)
ここではフォルダの名前をadditionaltest、テスト用のソースコードをsoracom_imagetest.ipynb、テスト用の画像はplants.jpgにしました。

処理するための画像をS+cameraから取得するための準備を行います。
ここからはsoracom mosic上での操作になります。

soracom mosaicにログインして
カメラで撮影した写真を受信する準備をします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244155/08537d21-82a3-54ba-244b-fad1ff501265.png)
Enablee Remote Accessを押して、ポートを番号を指定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244155/d8d7c329-d6bd-2f08-51b1-6f9b7523ab5b.png)
ポート番号を8080に指定します。アクセス可能時間を指定してconfirmを押すと、アクセス用のurlが発行されます。

発行されたurlに対してGETで画像を要求します。

```python
requests.get("http://取得したurl/v1/stopCameraCapture")      # httpのGETメソッド
```
pythonでこのapiをたたくと、
カメラで撮影した写真がバイナリでダウンロードできます。

早速テスト用のコードを書きます。

```python
import requests
import io
import cv2
import time
from PIL import Image
import numpy as np



# カメラの画像を取得する準備
# requests.get("http://localhost:8080/v1/stopCameraCapture")      # httpのGETメソッド
requests.get("http://取得したurl/v1/stopCameraCapture")      # httpのGETメソッド
print("START: waiting for cameraState(10s)", flush=True)
time.sleep(10)
# requests.put("http://localhost:8080/v1/cameraState", json={"width": 1280, "height": 720})
requests.put("http://3取得したurl/v1/cameraState", json={"width": 1280, "height": 720})
# r = requests.get("http://localhost:8080/v1/startCameraCapture")
r = requests.get("http://取得したurl/v1/startCameraCapture")

# カメラの画像を取得できないとき
if r.status_code != 200:
    print(f"Failed /v1/startCameraCapture (HTTP {r.status_code}), exited.", flush=True)
    exit
print("START: waiting for camera(5s)", flush=True)
time.sleep(5)
# 初期化の終了
print("Initialized. Go the loop.", flush=True)

#PILのデータをopencvで扱えるようにする関数
# https://qiita.com/derodero24/items/f22c22b22451609908ee
def pil2cv(image):
    ''' PIL型 -> OpenCV型 '''
    new_image = np.array(image, dtype=np.uint8)
    if new_image.ndim == 2:  # モノクロ
        pass
    elif new_image.shape[2] == 3:  # カラー
        new_image = cv2.cvtColor(new_image, cv2.COLOR_RGB2BGR)
        print("cv2 change", flush=True)

    return new_image

# 処理を２回繰り返す
num = 1
while num < 3:
    #画像の読み込み
    r = requests.get("http://35.76.163.176:34060/v1/cameraJpegImage")    #画像を取得

    if r.status_code != 200:        #200以外のステータスの時は画像取得失敗
        print(f"Failed /v1/cameraJpegImage (HTTP {r.status_code}), next.", flush=True)
        break

    #バイナリで画像が下りてくる
    image = Image.open(io.BytesIO(r.content))
    #画像をopencvで扱えるように変換する
    img = pil2cv(image)



    #取得した画像をグレースケールに変換する
    #第一引数：入力画像、第二引数変換形式
    gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    #cannyアルゴリズムでエッジを検出する
    #第一引数：入力画像、第二引数：最小閾値、第三引数：最大閾値
    edge_img = cv2.Canny(gray_img, 100, 200)


    # イメージの表示　何かボタンを押すと終了します
    cv2.imshow('image', img)
    cv2.imshow('gray', gray_img)
    cv2.imshow('edge', edge_img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
    num = num +1
    time.sleep(10)
```
このテストコードを実行した結果です
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244155/a507df9d-39e3-69ca-da1c-9f19290540bd.png)
このようにフルカラー画像、グレイスケール、エッジ画像が出れば成功です。
何かキーを押すと作成した画像を閉じます。
この処理を２回繰り返すようなプログラムにしています。

これでS+cameraから取得した画像をローカルで処理することができました。
このプログラムは設定を変更を変更するだけで、S+cameraに遠隔コンパイルでき、エッジで画像処理を行うことができます。

遠隔コンパイルの方法についてはまた次回説明していきます。

感想（言い訳）
推奨の開発環境を用意するのは大変かもしれないので、モジュールのインストールで引っかかるときは、とりあえず動くものから動かしていったほうがいいと思います。
pythonを普段書かないので、パイソニックなコードになってないと思います。
次回はサンプルプログラムももう少しきれいに書きます。

参考情報
[エッジAIカメラ「S+ Camera Basic」でソラコムサンタを検知する](https://qiita.com/ma2shita/items/42ad9527b37495887449)
[ソラコムユーザードキュメント](https://users.soracom.io/ja-jp/docs/mosaic/develop-algorithm/)
[S+ Camera Basic が提供する API](https://users.soracom.io/ja-jp/guides/iot-devices/splus-camera-basic/api-reference/)
[【Python】Pillow ↔ OpenCV 変換](https://qiita.com/derodero24/items/f22c22b22451609908ee)
画像処理アルゴリズム入門　西住流著　２０１８年３月２０日発行　
ISBN978-4-77752046-6