利用OpenCV对视频进行操作 , 再利用 TCP协议 创建服务端和客户端 ,完成视频的远程传输…
tcp传输视频数据和tcp收发信息流程差不多
tcp客户端 :
1 init方法完成套接字的初始化,传入地址和端口,设置视频的分辨率
2 connect方法 利用客户端的socket对象的connect方法 连接服务器
3 send方法 读取摄像头拍摄的数据, ( socekt对象只发送二进制的数据 ) 把数据处理成二进制 , struct包的pack方法把里面的参数打包好自动转成二进制 , 再利用send发送出去
客户端完整代码:
import socket
import struct
import time
import traceback
import cv2
import numpy
class Client(object):
"""客户端"""
def __init__(self, addr_port=('192.168.52.1', 11000)):
# 连接的服务器的地址
# 连接的服务器的端口
self.addr_port = addr_port
# 创建套接字
self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 地址端口可以复用
self.client.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 分辨率
self.resolution = (640, 480)
def connect(self):
"""链接服务器"""
try:
self.client.connect(self.addr_port)
return True
except Exception as e:
traceback.print_exc() # 打印原始的异常信息
print('连接失败')
return False
def send2server(self):
"""读摄像头数据 发送给服务器"""
camera = cv2.VideoCapture(0) # 摄像头对象
print('isOpened:', camera.isOpened())
while camera.isOpened():
try:
# 获取摄像头数据
ret, frame = camera.read()
# 对每一帧图片做大小处理 和大小的压缩
frame = cv2.resize(frame, self.resolution)
# 参1图片后缀名 参2 原图片的数据 参3图片质量 0-100 越大越清晰
ret, img = cv2.imencode('.jpg', frame, [cv2.IMWRITE_JPEG_QUALITY, 100])
# img 是被压缩后的数据 无法正常显示
print(img)
print('-------------')
print(img.tostring())
# 转换为numpy格式数据
img_code = numpy.array(img)
# 转为二进制数据
img = img_code.tostring()
# 获取数据长度
length = len(img)
# 发送的数据 大小 宽 高 图片数据
# 数据打包变为二进制
# pack方法参数1 指定打包数据的数据大小 i 4字节 h代表2字节
all_data = struct.pack('ihh', length, self.resolution[0], self.resolution[1]) + img
self.client.send(all_data)
time.sleep(0.01)
except:
camera.release() # 释放摄像头
traceback.print_exc()
return
if __name__ == '__main__':
client = Client()
if client.connect():
client.send2server()
这个tostring把数据转成二进制 , 可以发送但是要还原的时候没有对应的方法 , 所以先把数据转为numpy格式,用他的方法可以还原数据
tcp服务端 :
有了之前tcp聊天室案例的基础 , 现在我们只要接收到从服务端发来的"视频数据"只要将它在逆操作得到可以原来的视频.
import socket
import struct
import threading
import cv2
import numpy
import pygame
class Server:
def __init__(self):
# 设置tcp服务端的socket
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置重复使用
self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 绑定地址和端口
self.server.bind(('192.168.198.129', 11000))
# 设置被动监听
self.server.listen(128)
def run(self):
while True:
print('等待客户端连接')
# 等待客户端连接
client, addr = self.server.accept()
ProcessClient(client).start()
class ProcessClient(threading.Thread):
def __init__(self, client):
super().__init__()
self.client = client
# 创建一个窗口
self.screen = pygame.display.set_mode((640, 480), 0, 32)
def run(self):
while True:
data = self.client.recv(8)
if not data:
break
# 图片的长度 图片的宽高
length, width, height = struct.unpack('ihh', data)
imgg = b'' # 存放最终的图片数据
while length:
# 接收图片
temp_size = self.client.recv(length)
length -= len(temp_size) # 每次减去收到的数据大小
imgg += temp_size # 每次收到的数据存到img里
# 把二进制数据还原
data = numpy.fromstring(imgg, dtype='uint8')
# 还原成矩阵数据
image = cv2.imdecode(data, cv2.IMREAD_UNCHANGED)
print(image)
cv2.imshow('窗口1', image)
cv2.waitKey(1)
if __name__ == '__main__':
server = Server()
server.run()
使用cv2.imshow显示视频,
因为地址和端口每个人不统一, 自己改一下 window系统使用cmd输出ipconfig查看自己的ip
优化:
之前用pygame做过 飞机大战 也可以利用pygame的窗口显示视频 , 只需要添加如下代码 :
# 把bgr转rgb
img = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
# 转换为pygame能显示的数据格式
img = pygame.image.frombuffer(img, (640, 480), 'RGB')
# 显示 在pygame上
self.screen.blit(img, (0, 0))
pygame.display.update()
opencv的色彩模式是 :bgr
pygame的色彩模式是 :rgb
我们用cv2获取到的数据想要在pygame上显示清楚 , 就要用cvtColor转换一下
.
现在的程序流程是 : 当客户端有一个人发来数据时把这个服务器扔进线程去处理, 然后开启一个pygame窗口 , 当第二个客户端发来数据时 , 那还是会在创建一个pygame窗口 ,我们接下来想让这些数据显示在一个窗口里面 , 就像是微信多人视频那样…
服务器完整代码 :
import queue
import socket
import struct
import threading
import cv2
import numpy
import pygame
class Server:
def __init__(self, q):
# 设置tcp服务端的socket
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置重复使用
self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 绑定地址和端口
self.server.bind(('192.168.52.1', 11000))
# 设置被动监听
self.server.listen(128)
# 队列
self.q = q
# 定义一个字典 , 记录每个窗口是否显示内容
self.nums = {1: False, 2: False, 3: False, 4: False, 5: False, 6: False}
def waiting(self):
while True:
print('等待客户端连接')
# 等待客户端连接
client, addr = self.server.accept()
for i, is_on in self.nums.items():
# 遍历字典 从前往后 找到没开的窗口
if not is_on:
# 找到了没用的窗口 , 开始使用 ,并改变他的值
self.nums[i] = True
# 把队列传入客户端的线程里 i是窗口号
ProcessClient(client, self.q, self.nums,i).start()
break
class ProcessClient(threading.Thread):
client: socket.socket
def __init__(self, client, q, nums,i):
super().__init__()
self.client = client
self.picture = pygame.image.load("picture.png")
self.q = q
self.i = i
self.nums = nums
def run(self):
while True:
try:
data = self.client.recv(8)
# 如果接收不到数据
if not data:
self.nums[self.i] = False
self.q.put([self.picture, self.i])
break
# 图片的长度 图片的宽高
length, width, height = struct.unpack('ihh', data)
self.img = b'' # 存放最终的图片数据
while length:
# 接收图片
temp_size = self.client.recv(length)
length -= len(temp_size) # 每次减去收到的数据大小
self.img += temp_size # 每次收到的数据存到img里
# 把二进制数据还原
data = numpy.fromstring(self.img, dtype='uint8')
# 还原成矩阵数据
image = cv2.imdecode(data, cv2.IMREAD_UNCHANGED)
print(image)
# opencv是bgr
# pygame是rgb
img = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
# 转换为pygame能显示数据格式
img = pygame.image.frombuffer(img, (640, 480), 'RGB')
self.q.put([img, self.i])
except:
self.nums[self.i] = False
self.client.close()
# 如果断开连接 显示一张默认图片
self.q.put([self.picture, self.i])
break
class MyPygame(threading.Thread):
def __init__(self, q):
super().__init__()
self.q = q
def run(self):
pygame.init()
self.screen = pygame.display.set_mode((640 * 3, 480 * 2), 0, 32)
image = pygame.image.load("picture.png")
self.screen.blit(image, (0, 0))
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
print("exit")
pygame.quit()
exit()
try:
# 从队列获取要显示的图片
img, num = self.q.get(timeout=0.1)
pass
except:
pass
else:
# 处理图片
self.show_img(img, num)
finally:
pygame.display.update()
def show_img(self, img, num):
if num == 1:
# 显示图片
self.screen.blit(img, (640 * 0, 480 * 0))
elif num == 2:
self.screen.blit(img, (640 * 1, 480 * 0))
elif num == 3:
self.screen.blit(img, (640 * 2, 480 * 0))
elif num == 4:
self.screen.blit(img, (640 * 0, 480 * 1))
elif num == 5:
self.screen.blit(img, (640 * 1, 480 * 1))
elif num == 6:
self.screen.blit(img, (640 * 2, 480 * 1))
if __name__ == '__main__':
# 队列
q = queue.Queue()
# 显示pygame的线程
mygame = MyPygame(q).start()
server = Server(q)
server.waiting()
每个客户端发来的连接都在一个线程里 , 带上他的窗口号(分配好的) , put进 队列 里,再创建一个线程 用来显示这些收到的数据,
这是别人教我的一种思路…