Raspberry Pi3 Model B とWebカメラで遠隔撮影

# Raspberry Pi3 Model B と Webカメラで遠隔撮影
家にRaspberry PiとWebカメラが転がっていたので、大学の自由課題として作ってみました。
Linux系のOS使用したことがある人であれば、誰でも作れるような記事にする予定です。
初心者の人でもググりながらであれば、普通につくれると思います！

# 使用したもの
- Raspberry Pi 3 Model B
もしこれから買うのであれば、3 Model B+や4を買ったほうがいいかと思います!
BはWi-Fiの5GHzに非対応なので、作業効率が結構悪かったです...

- Logicool Webカメラ C525
なんか中途半端な性能のWebカメラ...
価格重視なら低価格なC270、性能重視なら...C910とかがおススメ？

- Slack
IT企業御用達のチャットツールです！
今回はこのチャットツールで指示を出し、遠隔撮影させます。
アカウントの作成は事前に済ませておいてくださいね！

- 備品
マウス、キーボード、モニター、電源ケーブル、HDMIケーブル、PC、MicroSDカード...など
これらはRaspberry Piを使用するのに必要です。

# 下準備
以下の記事を参考に、Raspberry PiにNOOBSをインストール。
[第56回「改めましてラズベリーパイの基本！(1) Raspberry Pi　NOOBSインストール 2017年度版」](https://deviceplus.jp/hobby/raspberrypi_entry_056/)

次に、プログラムを記述するためのエディターのインストール。
[ラズベリーパイを使ってみる – 歴史あるエディタ emacs をインストールする – ](https://www.limemo.net/blog/2013/12/ラズベリーパイを使ってみる-なれると使いやす.html)
端末にこれを打ち込みます。

```Bash
$ sudo apt-get install emacs
```

各自お好みのテキストエディタを入れてください！
vimとか聞いたことないよ！って人は、emacsが無難かと思います。
ただ多少の癖はあります...
[Emacsの使い方](http://hogehoge.konjiki.jp/)


# python3にslackbotライブラリをインストール

```Bash
$ sudo apt-get install python3-pip
$ sudo pip3 install slackbot
$ sudo apt-get install python3-opencv
```

ダウンロード成功！的な表示があれば大丈夫です。
もし、あとでうまくいかなかった場合、

```Bash
$ sudo apt-get install slacker
```

も入れてみてください。
# slackbotの設定
slackbotを動かすにあたって、以下のようなディレクトリ構造にします。

- slack
    - run.py
    - slackbot_settings.py
    - plugins
        - \_\_init\_\_.py
        - take_picture.py

slackとpluginsはディレクトリになっています。
まず、ディレクトリを作ります。今回は、ホームディレクトリ直下に作成します。

```Bash
$ cd ~/
$ mkdir slack
$ cd slack/
$ mkdir plugins
```

これで完成しました。次に、必要なPythonプログラムを作成します。

```Bash
$ touch run.py slackbot_settings.py
$ cd plugins/
$ touch __init__.py take_picture.py
```

これで空のPythonファイルが作成できました！
これ以降はディレクトリ移動の記述をしないので、ここまでの作業で「cdって何？？」と感じた人は、以下の記事を参考にしてください。
[cdコマンド——ディレクトリの移動](https://raspida.com/cd-command)

# Pythonプログラムの作成
まず、run.pyを仕上げます。slackディレクトリへ移動し、

```Bash
$ emacs run.py & 
```

そして、出てきたエディタに以下を記述。

```python3:run.py

from slackbot.bot import Bot

def main():
    bot = Bot()
    bot.run()

if __name__ == '__main__':
    printf('launch slackbot')
    main()
```


同様にslackbot_settings.pyも作成します。


```Bash
$ emacs slackbot_settings.py & 
```


```python3:slackbot_settings.py
API_TOKEN = "xoxv-156423218-651873211325-Asf44a4ds5f45AS552" #参考例です。各自で取得したトークン値を入力してください。

DEFAULT_REPLY = "わかりませーん..."

PLUGINS = ['plugins']
```


なお、Botの取得＆トークンの作成は、以下のページを参考にしました。
[PythonによるSlack bot作成](http://blog.roy29fuku.com/web/slack-bot-with-python/)
[slackのbotを作る 入門編](http://shutosg.hatenadiary.com/entry/2016/03/14/030519)
[Raspberry Piでslackbotを飼う](https://qiita.com/undo0530/items/2139a1e8b73b3eee6e00)


そして、pluginsディレクトリに移動し、take_picture.pyを作成します。


```python:take_picture.py
from slackbot.bot import respond_to
from slackbot.bot import listen_to
from slackbot.bot import default_reply
from slacker import Slacker
import os
import cv2
import time

@respond_to('写真')
def picture_func(message):
    message.reply('撮影中...')
    

    #take_picture
    cap = cv2.VideoCapture(0)
    cap.read()
    time.sleep(1)

    ret, frame = cap.read()
    cv2.imwrite("/home/pi/slack/plugins/picture.jpg",frame)
    time.sleep(2)
    cap.release()
    time.sleep(2)


    #upload_picture

    TOKEN = "xoxv-156423218-651873211325-Asf44a4ds5f45AS552"
    #先ほどのトークンと同じものを記述
    CHANNEL = "APODV2HW1"
    #取得方法は後述

    file = "/home/pi/slack/plugins/picture.jpg"
    filename = 'webcam'

    message.reply('撮影完了...！')
    time.sleep(1)
    slacker = Slacker(TOKEN)
    slacker.files.upload(file_=file, channels=CHANNEL)
```


CHANNELの値は、PCでslackを開いたときのリンクの__' '__で囲まれた部分です。私は\#generalのCHANNEL値を取得しました。


```
https://app.slack.com/client/MQ18ALEJ/'APODV2HW1'/files
```

#実行方法
slackディレクトリに移動し、

```
python3 run.py
```

を実行するだけです。そして、slackで以下のように指示すると写真が撮れます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477153/07bdd576-1fe4-03a3-1a15-22258f351ffc.jpeg" width=50%>

ちなみに、raspbotは自作Botの名前です。そしてiPhoneから命令出してます。早朝の真っ暗な部屋でモニターを撮影させています。

#解説
###run.py 

実行プログラムです。このプログラムを実行すると、自作したBotが実行されます。

```bash
$ python3 run.py 
```

  
###slackbot_settings.py
自作したBotが必要とするデータを格納しておくプログラムです。


- API_TOKEN
自作したBotの識別値

- DEFAULT_REPLY
このBot宛てのメッセージで、定義していない言葉が送られてきたときの反応

- PLUGINS
自作Botの機能拡張を行うプログラムの保存場所。この例では、~/slack/plugins/ディレクトリ内にあるプログラムも、run.pyの実行と同時に実行されます。

###\_\_init\_\_.py
中身は空のプログラムです。

###take_picture.py
ここで、自作Bot宛てに送られてきたメッセージ内容の判定、写真の撮影、slack上にアップロードのすべての処理を行っています。

- @respond_to(' ')
'   '内に囲まれた言葉(この例だと'写真')が送られてきた場合、以下に記述した処理(写真の撮影とアップロード)を行う。

-time.sleep(1)
()内に入れた秒数だけ次の処理の開始を遅らせる。少数も代入可能。

- cap = cv2.VideoCapture(0)
webカメラの割り当てを行う。()内に入れた数値で、どのカメラを使用するか決める。
なお、1つしかカメラが接続されていない場合は0または-1を代入する必要がある。

- ret, frame = cap.read()
retにはTrue / False、frameには画像データが返される。

- cv2.imwrite("/home/pi/slack/plugins/picture.jpg",frame)
画像を指定した名前で指定したディレクトリに保存する。

- cap.release()
カメラの解放。

- slacker.files.upload(file_=file, channels=CHANNEL)
Slacker(TOKEN).files.upload(file_="/home/pi/slack/plugins/picture.jpg"
, channels=CHANNEL)ということ。
要するに画像アップロード処理。



#補足説明
試行錯誤しながら作成したので、不要な部分が結構ありそうですが、多めに見てください...

###\_\_init\_\_.py
このプログラムが置かれているディレクトリはパッケージとして扱われるそうです。あと、ここに初期化処理を記述することもできます。
なお、Python3.3以降ではこれが無くても、パッケージとして認識するそうなので、おそらく作成しなくても実行できると思いますが...慣習的なものなので作成しました。


###'  'と"   "の違い
Pythonにおいて、違いはありません。ごちゃ混ぜになってますが問題はありません。

###take_picture部分の処理

```python:take_picture.py
    cap = cv2.VideoCapture(0)
    cap.read()
    time.sleep(1)

    ret, frame = cap.read()
```


この部分では、1枚目の写真を撮った後1秒待ち、2枚目の写真を撮影し保存しています。このような処理をしている理由ですが、どこかでLogicoolのカメラはこのようにしたほうが良い、というような記事を見たからです。(どこのページだったか忘れたのでリンクはありません...ごめんなさいm(_ _)m)
そこで、実際に比較実験をしたところ、C525では目に見える差がありませんでした。よって気分でこの方法を使用しています。
もし、1秒でも短縮したい場合は、

```python:take_picture.py
    cap = cv2.VideoCapture(0)
    #cap.read()
    #time.sleep(1)

    ret, frame = cap.read()
```

このようにコメントアウトでもしてください。

###slackerについて
当初、画像アップロード処理中にエラーが多発していました。

```
requests.post(url = 'https://~~~ ',params = param, files = file)
```

で画像アップロード処理を行っていたんですが、デバッグをしたところ、この関数がうまく機能していなかったので、slackerでアップロード処理を行いました。ただし、requests.post()のほうが一般的っぽいです。

###ipアドレスの固定化問題
sshログイン環境を整えるため、Raspberry Piのプライベートipアドレスを固定したところ、slackへの接続ができなくなってしまいました...slack側で設定をいじれば繋がる可能性がありますが、残念ながらslackに詳しくないため、ipアドレスの固定化を断念して使っています。

#最後に
長文にお付き合いいただき、ありがとうございました！
おそらくこれで作れると思いますが、もし何かミスがあったらご指摘お願いします...！