我的程序是通过opencv读取视频,然后进行图片处理,之后输出到前端界面。在这个过程中我想使用socket将处理后的图片发送到局域网内的其他电脑。但是只能发送一帧。我的本机作为服务器端,另一台电脑作为接收端。我想在不影响我当前程序的运作下,能把图片也发出去。但是现在我写的socket是阻塞的,必须接受信号才能运作,运行也只能运行一次 就卡死。如何修改可以一边不影响我的程序,一边还能发信息。
class VideoStreamingTest(object):
def __init__(self,img,id,a):
self.connection, self.client_address=a[id].accept()
self.connect = self.connection.makefile('wb') # 创建一个传输文件 写功能 写入数据时b''二进制类型数据
self.host_name = socket.gethostname()
self.img=img # 获得服务端主机名
self.host_ip = socket.gethostbyname(self.host_name) # 获得服务端主机IP地址
time.sleep(3)
self.start()
def start(self):
print("客服端已连接:")
print("Client Host Name:", self.host_name)
print("Connection from: ", self.client_address)
print("Streaming...")
stream = io.BytesIO() # 创建一个io流,用于存放二进制数据
try:
frame=self.img
img_encode = cv2.imencode('.jpg', frame)[1] # 编码
data_encode = np.array(img_encode) # 将编码数据转换成二进制数据
stream.write(data_encode) # 将二进制数据存放到io流
self.connect.write(struct.pack('<L', stream.tell())) # struct.pack()将数据转换成什么格式 stream.tell()获得目前指针的位置,将数据写入io流后,数据指针跟着后移, # 也就是将数据长度转换成'<L'类型(无符号长整型),写入makefile传输文件 # 它的作用相当于 帧头数据,单独收到这个数据表示开始传输一帧图片数据,因为图片大小确定,这个数也就定下不变
self.connect.flush() # 刷新,将数据长度发送出去
stream.seek(0) # 更新io流,将指针指向0
self.connect.write(stream.read()) # 指针指向0后,从头开始读数据,然后写到makefile传输文件
stream.seek(0) # 更新指针
stream.truncate() # 更新io流数据,删除指针后面的数据
# 发送0,相当于帧尾数据,单独收到这个数表示一帧图片传输结束
except:
print(" ")
print("The customer service is disconnected!")
self.connect.write(struct.pack('<L', 0))
#self.connection.close()
print("与客服端断开连接!")
print(" ")
这是我在网上找的传输代码 但是我不知道怎么停止,让这个函数可以传输出去 就运行我程序的下一步。我把上面的函数添加到我处理完图像的函数之后,在要发到前端界面之前的位置。但是他会在socket停住,每次只能发送一张,等待我客户端运行一次 这边发一张。
self.is_draw = False
self.SaveNowVideo()
#self.send_look(img=self.img, id=self.id)
VideoStreamingTest(img=self.img,id=self.id,a=a)#这句就是上面的那段
self.img = cv2.cvtColor(self.img, cv2.COLOR_BGR2RGB)
self.img_single.emit(self.img, self.id)
可以使用 Threading 创建多线程处理这些问题
要在不影响您的程序的情况下发送图片,您可以使用异步socket编程。使用异步socket编程,您可以在处理图像的同时发送图片,而不会阻塞主线程。
不知道你这个问题是否已经解决, 如果还没有解决的话:答案:
为了实现非阻塞式socket,在Python中,我们可以使用select模块。select模块允许程序同时监听多个文件描述符(即socket),并在其中之一变为可读或可写时通知程序,不需要阻塞等待。
使用非阻塞式socket,我们可以在程序发送图片的同时,继续进行其他操作,比如继续读取视频进行图片处理等。当socket变为可写时,我们就可以发送下一张图片了。
以下是一个可以满足你需求的样例代码,其中包含了一个使用非阻塞式socket发送处理好的图片到另一台电脑的函数:
import socket
import select
import cv2
import pickle
import struct
def send_image(frame, ip, port):
# 编码图片
data = pickle.dumps(frame)
# 将消息头打包成struct格式,其中存储了消息的大小
header_size = struct.pack('!I', len(data))
# 创建socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setblocking(0) # 非阻塞模式
s.connect_ex((ip, port))
# 等待连接成功或者失败,如果失败就继续等待,直到成功为止
while True:
ready, _, _ = select.select([], [s], [], 1) # 等待socket变为可写
if ready:
break
# 发送消息头
s.send(header_size)
# 发送消息体
s.send(data)
# 等待消息发送完成
s.shutdown(socket.SHUT_WR)
# 等待连接断开
while True:
ready, _, _ = select.select([s], [], [], 1) # 等待socket变为可读
if ready:
# 接收返回消息
response = s.recv(1024)
if not response:
break
# 处理返回消息
print(response.decode())
s.close()
在这个函数中,我们先将图片序列化并打上消息头信息(消息体的大小)。然后我们创建一个socket连接到指定IP和port。由于使用了非阻塞模式,我们需要等待socket成功连接或者失败,并在socket变为可写时发送消息。发送完成后,我们再等待socket变为可读,接收服务器端返回的消息。如果返回消息为空,说明连接已关闭,可以关闭socket。
在主程序中,你可以利用该函数来发送处理好的图片。在程序发送图片的同时,你可以继续读取视频并进行图片处理等操作。以下是一个样例代码:
cap = cv2.VideoCapture('sample.mp4')
while True:
# 读取视频帧并进行图片处理
_, frame = cap.read()
processed_frame = cv2.flip(frame, 0)
# 显示图片到前端界面
cv2.imshow('Processed Frame', processed_frame)
# 发送处理好的图片
send_image(processed_frame, '127.0.0.1', 8000) # 发送到指定IP和port
if cv2.waitKey(1) == ord('q'): # 按'q'键退出
break
cap.release()
cv2.destroyAllWindows()
在这个样例代码中,我们使用OpenCV读取名为sample.mp4的视频文件,并进入一个循环中。在循环中,我们首先读取一帧视频,并进行图片处理(这里只是简单地将图片进行上下翻转)。然后我们将处理后的图片显示到前端界面中,同时调用之前编写的send_image函数,将处理后的图片发送到另一台电脑。如果按下了'q'键,程序退出循环并关闭视频。