Rasberry Piで動画送信してみる

Raspberry Piでキャプチャした画像を、リクエストのあったクライアントにJpeg画像で送るTCPサーバと、クライアントをPythonで作ってみる。
連続でJpeg画像を表示することによって動画として再生できるようにする。

サーバ側

import cv2
import numpy

class Getter:
    cap: cv2.VideoCapture
    avg: numpy.ndarray = None
    encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), 100]

    def __init__(self):
        self.cap = cv2.VideoCapture(0)

    def __del__(self):
        self.cap.release()

    def getImage(self):
        ret, frame = self.cap.read()
        rreturn cv2.imencode('.jpeg', frame, self.encode_param)[1]

Jpegイメージを取得するクラスを作成する。(getter.py)

import getter

import cv2
import socketserver
import sys

HOST = "192.168.0.2" # サーバのアドレス
PORT = 1234

class TCPHandler(socketserver.BaseRequestHandler):
    gt = getter.Getter()

    def handle(self):
        while True:
            recv = self.request.recv(1024).strip()
            recvStr = recv.decode('utf-8')

            if recvStr == "bye":
                break

            jpeg_bytes = self.gt.getImage()

            if jpeg_bytes is None or not jpeg_bytes.any():
                self.request.send((0).to_bytes(4, 'little'))
                continue

            self.request.send(len(jpeg_bytes).to_bytes(4, 'little'))
            self.request.send(jpeg_bytes)

socketserver.TCPServer.allow_reuse_address = True
server = socketserver.TCPServer((HOST, PORT), TCPHandler)

try:
    server.serve_forever()
except KeyboardInterrupt:
    server.shutdown()
    sys.exit()

サーバプログラム、TCP接続を受け入れたらなにかデータが送られてくるのを待つ。
送られてきたデータが文字列でbyeだった場合は通信を終了する。
byeでなければ画像を取得し、4byteでデータサイズを送信した後に、画像データを送信する。
一枚送信し終わったらまた受信待機して待つ。

クライアント側

import cv2
import numpy
import socket

HOST = "192.168.0.2"
PORT = 1234

sock = None

class Getter:
    def __init__(self):
        self.sock = sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.sock.connect((HOST, PORT))

    def __del__(self):
        self.sock.send('bye'.encode('utf-8'))
        self.sock.close()

    def getImage(self):
        buf = []
        recvlen = 180
        image_size = 0

        self.sock.send('img'.encode('utf-8'))

        while True:
            received = self.sock.recv(1024 * 8)
            recvlen = len(received)

            if recvlen <= 0:
                if len(buf) <= 0:
                    print("No Data")
                break

            if image_size == 0:
                image_size = int.from_bytes(received[:4], 'little')

                if image_size == 0:
                    print("No image")
                    return None

                received = received[4:]

            buf += received

            if len(buf) >= image_size:
                break

        if image_size == 0:
            return None

        recdata = numpy.array(buf, dtype='uint8')

        return cv2.imdecode(recdata, 1)

def main():
    getter = Getter()

    while True:
        img = getter.getImage()

        if img is None:
            continue

        cv2.imshow('Capture', img)
        if cv2.waitKey(5) & 0xFF == ord('q'):
            break


if __name__ == '__main__':
    main()

サーバ側のプログラム実行後に、クライアント側プログラムを実行するとクライアント側のマシンでも動画を見ることができるようになる。
このプログラムの場合、Jpeg画像をそのまま送っているので通信速度がそれなりに必要となる。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください