ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于pHash的树莓派Python监控报警系统实现

基于pHash的树莓派Python监控报警系统实现 简介这是一份基于Python与树莓派实现的寝室小监控系统毕业设计资源面向计算机相关专业软件工程、计科、人工智能、自动化、电子信息等的在校学生与开发者适合作为毕业设计、课程设计或项目立项演示的完整参考。资源共包含65个文件以33个py源码文件为核心覆盖监控逻辑、人脸识别、邮件告警、百度API调用等模块另有28个pyc编译文件便于直接运行验证1个HTML页面作为可视化展示界面1个Markdown文档提供部署说明整体压缩包仅89KB结构清晰、轻量易上手。项目代码已经过运行测试功能稳定可用且已有177人学习浏览。下载后既能直接获取整套寝室小监控系统的可运行源码也能结合部署文档快速搭建环境理解从摄像头数据采集、图像感知处理到异常告警通知的完整链路对于希望二次开发的读者更可在现有代码基础上灵活扩展移动端推送、云存储或更多传感器联动功能是实战练手与毕设参考的高性价比资源。1. 树莓派Python 寝室小监控用 pHash 做变化检测而不是一直录视频在寝室这个断电断网、空间局促的场景里与其让摄像头 7x24 小时录视频不如让它每隔几秒拍一张照片用感知哈希pHash算出特征值只有前后两帧汉明距离超过阈值时才触发下一步动作。这个毕业设计项目 Raspberry-pie-monitoring-master 就是这种思路源码不大但把树莓派摄像头、Python 图像处理、百度 AI 人体过滤、SMTP 邮件通知完整串了起来。对于软件工程、自动化、电子信息等专业的学生它最大的价值不是摄像头本身而是一条可以现场演示的完整告警链路并且支持在不改动主逻辑的情况下替换掉某个环节。2. 源码拆解monitor.py、Pi.py、phash.py 在监控链路里的职责拿到源码包后先别急着跑按 README.md 和文件命名把这几个模块的边界看清楚后面调参会快很多。项目根目录下看到的.py文件都不是随便放的它们基本对应了监控链路里的“采集、比对、过滤、通知”四个阶段。2.1 根目录文件与职责对比文件/目录职责关键依赖monitor.py主调度循环定时触发拍照与比对Pi.py、phash.py、notice.pyPi.py封装摄像头拍照、GPIO 初始化picamera2 / OpenCVphash.py生成感知哈希、计算汉明距离OpenCV、NumPybaidu.py api/调用百度 AI 人体检测过滤无人误报requestsnotice.py mail.html发送带图片附件的告警邮件smtplib、Jinja2/string.Template从表里能看到这套设计把硬件依赖和业务逻辑做了隔离。Pi.py 负责和树莓派硬件打交道phash.py 只做纯图像计算baidu.py 处理远端接口notice.py 只管发信。这样在本地开发机上也能先用普通摄像头把整条流程跑通再放到树莓派上替换 Pi.py 的摄像头实现。2.2 monitor.py主循环的调度逻辑monitor.py 的作用不是实现算法而是把上面几个模块按时间顺序串联起来。常见做法是启动后先拍一帧作为基准然后每隔 N 秒拍一帧计算当前帧与上一帧的 pHash 汉明距离超过阈值就把当前图像交给 baidu.py 判断画面里有没有人确认后再调用 notice.py 发邮件。这个循环最值得参考的是“异步”和“防抖”的思路。如果拍照和比对都在一个线程里做完当网络请求或邮件发送阻塞时后续帧就会堆积。毕业设计里只要能说明白这一点并把 check_interval 做成可配置项在答辩时就是加分点。2.3 Pi.py把摄像头操作隔离成可替换层Pi.py 通常暴露一个capture()函数内部针对树莓派平台调用摄像头驱动返回当前帧写入的临时图片路径。隔离层设计的好处是当需要把监控从树莓派迁到普通 Linux 主机时只需要改 Pi.py 内部的实现比如从cv2.VideoCapture(0)读取而 phash.py、notice.py 一行都不改。我一般会在 Pi.py 里增加一个get_camera_info()方法把摄像头编号、分辨率、曝光模式打印到控制台排错时非常有用。这能提前暴露“摄像头没插稳”或“权限不足”这一类低级问题。2.4 phash.py感知哈希的核心原理感知哈希处理的不是像素本身而是像素的“频域指纹”。它先把图像缩小到 32x32转为灰度做离散余弦变换DCT再截取左上角 8x8 的低频系数。低频信息代表人眼最能感知的亮度分布对这 8x8 个系数分别与均值比较大于均值记为 1否则为 0拼成一个 64 位整数。两张图的差异就用汉明距离度量也就是二进制位中不同位的个数。和帧差法、背景建模相比pHash 对轻微的光照变化和摄像头白平衡波动不敏感非常适合寝室里灯光忽明忽暗的环境。但它对画面旋转、裁剪比较敏感所以摄像头安装后不要随意挪动。2.5 baidu.py用百度 AI 过滤无人误报如果只靠 pHash窗帘飘动、晚上灯被打开、飞虫飞过都会带来远超阈值的距离。baidu.py 的作用就是在判定为“画面变化”后再请求一次百度 AI 的人体检测接口返回人体置信度。代码里有一个filter(img_path)函数只有置信度高于设定值才允许后续发邮件。注意这里不要做成同步阻塞两次请求更稳妥的是在 baidu.py 里配置超时时间比如 5 秒无响应就跳过告警避免某个网络抖动把整条监控链路卡死。2.6 notice.py 与 mail.html告警通知模板分离notice.py 负责拼接告警邮件mail.html 是邮件正文模板。模板里一般会放图片占位符、时间、房间号三个变量。这样业务代码里就不需要写死 HTML 字符串改通知样式时只动 mail.html。对于答辩来说这种模板分离的做法非常容易讲清楚代码结构也更干净。3. 环境部署在树莓派 4B 上把监控系统跑起来的完整步骤这个资源在“部署文档”里强调过需要先初始化系统。这里给出我在树莓派 4B Raspberry Pi OS Bookworm 上的常见部署流程重点放在摄像头驱动、OpenCV 和百度 AI 密钥三个容易出问题的位置。3.1 启用树莓派摄像头模块拿到树莓派后先把系统烧进 SD 卡并完成基础配置。在 Raspberry Pi OS 里打开终端执行sudo raspi-config在 Interface Options 里选择 Camera 并启用重启后检查摄像头设备是否被系统识别。新版系统推荐直接用libcamera-hello验证libcamera-hello --list-cameras如果能看到摄像头传感器型号说明驱动已经加载。在这个阶段不要急着装 OpenCV先确认硬件链路是通的否则后面报错会分不清是代码问题还是系统问题。注意raspi-config只对官方摄像头有效。如果手边是 USB 摄像头则不需要启用 CSI 接口直接在 Python 里用cv2.VideoCapture(0)读取即可。项目里的 Pi.py 是否需要改动取决于摄像头类型这也是把硬件访问封装起来的价值所在。3.2 安装 Python 依赖与虚拟环境项目基于 Python 3建议先建虚拟环境再装依赖避免干扰系统自带的 Pythonsudo apt update sudo apt install -y python3-venv python3-opencv python3 -m venv .venv source .venv/bin/activate pip install --upgrade pip pip install requests numpy opencv-python-headless上面的命令里python3-opencv是树莓派官方仓库预编译的版本比直接用 pip 编译速度更快opencv-python-headless用于在虚拟环境里提供 cv2 接口二者可以共存但要注意环境变量的加载顺序。实际使用时我一般会先启动虚拟环境再python -c import cv2验证一次。如果源码里有requirements.txt直接pip install -r requirements.txt。这里没有把它写成唯一方式是因为部分树莓派镜像会缺少编译依赖用 apt 安装更稳妥。3.3 配置百度 AI 密钥baidu.py 里通常用一个配置文件或环境变量读取 API Key 和 Secret Key。在项目根目录创建一个.env文件内容类似BAIDU_APP_ID你的AppID BAIDU_API_KEY你的APIKey BAIDU_SECRET_KEY你的SecretKey SMTP_HOSTsmtp.qq.com SMTP_PORT465 SMTP_USER你的邮箱 SMTP_PASS邮箱授权码密钥获取方法在部署文档里有截图流程上是登录百度智能云控制台创建应用选择人体识别相关的产品权限拿到三个字符串。注意这里用的是“人体检测”而不是“人脸检测”因为监控画面里经常只有背面或者半身人脸的召回率太低。代码里读取环境变量时最好先判断变量是否存在缺少密钥就快速失败而不是请求后才报错。3.4 验证摄像头能拍到第一张照片环境配置完成后先单独测试 Pi.py 的拍照逻辑python Pi.py如果 Pi.py 有if __name__ __main__:入口会直接拍一张临时照片并打印保存路径。这一步跑通了再执行python monitor.py开始监控。我见过的常见失败有两种一是libcamera被占用报 Device or resource busy通常是有其他进程打开了摄像头二是虚拟环境里的 OpenCV 与系统版本的libcamera头文件不匹配这时把依赖装进全局环境反而更省事。总之摄像头能出图后面的 pHash 和通知才有意义。4. 核心代码走读pHash 图像对比与邮件通知的串联方式部署完成后需要把代码真正读进去。这一章会拆 phash.py 和 notice.py 的关键实现再回到 monitor.py 看它们怎么被串联。读代码时不需要理解每一个像素的操作能讲清输入输出和阈值参数就够了。4.1 phash.py 的图像指纹算法phash.py 用 OpenCV 实现感知哈希。下面这段代码与常见实现一致是我基于项目目的整理出的可运行版本import cv2 import numpy as np def calc_phash(img_path: str) - int: img cv2.imread(img_path, cv2.IMREAD_GRAYSCALE) img cv2.resize(img, (32, 32), interpolationcv2.INTER_AREA) img img.astype(np.float32) / 255.0 dct cv2.dct(img)[:8, :8] med dct.mean() bits (dct med).flatten() phash 0 for bit in bits: phash (phash 1) | (1 if bit else 0) return phash def hamming(hash1: int, hash2: int) - int: return bin(hash1 ^ hash2).count(1)这个实现里resize((32, 32))把图像缩得很小目的是去掉高频细节只保留整体构图cv2.dct是二维离散余弦变换取前 8 行 8 列低频系数。注意med用的是所有系数的均值而不是固定阈值所以照片整体偏亮或偏暗时哈希结果不会被全局亮度带偏。调用时只需要phash.calc_phash(frame.jpg)获得整数指纹phash.hamming(h1, h2)获得汉明距离。距离超过阈值说明画面发生了可感知变化。4.2 monitor.py 中如何组织循环monitor.py 的主流程通常类似下面的伪代码但它很好展示了模块间的关系import time from Pi import capture from phash import calc_phash, hamming from baidu import human_confirm import notice INTERVAL_SECONDS 10 CHANGE_THRESHOLD 15 def main(): previous_hash None image_path capture() previous_hash calc_phash(image_path) while True: current_path capture() current_hash calc_phash(current_path) if previous_hash is not None and hamming(previous_hash, current_hash) CHANGE_THRESHOLD: if human_confirm(current_path): notice.send_alert(current_path) previous_hash current_hash time.sleep(INTERVAL_SECONDS) if __name__ __main__: main()这个模式里有两个易错点。第一个是previous_hash不能一直是初始帧否则时间一长环境变化累计出来误报会非常多每次循环结束前必须把当前帧赋值给上一个帧保证比较的是相邻两帧。第二个是human_confirm的返回结果不能影响previous_hash的更新否则在“没有人”的情况下下一帧会和一个旧基准比对结果越来越离谱。4.3 notice.py 与 mail.html 的模板拼接notice.py 发送邮件时不需要把 HTML 写死而是读取 mail.html 后用字符串替换填入变量。下面是一个基于标准库的实现思路import smtplib from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.mime.image import MIMEImage def send_alert(img_path: str) - None: with open(mail.html, r, encodingutf-8) as f: html f.read() html html.replace({{TIME}}, time.strftime(%Y-%m-%d %H:%M:%S)) html html.replace({{ROOM}}, A-302) msg MIMEMultipart(related) msg[Subject] 寝室监控异常告警 msg.attach(MIMEText(html, html, utf-8)) with open(img_path, rb) as fp: image MIMEImage(fp.read()) image.add_header(Content-ID, snapshot) msg.attach(image) with smtplib.SMTP_SSL(SMTP_HOST, SMTP_PORT) as server: server.login(SMTP_USER, SMTP_PASS) server.send_message(msg)这里Content-ID的写法很关键mail.html 里的img srccid:snapshot会引用这封邮件内嵌的图片而不是外链图片。很多邮件客户端默认屏蔽外部图片内嵌是为了在无网环境下也能看到监控截图。SMTP_SSL连接方式对应 465 端口如果使用 QQ 邮箱SMTP_PASS 填的是授权码而不是登录密码这个细节在部署文档里会特别标注。4.4 参数说明阈值改成多少合适监控效果好不好很大程度取决于两个参数INTERVAL_SECONDS和CHANGE_THRESHOLD。参数取值范围效果说明INTERVAL_SECONDS5-30越短越敏感但会加快 SD 卡写入和百度 API 调用频率CHANGE_THRESHOLD10-20汉明距离超过该值才认为有变化值越小越容易误报人体置信度0.6-0.9baidu.py 里过滤用的概率门槛越高越严格这四个模块读下来整个项目的运行链路就清楚了Pi.py 抓帧phash.py 算差异baidu.py 过滤notice.py 告警。每一步只做一件小事替换任何一个环节都不会影响其他模块。5. 调参与验证把误报率降下来的几个具体做法代码跑通只是第一步实际放在寝室里误报才是主要矛盾。这里分享几个不依赖训练模型也能有效降误报的做法。5.1 用一份真实日志倒推阈值先跑一个晚上在 monitor.py 里加上调试输出记录每帧的汉明距离和是否告警再用表格统计。我一般会把这组数据写到 CSV用脚本看距离分布如果大部分安静时段的距离都小于 8而有人的距离通常大于 20那么把阈值设在 12~15 就很安全。如果两种场景重叠说明摄像头附近有闪烁光源优先调整摄像头位置而不是一味调阈值。5.2 配合 crontab 做定时启停晚上睡觉时才需要监控白天可以让摄像头歇着。用系统自带的 crontab 控制启动时间0 22 * * * cd /home/pi/raspberry-pie-monitoring /home/pi/.venv/bin/python monitor.py monitor.log 21 注意分号前不要加nohup否则后台进程会因缺少环境变量而失效。同时给 SD 卡留出足够空间建议把临时照片写到/tmp而不是 USB 外设上因为树莓派意外断电时文件系统更容易损坏。5.3 细调 baidu.py 的请求超时与重试百度 AI 接口并不是每次都能在 1 秒内返回给请求设置 timeout 和重试次数能避免告警链路卡死。比如只重试一次超时时间设为 5 秒。如果寝室网络不稳定与其强行增加重试不如让监控放弃这次告警下一轮继续检测这样稳定性更好。功率限制也要留意如果摄像头带红外补光瞬时电流可能超过树莓派 USB 口的默认限制出现反复掉画面时在config.txt里打开电流上限即可。这个操作在部署文档的“注意事项”一节有强调用来排查时非常有效。把 timeout 参数写进 baidu.py 的请求调用里同时把重试次数降为 1必要时直接放弃本轮告警。如果后续要扩展把 notice.py 里的 send_alert 换成企业微信机器人也只需要改一个函数。这样这个小监控就可以长期无人值守。本文还有配套的精品资源点击获取
返回列表