
树莓派监控画面平铺模式实现
引言
树莓派作为一个功能强大的小型计算机,在监控系统中有着广泛的应用。当我们需要同时监控多个区域时,将单个摄像头画面进行平铺显示是一种有效的方法。本文将详细介绍如何在树莓派上使用Python和Tkinter库实现监控画面的平铺模式显示。
硬件和软件准备
硬件需求:
树莓派(推荐Raspberry Pi 4)
摄像头模块(如Raspberry Pi Camera Module)
HDMI显示器或通过VNC连接的远程显示器
软件环境:
Raspberry Pi OS
Python 3.x
OpenCV库(pip install opencv-python)
Pillow库(pip install pillow)
NumPy库(pip install numpy)
技术原理
图像平铺模式是指将单个摄像头捕获的画面复制并排列成网格状,形成更大的显示区域。这种显示方式可以在有限的空间内展示更多的监控区域,或者创建一种特殊的视觉效果。
实现平铺模式的关键步骤:
从摄像头捕获实时视频流
对每帧图像进行处理和缩放
在GUI界面中创建网格布局
将处理后的图像块填充到网格中
实现实时更新
代码实现
下面是一个完整的树莓派监控画面平铺显示的Python实现:
import tkinter as tk
from tkinter import ttk
import cv2
import PIL.Image, PIL.ImageTk
import numpy as np
import time
import threading
class RaspiSurveillanceApp:
def init(self, window, window_title):
self.window = window
self.window.title(window_title)
# 视频源,默认为0(通常是内置摄像头)
self.video_source = 0
self.vid = None
# 平铺配置
self.tile_rows = 2 # 行数
self.tile_cols = 2 # 列数
self.tile_gap_x = 10 # 水平间隙(像素)
self.tile_gap_y = 10 # 垂直间隙(像素)
# 创建画布用于显示平铺视频
self.canvas_width = 800
self.canvas_height = 600
self.canvas = tk.Canvas(window, width=self.canvas_width, height=self.canvas_height)
self.canvas.grid(row=0, column=0, padx=10, pady=10)
# 控制按钮框架
self.controls_frame = ttk.Frame(window)
self.controls_frame.grid(row=1, column=0, padx=10, pady=10, sticky="ew")
# 添加控制按钮
self.btn_pause = ttk.Button(self.controls_frame, text="暂停", command=self.toggle_pause)
self.btn_pause.pack(side=tk.LEFT, padx=5)
self.btn_tile_config = ttk.Button(self.controls_frame, text="平铺配置", command=self.open_tile_config)
self.btn_tile_config.pack(side=tk.LEFT, padx=5)
self.is_paused = False
self.defocus_color = "#222222" # 非焦点区域的颜色
# 打开视频源
self.open_video_source()
# 设置窗口关闭事件
self.window.protocol("WM_DELETE_WINDOW", self.on_closing)
# 设置定时更新UI
self.window.after(10, self.update)
def open_video_source(self):
"""打开视频源"""
try:
# 释放之前的视频源(如果有)
if self.vid is not None and self.vid.isOpened():
self.vid.release()
# 打开新的视频源
self.vid = cv2.VideoCapture(self.video_source)
if not self.vid.isOpened():
raise ValueError("无法打开视频源", self.video_source)
# 获取视频源的宽度和高度
self.width = self.vid.get(cv2.CAP_PROP_FRAME_WIDTH)
self.height = self.vid.get(cv2.CAP_PROP_FRAME_HEIGHT)
print(f"视频源分辨率: {self.width}x{self.height}")
except Exception as e:
print(f"打开视频源错误: {e}")
self.window.destroy()
def toggle_pause(self):
"""切换暂停状态"""
self.is_paused = not self.is_paused
status = "暂停" if self.is_paused else "继续"
print(f"{status}监控画面")
def open_tile_config(self):
"""打开平铺配置对话框"""
config_window = tk.Toplevel(self.window)
config_window.title("平铺配置")
config_window.geometry("300x200")
# 行数设置
ttk.Label(config_window, text="行数:").grid(row=0, column=0, padx=5, pady=5, sticky="w")
row_var = tk.IntVar(value=self.tile_rows)
row_spinbox = ttk.Spinbox(config_window, from_=1, to=4, textvariable=row_var, width=5)
row_spinbox.grid(row=0, column=1, padx=5, pady=5)
# 列数设置
ttk.Label(config_window, text="列数:").grid(row=1, column=0, padx=5, pady=5, sticky="w")
col_var = tk.IntVar(value=self.tile_cols)
col_spinbox = ttk.Spinbox(config_window, from_=1, to=4, textvariable=col_var, width=5)
col_spinbox.grid(row=1, column=1, padx=5, pady=5)
# 间隙设置
ttk.Label(config_window, text="间隙大小:").grid(row=2, column=0, padx=5, pady=5, sticky="w")
gap_var = tk.IntVar(value=10)
gap_spinbox = ttk.Spinbox(config_window, from_=0, to=50, textvariable=gap_var, width=5)
gap_spinbox.grid(row=2, column=1, padx=5, pady=5)
# 保存配置按钮
def save_config():
self.tile_rows = row_var.get()
self.tile_cols = col_var.get()
self.tile_gap_x = gap_var.get()
self.tile_gap_y = gap_var.get()
print(f"平铺配置已更新: {self.tile_rows}行 x {self.tile_cols}列, 间隙: {self.tile_gap_x}px")
config_window.destroy()
save_btn = ttk.Button(config_window, text="保存", command=save_config)
save_btn.grid(row=3, column=0, columnspan=2, pady=10)
def video_loop(self):
"""从视频源获取帧并更新画布"""
if not self.is_paused:
ret, frame = self.vid.read()
if ret:
# 处理帧并更新UI
self.photo = self.process_frame(frame)
self.update_canvas()
# 每10毫秒循环一次
self.window.after(10, self.video_loop)
def process_frame(self, frame):
"""处理视频帧,根据平铺配置生成适当的图像"""
# 调整单个区块的大小
tile_width = (self.canvas_width - (self.tile_cols - 1) * self.tile_gap_x) // self.tile_cols
tile_height = (self.canvas_height - (self.tile_rows - 1) * self.tile_gap_y) // self.tile_rows
# 调整原始图像大小,使其高度与单个区块高度相匹配
frame_h, frame_w = frame.shape[:2]
aspect_ratio = frame_w / frame_h
# 计算合适的宽度,保持宽高比
resized_width = int(tile_height * aspect_ratio)
resized_frame = cv2.resize(frame, (resized_width, tile_height))
# 创建一个空白画布,用于放置平铺的图像
tiled_image = np.ones((tile_height self.tile_rows + self.tile_gap_y (self.tile_rows - 1),
tile_width self.tile_cols + self.tile_gap_x (self.tile_cols - 1),
3), dtype=np.uint8) * 200 # 200是灰色
# 将原始图像复制到每个区块位置
for i in range(self.tile_rows):
for j in range(self.tile_cols):
# 计算当前区块的位置
x_offset = j * (tile_width + self.tile_gap_x)
y_offset = i * (tile_height + self.tile_gap_y)
# 将调整后的图像放置在对应位置
h, w = resized_frame.shape[:2]
tiled_image[y_offset:y_offset+h, x_offset:x_offset+w] = resized_frame
# 添加边框和编号
cv2.rectangle(tiled_image,
(x_offset, y_offset),
(x_offset+w, y_offset+h),
(0, 0, 255), 2)
cv2.putText(tiled_image, f"Cam{i*4+j+1}",
(x_offset+10, y_offset+30),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
# 转换为PIL格式以便在Tkinter中使用
return PIL.Image.fromarray(cv2.cvtColor(tiled_image, cv2.COLOR_BGR2RGB))
def update_canvas(self):
"""更新画布上的图像"""
if self.photo:
# 调整图像以适应画布大小
self.photo = self.photo.resize((self.canvas_width, self.canvas_height), PIL.Image.LANCZOS)
# 将PIL图像转换为Tkinter PhotoImage
self.tk_photo = PIL.ImageTk.PhotoImage(self.photo)
# 在画布上显示图像
self.canvas.create_image(0, 0, image=self.tk_photo, anchor=tk.NW)
def on_closing(self):
"""关闭应用程序"""
# 停止视频线程
if self.vid and self.vid.isOpened():
self.vid.release()
# 关闭窗口
self.window.destroy()
创建并运行应用
if name == “main”:
root = tk.Tk()
app = RaspiSurveillanceApp(root, “树莓派监控画面平铺模式”)
root.mainloop()
功能说明
这个应用程序提供以下功能:
实时监控显示:从摄像头捕获实时视频流
平铺模式:将单个视频源平铺到多个区块中显示
可配置布局:可以调整平铺的行数、列数和间隙大小
暂停/继续:随时暂停或继续监控画面的显示
区块标识:每个平铺区块都有编号和边框标识
运行效果
运行程序后,将显示一个窗口,其中包含平铺显示的监控画面。默认情况下,使用2×2的平铺布局,您可以通过"平铺配置"按钮调整布局参数。点击"暂停"按钮可以暂停画面更新,便于观察某一时刻的场景。
扩展功能
除了基本的平铺显示外,还可以扩展以下功能:
多摄像头支持:修改代码以支持多个摄像头源,每个区块显示不同的摄像头画面
动态布局调整:添加鼠标交互功能,允许用户拖拽调整区块大小和位置
运动检测:在每个区块中添加运动检测功能,当检测到运动时高亮显示
报警功能:当特定区域发生异常时,触发报警并记录视频
性能优化
在树莓派上运行视频处理应用时,性能是一个需要考虑的因素。以下是一些优化建议:
降低分辨率:适当降低摄像头捕获的分辨率可以提高处理速度
减少平铺数量:过多的平铺区块会增加CPU负载,根据树莓派的性能选择合适的平铺数量
跳过帧处理:可以设置每隔几帧处理一次,而不是处理每一帧
使用硬件加速:利用树莓派的GPU进行图像处理,提高处理效率
结论
本文介绍了如何在树莓派上使用Python和Tkinter实现监控画面的平铺显示功能。通过这种平铺模式,我们可以在有限的显示区域内同时查看多个监控点的情况,这对于监控系统来说非常实用。代码已经实现了基本的功能,并提供了可配置的选项,您可以根据自己的需求进一步扩展和优化。
