【问题标题】:How to send and receive webcam stream using tcp sockets in Python?如何在 Python 中使用 tcp 套接字发送和接收网络摄像头流?
【发布时间】:2018-08-19 20:33:25
【问题描述】:

我正在尝试重新创建 this project。我拥有的是服务器(我的计算机)和客户端(我的树莓派)。与原始项目不同的是,我正在尝试使用简单的网络摄像头而不是树莓派相机将图像从我的 rpi 流式传输到服务器。我知道我必须:

  1. 从摄像头获取 opencv 图像帧。
  2. 将帧(一个 numpy 数组)转换为字节。
  3. 将字节从客户端传输到服务器。
  4. 将字节转换回帧和视图。

示例将不胜感激。

self_driver.py

import SocketServer
import threading
import numpy as np
import cv2
import sys


ultrasonic_data = None

#BaseRequestHandler is used to process incoming requests
class UltrasonicHandler(SocketServer.BaseRequestHandler):

    data = " "

    def handle(self):

        while self.data:
            self.data = self.request.recv(1024)
            ultrasonic_data = float(self.data.split('.')[0])
            print(ultrasonic_data)


#VideoStreamHandler uses streams which are file-like objects for communication
class VideoStreamHandler(SocketServer.StreamRequestHandler):

    def handle(self):
        stream_bytes = b''

        try:
            stream_bytes += self.rfile.read(1024)
            image = np.frombuffer(stream_bytes, dtype="B")
            print(image.shape)
            cv2.imshow('F', image)
            cv2.waitKey(0)

        finally:
            cv2.destroyAllWindows()
            sys.exit()


class Self_Driver_Server:

    def __init__(self, host, portUS, portCam):
        self.host = host
        self.portUS = portUS
        self.portCam = portCam

    def startUltrasonicServer(self):
        # Create the Ultrasonic server, binding to localhost on port 50001
        server = SocketServer.TCPServer((self.host, self.portUS), UltrasonicHandler)
        server.serve_forever()

    def startVideoServer(self):
        # Create the video server, binding to localhost on port 50002
        server = SocketServer.TCPServer((self.host, self.portCam), VideoStreamHandler)
        server.serve_forever()

    def start(self):
        ultrasonic_thread = threading.Thread(target=self.startUltrasonicServer)
        ultrasonic_thread.daemon = True
        ultrasonic_thread.start()
        self.startVideoServer()


if __name__ == "__main__":

    #From SocketServer documentation
    HOST, PORTUS, PORTCAM = '192.168.0.18', 50001, 50002
    sdc = Self_Driver_Server(HOST, PORTUS, PORTCAM)

    sdc.start()

video_client.py

import socket
import time
import cv2


client_sock = socket.socket()
client_sock.connect(('192.168.0.18', 50002))
#We are going to 'write' to a file in 'binary' mode
conn = client_sock.makefile('wb')

try:
    cap = cv2.VideoCapture(0)
    cap.set(cv2.cv.CV_CAP_PROP_FRAME_WIDTH,320)
    cap.set(cv2.cv.CV_CAP_PROP_FRAME_HEIGHT,240)

    start = time.time()

    while(cap.isOpened()):
        conn.flush()
        ret, frame = cap.read()
        byteImage = frame.tobytes()
        conn.write(byteImage)


finally:
    finish = time.time()
    cap.release()
    client_sock.close()
    conn.close()

【问题讨论】:

    标签: python sockets opencv raspberry-pi3 socketserver


    【解决方案1】:

    您不能只将每个接收到的 1-1024 字节缓冲区显示为图像;您必须将它们连接起来,并且仅在缓冲区完成时才显示图像。

    如果您知道,带外,您的图像将是固定数量的字节,您可以执行以下操作:

    IMAGE_SIZE = 320*240*3
    
    def handle(self):
        stream_bytes = b''
    
        try:
            stream_bytes += self.rfile.read(1024)
            while len(stream_bytes) >= IMAGE_SIZE:
                image = np.frombuffer(stream_bytes[:IMAGE_SIZE], dtype="B")
                stream_bytes = stream_bytes[IMAGE_SIZE:]
                print(image.shape)
                cv2.imshow('F', image)
                cv2.waitKey(0)
        finally:
            cv2.destroyAllWindows()
            sys.exit()
    

    如果您不知道,则必须添加某种帧协议,例如在每帧之前将帧大小作为 uint32 发送,这样服务器就可以知道每帧要接收多少字节。


    接下来,如果您只是发送原始字节,没有任何 dtype 或 shape 或 order 信息,则需要将 dtype 和 shape 信息嵌入到服务器中。如果您知道它应该是特定形状的 C 顺序字节,您可以手动执行此操作:

    image = np.frombuffer(stream_bytes, dtype="B").reshape(320, 240, 3)
    

    ...但如果没有,您也必须将该信息作为框架协议的一部分发送。

    或者,您可以将缓冲区的pickle.dumpspickle.loads 发送到另一侧,或者将np.save 发送到BytesIOnp.load 结果。无论哪种方式,这都包括 dtype、shape、order 和 stride 信息以及原始字节,因此您不必担心。


    下一个问题是,您一显示一张图片就退出了。这真的是你想要的吗?如果不是……就不要那样做。


    但这只会引发另一个问题。你真的想用cv.waitKey 阻止整个服务器吗?您的客户正在捕获图像并尽可能快地发送它们;当然,您要么想让服务器在它们到达时立即显示它们,要么更改设计以便客户端仅按需发送帧。否则,您只会得到一堆几乎相同的帧,然后在客户端被阻塞时会出现数秒长的间隙,等待您耗尽缓冲区,然后重复。

    【讨论】:

    • 非常好的答案!
    猜你喜欢
    • 2020-04-22
    • 2015-07-28
    • 2018-04-05
    • 2021-01-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多