Python 3中使用套接字实现OpenCV视频实时流传输

7
我尝试使用Python 3和OpenCV创建一个简单的应用程序,通过socket发送实时视频流。由于我对OpenCV和socket编程都很陌生,如果您能提供详细的答案,我将不胜感激。谢谢。
下面是sender.py:
import socket
import time

import cv2

capture = cv2.VideoCapture(0)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('192.168.1.10', 50505))

while True:
    ret, frame = capture.read()
    data = cv2.imencode('.jpg', frame)[1].tostring()
    sock.sendall(data)
    time.sleep(2)

这里是receiver.py

import socket
import cv2
import numpy as np
import time

HOST = '192.168.1.10'
PORT = 50505

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print('Socket created')

s.bind((HOST, PORT))
print('Socket bind complete')

s.listen(10)
print('Socket now listening')

conn, addr = s.accept()

while True:
    data = conn.recv(8192)
    nparr = np.fromstring(data, np.uint8)
    frame = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
    cv2.imshow('frame', frame)
    time.sleep(2)

并且这是错误信息

 receiver.py", line 29, in <module>
        cv2.imshow('frame', frame)
    cv2.error: D:\Build\OpenCV\opencv-3.4.0\modules\highgui\src\window.cpp:339:
error: (-215) size.width>0 && size.height>0 in function cv::imshow
3个回答

9

我是VidGear视频处理Python库的作者,现在还提供NetGear API,专门设计用于实时在网络上同步传输连接系统之间的视频帧。您可以按照以下步骤进行尝试:

A. 服务器端:(最简单的示例)

打开您喜欢的终端并执行以下Python代码:

注意:您可以通过在服务器端键盘上按[Ctrl+c]随时结束服务器和客户端的流式传输!

# import libraries
from vidgear.gears import VideoGear
from vidgear.gears import NetGear

stream = VideoGear(source='test.mp4').start() #Open any video stream
server = NetGear() #Define netgear server with default settings

# infinite loop until [Ctrl+C] is pressed
while True:
    try: 
        frame = stream.read()
        # read frames

        # check if frame is None
        if frame is None:
            #if True break the infinite loop
            break

        # do something with frame here

        # send frame to server
        server.send(frame)
    
    except KeyboardInterrupt:
        #break the infinite loop
        break

# safely close video stream
stream.stop()
# safely close server
writer.close()

B. 客户端:(最基本的示例)

然后在同一系统上打开另一个终端并执行以下Python代码,查看输出:

# import libraries
from vidgear.gears import NetGear
import cv2

#define netgear client with `receive_mode = True` and default settings
client = NetGear(receive_mode = True)

# infinite loop
while True:
    # receive frames from network
    frame = client.recv()

    # check if frame is None
    if frame is None:
        #if True break the infinite loop
        break

    # do something with frame here

    # Show output window
    cv2.imshow("Output Frame", frame)

    key = cv2.waitKey(1) & 0xFF
    # check for 'q' key-press
    if key == ord("q"):
        #if 'q' key-pressed break out
        break

# close output window
cv2.destroyAllWindows()
# safely close client
client.close()

目前,NetGear支持两种ZeroMQ消息模式:即zmq.PAIRzmq.REQ和zmq.REP,支持的协议有:'tcp'和'ipc'。更高级的用法可以在这里找到:https://abhitronix.github.io/vidgear/latest/gears/netgear/overview/

如果您能在视频中实现这个的话,那会很好。关于其他类型的流媒体,比如纯粹的旧式ASCII数据呢?我接下来打算看一下Twisted。 - John Carlson
@JohnCarlson 你可以将任何类型的数据作为数组传递,因此传递疼痛ASCII应该可以直接使用。 - abhiTronix

4
由于您只接收到了少量数据,所以图片并不完整。在99.99%的情况下,8192字节是不够的,因为每个图像都大于8Kb。您需要获取发送者发送的所有数据才能将其转换为图像。
您可以查看我的代码 github 并根据您的需求进行更改。
简而言之,最简单的方法是首先向客户端发送字节数,然后再发送图像本身。在客户端代码中,在接收到图像的长度后,循环直到接收到所有字节。例如:
...
img_len = 175428 # received by sender.py
e=0
data = ''
while e < img_len:
  d = sock.recv(1024)
  e += len(d)
  data += d

nparr = np.fromstring(data, np.uint8)
frame = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
cv2.imshow('frame', frame)

3
迟来的回答,但对于那些寻找实时视频传输和接收的人,可以使用socket: Socket编程 这是结果的快照: 输入图像描述

server.py

import socket, cv2, pickle,struct

# Socket Create
server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
host_name  = socket.gethostname()
host_ip = socket.gethostbyname(host_name)
print('HOST IP:',host_ip)
port = 9999
socket_address = (host_ip,port)

# Socket Bind
server_socket.bind(socket_address)

# Socket Listen
server_socket.listen(5)
print("LISTENING AT:",socket_address)

# Socket Accept
while True:
    client_socket,addr = server_socket.accept()
    print('GOT CONNECTION FROM:',addr)
    if client_socket:
        vid = cv2.VideoCapture(0)
        
        while(vid.isOpened()):
            img,frame = vid.read()
            a = pickle.dumps(frame)
            message = struct.pack("Q",len(a))+a
            client_socket.sendall(message)
            
            cv2.imshow('TRANSMITTING VIDEO',frame)
            key = cv2.waitKey(1) & 0xFF
            if key ==ord('q'):
                client_socket.close()

client.py

import socket,cv2, pickle,struct

# create socket
client_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
host_ip = '192.168.1.20' # paste your server ip address here
port = 9999
client_socket.connect((host_ip,port)) # a tuple
data = b""
payload_size = struct.calcsize("Q")
while True:
    while len(data) < payload_size:
        packet = client_socket.recv(4*1024) # 4K
        if not packet: break
        data+=packet
    packed_msg_size = data[:payload_size]
    data = data[payload_size:]
    msg_size = struct.unpack("Q",packed_msg_size)[0]
    
    while len(data) < msg_size:
        data += client_socket.recv(4*1024)
    frame_data = data[:msg_size]
    data  = data[msg_size:]
    frame = pickle.loads(frame_data)
    cv2.imshow("RECEIVING VIDEO",frame)
    key = cv2.waitKey(1) & 0xFF
    if key  == ord('q'):
        break
client_socket.close()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接