はじめに
監視カメラでも作ろうかと過去のコードをぴっぱり出したところ、コピペでは動作しなくなっていたので、最近の仕様変更に合わせたコードへの修正となります。
具体的には、RaspberryPiで取得したデータをほかのデバイス(Pythonが動くもの)で表示させるというものになります。
本コードは拡張性が高く、リアルタイム映像をAI・機械学習等の処理にかけるときに別のサーバー上で処理ができるので大変重宝しております。簡易監視カメラにもなりますし、Pythonで書かれているのでNAS等に動画を保存しておくことも可能です。可能性は無限大と言えますね!
中華のIPカメラでやれよって話ですけどね。
目標
本記事では映像の転送にPython3.7を使用しております。いまだにRaspberryPiの標準装備だったので採用しました。
ライブラリにはSocketserverとOpenCVを利用しております。
ハードウェアには自宅に転がっていたRaspberryPi4とWindows10PCを利用しています。
カメラは選択さえすれば良いのでUSBカメラでもOKです。
コード
まずカメラ側、RaspberryPi側のコードです。
とはいっても単にSocketserverでサーバーを建ててOpenCVで圧縮した映像のデータを転送するだけです。
import socketserver
import cv2
import sys
HOST = "192.168.XXX.XXX" # ここはRaspberryPiのIPアドレスを入力
PORT = 5569
class TCPHandler(socketserver.BaseRequestHandler):
videoCap = ''
def handle(self):
ret, frame = videoCap.read()
encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), 100] # この値100は映像の質の値
jpegs_byte = cv2.imencode('.jpeg', frame, encode_param)[1]
self.request.send(jpegs_byte)
videoCap = cv2.VideoCapture(0)
socketserver.TCPServer.allow_reuse_address = True
server = socketserver.TCPServer((HOST, PORT), TCPHandler)
try:
server.serve_forever()
except KeyboardInterrupt:
server.shutdown()
sys.exit()
この程度のコードにClass分けなどしたくないのですが、Socketserverのドキュメントには使い方としてこの書き方をしていたため、致し方なくそのまま利用です。
して受信側のコードは以下の通りです。
import socket
import numpy
import cv2
HOST = "192.168.XXX.XXX" # ここはRaspberryPiのIPアドレスを入力
PORT = 5569
def getimage():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((HOST, PORT))
buf = []
recvlen = 100
while recvlen > 0:
receivedstr = sock.recv(1024*8)
recvlen = len(receivedstr)
buf += receivedstr
sock.close()
recdata = numpy.array(buf, dtype='uint8')
return cv2.imdecode(recdata, 1)
while True:
img = getimage()
cv2.waitKey(5) # 少し待ってやらないと映像が生成される前に次の処理が来て映像が映りませんでした。
# img_90deg = cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE) # 映像が時計方向90度に曲がっていた場合
cv2.imshow('Capture', img)
後はそれぞれを起動させて各々アクセスするだけです。
まとめ
実際に動作させているとこんな感じでWindowが出て映像をリアルタイムで表示します。
しかしこの場合はオートフォーカス機能がなく、ぼやぼやの映像が映し出されてますね。
ちなみにテストで24時間連続で稼働させてみましたが、特にエラーを吐くこともなく今のところ順調に接続されています。
過去のコードと比較していくつか小さな変化はあるのですが、最も大きかったのはcv2.waitKey(5) これでした。いままでRaspberryPi3で利用していた時にはこの行は不要だったのですが、4になってから処理速度が劇的に向上して、映像が形成される前に通信処理が済んでしまうため映像が中々出てきませんでした。
皆さんは自身の利用するデバイスに応じてこの値を弄ってみてください。
セオリー的には処理の遅いハードウェアにwaitって入れるものですけど、いやはや処理が早すぎるってのも罪ですね。