python图形差分传输实现windows桌面监视
图像的差分传输(Differential Transmission of Images)是指通过传输图像的差分信息而不是原始图像数据来实现更高效的数据传输。这种方法在图像压缩和传输中非常有用,因为它可以显著减少需要传输的数据量,从而提高传输效率和速度。
以下是图像差分传输的基本原理和步骤:
- 当前图像和参考图像:在差分传输中,通常会有一个参考图像(可能是之前传输过的图像或接收方已经拥有的图像)和一个需要传输的当前图像。参考图像和当前图像之间可能存在一定的差异。
- 计算差分图像:差分传输的核心是计算当前图像和参考图像之间的差异,即差分图像。差分图像的每个像素值表示当前图像和参考图像对应像素值的差异。例如,如果当前图像的像素值为A,参考图像的像素值为B,那么差分图像的像素值就是A - B。
- 传输差分数据:将压缩后的差分图像数据通过网络传输到接收端。由于差分图像的数据量较小,这一步通常比传输完整的原始图像更高效。
- 重建图像:接收端接收到差分图像后,会利用参考图像和差分图像重建出当前图像。重建过程是将参考图像和差分图像进行逆操作,即对每个像素值进行相加(如果差分图像的像素值为A - B,那么重建时的操作是B + (A - B) = A)。
远程桌面监控效果展示
当前图像和参考图像
在图形差分传输下,参考图像通常是上一帧图,当前图像是当前帧图,以下给出两张图用作案例演示:
![]() |
![]() |
---|---|
上一帧 | 当前帧 |
计算差分图像
使用Python计算差分图像可以通过一些图像处理库来实现,例如OpenCV或Pillow。以下是使用OpenCV实现差分图像计算的一个简单示例,将上述两个图像进行比较,并标记差异区域:
- 注意
- 当图像文件路径存在中文时,使用
cv2.imread
和cv2.imwrite
读取和保存图像时会报错,需要使用cv2.imdecode
和cv2.imencode
替换 - OpenCV进行差异比较时需要先将图像转换成灰度图像
- 通过
cv2.threshold
修改差异阈值可以提高差异识别灵敏度,以找到更细微的差异,或者降低灵敏度,以提高执行效率 - 使用pyautogui获取当前屏幕分辨率,根据比例使用
cv2.resize
调整展示的图像尺寸
- 当图像文件路径存在中文时,使用
import cv2
import numpy as np
import pyautogui
with
open(
r"D:/图像传输/prev.png", "rb"
) as prev,
open(
r"D:/图像传输/current.png", "rb"
) as current:
# 加载上一帧图
previous_frame = cv2.imdecode(
np.frombuffer(prev.read(), np.uint8), cv2.IMREAD_GRAYSCALE
)
# 加载当前帧图
current_frame = cv2.imdecode(
np.frombuffer(current.read(), np.uint8), cv2.IMREAD_GRAYSCALE
)
# 计算两帧之间的差异
diff = cv2.absdiff(current_frame, previous_frame)
# 设置差异阈值
_, thresholded_diff = cv2.threshold(diff, 30, 255, cv2.THRESH_BINARY)
# 找到变化区域的轮廓
contours, _ = cv2.findContours(
thresholded_diff, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE
)
# 在当前帧上标记变化区域
marked_image = cv2.cvtColor(current_frame, cv2.COLOR_GRAY2BGR)
for contour in contours:
x, y, w, h = cv2.boundingRect(contour)
cv2.rectangle(marked_image, (x, y), (x + w, y + h), (0, 255, 0), 2)
# 保存结果
cv2.imencode(".png", marked_image)[1].tofile(
r"D:/图像传输/comp.png"
)
# 显示结果
screen_width, screen_height = pyautogui.size()
scale = screen_height / marked_image.shape[0] - 0.1
marked_image = cv2.resize(marked_image, None, fx=scale, fy=scale)
cv2.imshow("Marked Image", marked_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
- 结果图
传输差分数据
由上节可知,差分数据存储在contours
中,但是我们不需要将这个对象的全部数据传输过去,我们只需要传输差异图像、图像尺寸以及图像的坐标信息就可以支持后续的重建。
- 本节需要注意的点:
- 要传输原图数据,而不是差分计算用到的灰度图像。因此需要使用差分数据对原图进行切片,代码片段如下:
for contour in contours: x, y, w, h = cv2.boundingRect(contour) # 原图切片 slice = original[y : y + h, x : x + w]
- 当存在多个差异点时,将差异数据合并传输,可以提高传输效率。但是合并传输需要规定数据包的结构和格式,本例使用以下规定:
包结构:{<信息头><图像描述><图像内容>} 信息头:{<身份标识><切片数量>} 身份标识:16字节 切片数量:4字节,0则表示替换整个图像,>=0则差分更新上一帧 图像描述:{<切片信息><切片信息>...} 切片信息:{<切片大小><切片高度><切片宽度><X轴坐标><Y轴坐标>} 切片大小:4字节,int 切片高度:4字节,int 切片宽度:4字节,int X轴坐标:4字节,int Y轴坐标:4字节,int 图像内容:切片有序排列组合,大小=所有切片大小之和
- 参考上述规定,差分数据的发送代码段如下:
# 发送变化区域图像数据 body = b"" header = b"" desc = b"" # 添加切片数量 header = id.bytes + len(contours).to_bytes(4, byteorder="big") for contour in contours: x, y, w, h = cv2.boundingRect(contour) # 原图切片 slice = mat[y : y + h, x : x + w] # 将切片图像转换为字节流 _, slice_encoded = cv2.imencode(".png", slice) slice_bytes = slice_encoded.tobytes() # 图像大小、尺寸、位置信息 size = len(slice_bytes) slice_info = ( size.to_bytes(4, byteorder="big") + h.to_bytes(