首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使用Python中的tcp套接字发送和接收摄像头流?

如何使用Python中的tcp套接字发送和接收摄像头流?
EN

Stack Overflow用户
提问于 2018-08-19 20:33:25
回答 1查看 6.8K关注 0票数 3

我正在尝试重新创建这个项目。我拥有的是一个服务器(我的计算机)和一个客户端(我的raspberry pi)。我所做的与最初的项目不同的是,我试图使用一个简单的摄像头,而不是树莓皮相机,以将图像从我的rpi流到服务器。我知道我必须:

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

请举例说明。

self_driver.py

代码语言:javascript
复制
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

代码语言:javascript
复制
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()
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-08-19 20:59:13

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

如果您知道,在带外,您的图像将是一个固定的字节数,您可以这样做:

代码语言:javascript
复制
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、形状或顺序信息,则需要将dtype和形状信息嵌入到服务器中。如果您知道它应该是一个特定形状的C顺序字节,您可以手动这样做:

代码语言:javascript
复制
image = np.frombuffer(stream_bytes, dtype="B").reshape(320, 240, 3)

…但如果不是,你也必须将这些信息作为框架协议的一部分发送。

或者,您可以发送缓冲区的pickle.dumps并将其放在另一端的pickle.loads,或者将结果发送给BytesIOnp.load。无论哪种方式,都包括dtype、形状、顺序和步长信息以及原始字节,所以您不必担心它。

下一个问题是,只要显示一个图像,您就会退出。这就是你想要的吗?如果不是…别这样就行了。

但这只会带来另一个问题。你真的想用那个cv.waitKey阻止整个服务器吗?您的客户端正在捕获图像并尽可能快地发送它们;当然,您希望让服务器在它们到达时立即显示它们,或者更改设计,以便客户端只根据需要发送帧。否则,你只会得到一堆接近相同的帧,然后在客户端被阻塞时,会有一个很长的间隔,等待你耗尽缓冲区,然后重复。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51921631

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档