DreamerDreamのブログ

夢想家の夢です。〜揚げたてのモヤっとしたものをラフレシアと共に〜

PythonでOpenCVを使ってWebサーバからのリクエストでUSBカメラから画像キャプチャ保存

こちらのブログを参考にOpenCVをインストールしてみた。

はんだ日和: Raspberry Pi にOpenCV をインストール

 

いろんなサイトでOpenCVをインストールするのはかなり骨の折れる作業だと書いてあるんですが、こちらのサイトの「②の方法」だとあっという間にインストールが終ります。

最新のバージョンが良いのでしょうけれど、最新バージョンでしか使えないような斬新な使い方はしないので僕は楽な②の方を選択。

 

インストール

sudo apt-get install libopencv-dev

バージョンの確認

nano /usr/include/opencv2/core/version.hpp

僕のインストールしたバージョンは2.4.9でした。

 

pythonからOpenCVを使うには,追加でパッケージが必要だそうなのでインストール

sudo apt-get install python-opencv

これで準備完了

 

これらのサイトを参考にさせていただくと

Python版OpenCVでカメラのキャプチャ

金星☆ちゃんねる: Python版OpenCVでWebカメラの画像を取得する

 

cv2をインポートして使うようだ。

GUIで使う場合は上記リンク通りにすれば映るらしい。

ただ、僕がやりたいのはCUI環境で設定してWEBページに表示させる。とWEBカメラのような使い方だ。

f:id:DreamerDream:20191230141500p:plain


 

RaspberryPiでWebカメラを作るには「motion」というアプリを使うのが一般的らしい。動きがあった際に自動的に画像保存してくれる優れものだ。

参考ブログ

http://divide-et-impera.org/archives/1098

 

しかしSSL接続したかったりWebページにスイッチを組み込んでGPIOと連携させたかったりしたいのでmotionを使わない方法を検索したがよくわからなかった。

 

なので無理矢理見れるようにpythonでコードを作った。

SSL接続でタイムラグが5〜10秒ほど発生するのでラジコン等の操作には実用的では無いがGPIOでオートフィーダーを目指す目的なのでペットの様子を見るには十分だ。

 

OpenCVでは

cv2.imwrite(セーブデータ, キャプチャしたイメージ)

で、データに指定した拡張子で保存してくれるらしい。

 

デーモンプログラムとして最初から起動させておいてカメラキャプチャコマンドとデータを内部メモリ(/dev/shm/cap/)でやりとりするという方法にした。

file_mngは以前作ったものを使用

dreamerdream.hateblo.jp

 

 

動作は

  1. システム終了時にカメラをリリースする設定(カメラをリリースしないと次回起動時にカメラを認識してくれないから最初に設定が絶対必要)
  2. カメラを起動させて「standby」とcomandファイルに記録
  3. comandファイルを0.2秒間隔で読み込んで10秒間何もなければ「sleep」と書き込み、読み込み間隔を1秒に更新
  4. comandファイルに他のプログラムから「get」と入力されていればcap.jpgというファイルで画像を保存して「ok」とcomandファイルに書き込む
  5. 3.へ戻る

これだけ。

 

これでWebサーバーから0.5秒間隔ぐらいでのアクセスだと問題無く読み込まれている様子。

いろいろ試行錯誤した結果が下記のコードになった。ゴチャってるけれどとりあえず正常に動いている。

 -- camerad.py --

#!/usr/bin/python

# -*- coding: utf-8 -*-

 

import cv2

import os,sys

import file_mng

import threading, time

import atexit

 

save_path = "/dev/shm/cam"

save_cap = "/cap.jpg"

save_com = "/comand"

 

count = 0

speed = 1.0

isCapture = False

capture = None

 

def chmod():

        if file_mng.is_file(save_path + save_com) is True:

                os.chmod(save_path + save_com,0777)

 

def set_comand(message = "non"):

                #print message

                file_mng.save(save_path + save_com,message)

                chmod()

 

def get_comand():

                lst = file_mng.load(save_path + save_com)

                if 0 < len(lst):

                        return lst[0]

                else:

                        return "non"

 

def camera_init():

        global isCapture, capture

        if os.path.isdir(save_path) == False:

                file_mng.make_dir(save_path)

                set_comand("init")

 

        isCapture = True

        set_comand("loading")

        capture = cv2.VideoCapture(0)

        if capture.isOpened() is False:

                raise("Camera Error")

        set_comand("stndby")

 

def camera_get():

        global count,speed,isCapture

        #time.sleep(0.2)

        count = 0.0

        speed = 0.01

        if isCapture is False:

                camera_init()

        try:

                set_comand("now")

                ret, image = capture.read()

        except:

                ret = False

        if ret is True:

                file_mng.del_file(save_path + save_cap)

                cv2.imwrite(save_path + save_cap, image)

                set_comand("ok")

                cv2.waitKey(1)

        else:

                set_comand("read_NG")

 

def camera_end():

        global capture,isCapture,speed,count

        count = 0.0

        speed = 1.0

        set_comand("sleep")

        if capture is not None:

                capture.release()

                capture = None

                isCapture = False

 

def loop():

        global count

        while 1:

                count += speed

                if "get" == get_comand() and 0.2 < count :

                        camera_get()

 

                if 9.9 < count:

                        camera_end()

                time.sleep(speed)

 

def init():

        atexit.register(camera_end)

        set_comand("start")

        camera_get()

        loop()

  

def fork():

        pid = os.fork()

 

        if pid > 0:

                f = open('/var/run/camerad.pid','w')

                f.write(str(pid)+" ")

                f.close()

                sys.exit()

 

        if pid == 0:

                init()

 

if __name__ == '__main__':

        fork()

 

保存先ディレクトリのパーミッションも変更しているのでWebサーバーからは/dev/shm/cam/comandファイルへ「get」と書き込んでcap.jpgを読み込むだけという単純な方法で画像を取得出来る。

 

HTMLにはJavascriptで0.5間隔で画像を更新するようにすれば良い

Bottle用のテンプレート

<html>
<body>

hello:{{name}}<br>

ライブ映像<br>
<script type="text/javascript">
var timer = window.setInterval(function() {
document.getElementById("hoge").src = "../../static/cap.jpg?" + (new Date() - 0);
}, 500);
</script>

<img id="hoge" src="../../static/cap.jpg" alt="ライブ画像">

</body>
</html>

今回実験で使ったカメラ↓

 

kampa.me