如何在Python中使用tcp套接字发送和接收网络摄像头流?
我正试图重新创造。我有一台服务器(我的计算机)和一台客户端(我的raspberry pi)。与最初的项目不同的是,我尝试使用一个简单的网络摄像头而不是raspberry pi摄像头将图像从我的rpi传输到服务器。我知道我必须:如何在Python中使用tcp套接字发送和接收网络摄像头流?,python,sockets,opencv,raspberry-pi3,socketserver,Python,Sockets,Opencv,Raspberry Pi3,Socketserver,我正试图重新创造。我有一台服务器(我的计算机)和一台客户端(我的raspberry pi)。与最初的项目不同的是,我尝试使用一个简单的网络摄像头而不是raspberry pi摄像头将图像从我的rpi传输到服务器。我知道我必须: 从相机获取opencv图像帧 将帧(numpy数组)转换为字节 将字节从客户端传输到服务器 将字节转换回帧和视图 请举例说明 self\u driver.py import SocketServer import threading import numpy as np
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()
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()
视频客户端.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()
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()
您不能将接收到的每个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发送,这样服务器就可以知道每个帧要接收多少字节
接下来,如果只是发送原始字节,而没有任何数据类型、形状或顺序信息,则需要将数据类型和形状信息嵌入到服务器中。如果您知道它应该是,例如,特定形状中以C顺序排列的字节,您可以手动执行此操作:
image = np.frombuffer(stream_bytes, dtype="B").reshape(320, 240, 3)
…但如果没有,您也必须将该信息作为帧协议的一部分发送
或者,您可以发送缓冲区的pickle.dumps
,然后pickle.loads
在另一侧,或者np.save
到BytesIO
并np.loads
结果。无论哪种方式,都包括数据类型、形状、顺序和步幅信息以及原始字节,因此您不必担心它
下一个问题是,只要显示一个图像,您就会退出。这真的是你想要的吗?如果没有…就不要那样做
但这只会引发另一个问题。你真的想用这个
cv.waitKey
阻止整个服务器吗?您的客户正在捕获图像并尽可能快地发送它们;当然,您要么想让服务器在它们到达时立即显示它们,要么改变设计,让客户端只按需发送帧。否则,您只会得到一堆几乎相同的帧,然后在客户端被阻塞等待您耗尽缓冲区时,会有几秒钟的间隔,然后重复。回答得很好!