Python使用OpenCV播放mss截取的桌面屏幕(包含本机和他机环境)
效果展示
python实现windows同屏捕捉
先上代码
本机环境 不区分服务器和用户端,便于理解基础运行原理
他机环境 区分服务器和用户端,额外需要理解网络传输的相关知识
原理
- 截屏获取当前桌面图像,参考python捕捉windows桌面
- 使用OpenCV显示图像,图像展示不全参考python-openCV展示图像不全的问题
with mss.mss() as sct:
frame = sct.grab(monitor)
cv2.imshow("Video", np.array(frame))
- 重复执行1~2,实现帧序列转视频
- 他机环境则需要搭建socket通道
- 服务器
# 监听地址和端口
server_address = ("127.0.0.1", 8080)
# 创建TCP套接字
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定到指定地址和端口
sock.bind(server_address)
# 开始监听
sock.listen(1)
- 用户端
# 远程服务器的地址和端口
server_address = ("127.0.0.1", 8080)
# 创建TCP套接字
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(server_address)
- 通过socket传输图像数据,首先需要客户端将图像转换成字节,服务器接收字节数据,再将字节转换成图像并展示
# 将图像转换为字节流
_, img_encoded = cv2.imencode(".png", frame)
img_bytes = img_encoded.tobytes()
#......
# 传输过程见下文
# ......
# 将字节流转换成图像
frame_cv = cv2.imdecode(np.frombuffer(img_bytes, np.uint8), cv2.IMREAD_UNCHANGED)
- socket传输过程,需要注意不同的图像由于像素差异,字节大小会变化,因此需要明确每张图片的数据读取边界,所以通常会在发送的图片数据前加一个消息头,用于存储图片大小等信息
- 用户端
# 发送图像大小
size = len(img_bytes)
sock.sendall(size.to_bytes(4, byteorder="big"))
# 发送图像数据
sock.sendall(img_bytes)
- 服务器
# 接收图像大小
size_bytes = conn.recv(4)
size = int.from_bytes(size_bytes, byteorder="big")
# 接收图像数据
img_bytes = b""
while len(img_bytes) < size:
packet = conn.recv(size - len(img_bytes))
if not packet:
break
img_bytes += packet
本机环境
按q键停止播放
import cv2
import mss
import numpy as np
import time
# 获取屏幕尺寸
with mss.mss() as sct:
monitor = sct.monitors[0]
screen_width = monitor["width"]
screen_height = monitor["height"]
# 设置捕获区域(这里设置为整个屏幕)
monitor = {"top": 0, "left": 0, "width": screen_width, "height": screen_height}
# 设置帧速率(每秒30帧)
frame_rate = 30
with mss.mss() as sct:
while True:
begin = time.time()
# 获取屏幕截图
frame = sct.grab(monitor)
# 显示帧
cv2.namedWindow("Video", cv2.WINDOW_GUI_NORMAL)
cv2.imshow("Video", np.array(frame))
# 检测键盘输入
key = cv2.waitKey(int(1000 / frame_rate)) & 0xFF
# 如果按下 'q' 键,退出循环
if key == ord("q"):
break
end = time.time()
# 打印帧数
print(1 / (end - begin))
cv2.destroyAllWindows()
他机环境
服务端
import cv2
import numpy as np
import threading
import socket
import time
frames = []
def initSocket():
# 监听地址和端口
server_address = ("", 8080)
# 创建TCP套接字
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定到指定地址和端口
sock.bind(server_address)
# 开始监听
sock.listen(1)
print("等待连接...")
while True:
# 接受连接
conn, addr = sock.accept()
print("连接来自:", addr)
try:
while True:
# 接收图像大小
size_bytes = conn.recv(4)
size = int.from_bytes(size_bytes, byteorder="big")
# 接收图像数据
frame = b""
while len(frame) < size:
packet = conn.recv(size - len(frame))
if not packet:
break
frame += packet
# 将字节流转换成图像
frame_cv = cv2.imdecode(np.frombuffer(frame, np.uint8), cv2.IMREAD_UNCHANGED)
# 展示图像
frames.append(frame_cv)
finally:
conn.close()
def show():
global frames
# 设置最大帧速率
frame_rate = 60
while True:
if len(frames) > 0:
begin=time.time()
frame = frames.pop(0)
cv2.namedWindow("image", cv2.WINDOW_GUI_NORMAL)
cv2.imshow("image", frame)
cv2.waitKey(int(1000 / frame_rate))
end=time.time()
# 打印帧数
print(1 / (end - begin))
if __name__ == "__main__":
thread1 = threading.Thread(target=show)
thread2 = threading.Thread(target=initSocket)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
用户端
import cv2
import mss
import numpy as np
import time
import socket
# 获取屏幕尺寸
with mss.mss() as sct:
monitor = sct.monitors[0]
screen_width = monitor["width"]
screen_height = monitor["height"]
# 设置捕获区域(这里设置为整个屏幕)
monitor = {"top": 0, "left": 0, "width": screen_width, "height": screen_height}
# 远程服务器的地址和端口
server_address = ("127.0.0.1", 8080)
# 创建TCP套接字
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(server_address)
with mss.mss() as sct:
while True:
begin = time.time()
# 获取屏幕截图
frame = sct.grab(monitor)
# 转换成OpenCV对象
frame_cv = np.array(frame)
# 将图像转换为字节流
_, img_encoded = cv2.imencode(".png", frame_cv)
img_bytes = img_encoded.tobytes()
# 发送图像大小
size = len(img_bytes)
sock.sendall(size.to_bytes(4, byteorder="big"))
# 发送图像数据
sock.sendall(img_bytes)
end = time.time()
# 打印帧数
print(1 / (end - begin))
cv2.destroyAllWindows()