✅ 前情提要
家里养了三只猫咪,其中一只布偶猫经常出入厕所。但因为平时忙于学业,没法时刻关注牠的行为。我知道猫咪的如厕频率和时长与健康状况密切相关,频繁如厕可能是泌尿问题,停留过久也可能是便秘或不适。为了更科学地了解牠的如厕习惯,我计划搭建一个基于视频监控和AI识别的系统,自动识别猫咪进出厕所的行为,记录如厕时间和停留时长,并区分不同猫咪。这样即使我不在家,也能掌握猫咪的健康状态,更安心地照顾它们。
已完成工作:
✅猫咪如厕检测与分类识别系统系列【一】 功能需求分析及猫咪分类特征提取
✅猫咪如厕检测与分类识别系统系列【二】多图上传及猫咪分类特征提取更新
计划工作:
✅ 猫咪管理功能:已完成猫咪照片上传与名称登记模块。
✅ 多图上传与分类特征提取:已支持批量上传猫咪图像并自动更新个体特征库。
🔄 目标检测与事件识别集成(YOLOv11):功能开发中,正在实现猫咪行为自动识别。
⏳ 检测区域绘制功能:待开发,计划支持用户自定义如厕检测区域。
⏳ 事件行为记录模块:待完善,将实现如厕进出时间、停留时长等事件记录功能。
⏳ 检测结果推流展示:待更新,计划支持算法结果实时推流。
⏳ 整体运行结果推流整合:待更新,计划集成检测图像与系统状态为统一视频流输出。
本次将继续制作 实时检测模块 :
使用 YOLOv11 检测摄像头画面中的猫、判断是否进入指定区域,并调用分类模块识别是哪只猫 🐱📹
✅ 功能目标:
- 打开摄像头实时读取画面
- 用 YOLOv11 检测猫目标(设定类名为
'cat'
) - 判断猫是否进入你定义的"如厕区域"(矩形区域)
- 如果猫在区域内 → 裁剪猫图 → 提特征 → 分类
- 在画面中显示识别结果,并记录状态变化(进入/离开)
🧱 YOLOv11 + 分类实时检测代码(main.py
简版)
python
import cv2
import time
import numpy as np
from recognizer.embedder import CatEmbedder
from recognizer.database import CatDatabase
from recognizer.matcher import CatMatcher
from ultralytics import YOLO # 假设你用的是YOLOv8/11格式
# 初始化
model = YOLO("yolov11_cat.pt") # 替换为你的模型路径
embedder = CatEmbedder()
db = CatDatabase()
matcher = CatMatcher(db)
# 区域设定(可以做成画图交互)
TOILET_REGION = (100, 100, 400, 400) # (x1, y1, x2, y2)
# 状态跟踪
cat_present = False
entry_time = None
# 启动摄像头
cap = cv2.VideoCapture(0)
while True:
ret, frame = cap.read()
if not ret:
break
# 画如厕区域
x1, y1, x2, y2 = TOILET_REGION
cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 255), 2)
# YOLOv11 推理
results = model.predict(frame, verbose=False)
boxes = results[0].boxes
detected = False
cat_name = "Unknown"
for box in boxes:
cls = int(box.cls[0])
conf = float(box.conf[0])
if cls != 0: # 类别为猫(根据你的模型调整)
continue
xmin, ymin, xmax, ymax = map(int, box.xyxy[0])
cx, cy = (xmin + xmax) // 2, (ymin + ymax) // 2
# 判断猫是否在如厕区域
if x1 < cx < x2 and y1 < cy < y2:
detected = True
cat_crop = frame[ymin:ymax, xmin:xmax]
# 保存临时图片 + 识别猫
tmp_path = "tmp.jpg"
cv2.imwrite(tmp_path, cat_crop)
vec = embedder.extract(tmp_path)
cat_name = matcher.match(vec)
# 显示识别名
cv2.putText(frame, f"{cat_name}", (xmin, ymin - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 200, 0), 2)
cv2.rectangle(frame, (xmin, ymin), (xmax, ymax), (0, 255, 0), 2)
# 状态变化记录
if detected and not cat_present:
entry_time = time.time()
print(f"🐱 {cat_name} 进入区域")
cv2.imwrite(f"records/{cat_name}_enter_{int(entry_time)}.jpg", frame)
if not detected and cat_present:
exit_time = time.time()
duration = round(exit_time - entry_time, 2)
print(f"🚪 {cat_name} 离开区域,用时 {duration} 秒")
cv2.imwrite(f"records/{cat_name}_leave_{int(exit_time)}.jpg", frame)
cat_present = detected
cv2.imshow("Cat Monitor", frame)
if cv2.waitKey(1) == 27: # ESC 退出
break
cap.release()
cv2.destroyAllWindows()
📦 依赖库列表:
-
ultralytics
(YOLOv8/11) -
torch
,opencv-python
,Pillow
-
scikit-learn
(用于相似度计算)
✅ 下一步工作:
-
用 Flask 做个记录页面,展示:
-
每次进入/离开的猫咪、时间、如厕时长
-
保存的截图
-
-
加入状态日志系统(写入 SQLite)
✅ 读取视频流:
可以通过 OpenCV 直接读取 RTSP 流的视频画面,替换掉 cv2.VideoCapture(0)
这一行。
✅ RTSP 摄像头接入方式
假设你的摄像头地址是:
rtsp://192.168.0.102:554/stream1
常见格式有以下几种(具体看你的摄像头厂商手册):
格式类型 | 示例 URL |
---|---|
无用户名密码 | rtsp://192.168.0.102:554/stream1 |
有用户名密码 | rtsp://admin:[email protected]:554/stream1 |
如果你不确定 RTSP 路径,可以用 VLC 打开地址测试能否连接。
✅ 替换代码中的摄像头源
python
# 本地摄像头写法
# cap = cv2.VideoCapture(0)
# 替换为 RTSP 摄像头:
cap = cv2.VideoCapture("rtsp://192.168.0.102:554/stream1")
也可以设置超时时间(推荐):
python
cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
⚠️ 注意事项:
-
防火墙:确保你的电脑和摄像头在同一局域网,且摄像头开放了 RTSP 服务。
-
稳定性:RTSP 会有卡顿或断流情况,建议加重连逻辑(稍后我们可以加)。
-
帧率延迟:默认 RTSP 有延迟,可尝试减少缓冲提高实时性。
✅ 示例(完整替换一行即可)
python
cap = cv2.VideoCapture("rtsp://192.168.0.102:554/stream1")
模型使用:
现在没有收集足够多的猫咪图片,就先使用 YOLOv11 COCO 官方权重 (基于 YOLOv8 推理架构),但是只想检测 cat 类别(class id = 15)。我们有两个解决方案:
✅ 方案一:在推理结果中筛选 cat
类别
这是最简单的方法,无需重新训练模型,只在代码里过滤掉非猫类目标。
✅ 修改你的检测代码:
python
results = model.predict(frame, verbose=False)
boxes = results[0].boxes
for box in boxes:
cls = int(box.cls[0])
conf = float(box.conf[0])
if cls != 15: # COCO 中 cat 是类别15
continue
# 保留 cat 类的后续逻辑...
你也可以打印类别名验证一下:
python
from ultralytics.utils import ops
print(ops.ANCHOR_ORDER) # 也可以用 model.names[cls] 打印类别名
✅ 方案二:导出只含猫的 YOLOv11 模型(可选,后续更新)
如果你未来希望模型体积更小、推理更快:
-
用 COCO 数据集中只包含 cat 的部分训练模型(或 fine-tune)。
-
自定义 data.yaml:
yaml
names: ['cat']
nc: 1
- 训练模型:
bash
yolo detect train data=cat.yaml model=yolov11.pt epochs=50 imgsz=640
然后就能得到一个"只识别猫"的轻量化模型了。
网络摄像头检测推理错误
[h264 @ 00000218ddca2b00] error while decoding MB 94 41, bytestream -5
表示 OpenCV(底层用 ffmpeg)在解析 RTSP 摄像头的视频流时,遇到了 H.264 视频帧解码错误。这种问题在使用 RTSP 摄像头时很常见,通常是由以下原因引起的:
❗ 常见原因分析:
1. 网络不稳定 / 丢帧
-
RTSP 是实时流协议,稍有波动就可能导致数据不完整。
-
解码器(H.264)收到了不完整的数据,无法正确解码宏块(Macroblock)。
2. 摄像头编码质量太低 / 不兼容
- 某些低端摄像头输出的 RTSP H.264 数据流存在兼容问题。
3. 缓冲区设置不当(OpenCV 默认大延迟)
- OpenCV 默认有较大的缓冲,容易产生过时帧或卡顿,进而出错。
✅ 解决方案
✅ 方法 1:设置 OpenCV 缓冲区为 1(强烈推荐及采用)
python
cap = cv2.VideoCapture("rtsp://192.168.0.102:554/stream1")
cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
✅ 方法 2:改用 ffmpeg
直接拉流更稳定(通过 subprocess
或 imageio
)
如果 OpenCV 解码不稳定,可以尝试用 ffmpeg
拉流 + pipe 给 Python 处理。
✅ 方法 3:尝试切换 RTSP 子码流(sub stream)
大多数摄像头有主码流(1080p)和子码流(D1 / CIF):
-
主码流可能过大、丢帧严重
-
可尝试地址类似:
rtsp://192.168.0.102:554/sub rtsp://192.168.0.102:554/live/ch0_1 ← 子码流
✅ 方法 4:尝试加参数或禁用 HW 加速(ffmpeg flags)
可通过设置更强容错的方式读取(但这需要更底层控制)。
🧪 调试策略
python
import cv2
rtsp_url = "rtsp://192.168.0.102:554/stream1"
cap = cv2.VideoCapture(rtsp_url)
cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
if not cap.isOpened():
print("❌ 无法连接摄像头")
exit()
while True:
ret, frame = cap.read()
if not ret:
print("❌ 帧读取失败,跳过")
continue
cv2.imshow("RTSP", frame)
if cv2.waitKey(1) == 27:
break
✅ 总结建议:
方式 | 说明 |
---|---|
set(CAP_PROP_BUFFERSIZE, 1) |
✅ 首选,缓解延迟和丢帧问题 |
检查 RTSP 子码流地址 | 可大幅提升兼容性 |
尝试更稳定解码方式(如 GStreamer) | 高级稳定需求下可选 |
cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
可以先加上上述代码试试是否缓解问题。如果问题仍然频繁,我们可以换 ffmpeg
或 GStreamer
拉流方式,我也可以帮你整合好。
窗口尺寸调整:
好的,如果用 OpenCV
的 cv2.imshow()
来显示摄像头窗口,可以通过 缩放帧的尺寸 来控制窗口大小。
✅ 方法:缩放图像再显示
在你显示前加一行缩放代码,比如缩放为 640x480:
python
# 假设 frame 是读取的图像
resized_frame = cv2.resize(frame, (640, 480)) # 你也可以用 (960, 720) 或其他分辨率
cv2.imshow("Cat Monitor", resized_frame)
✅ 推荐方式(根据原图比例缩小)
如果你想"等比例缩放",比如缩小为原来的 0.5 倍:
python
scale = 0.5
h, w = frame.shape[:2]
resized_frame = cv2.resize(frame, (int(w * scale), int(h * scale)))
cv2.imshow("Cat Monitor", resized_frame)
✅ 如果你想让窗口是"可拉伸的"
还可以配合 cv2.namedWindow
控制窗口样式:
python
cv2.namedWindow("Cat Monitor", cv2.WINDOW_NORMAL)
cv2.resizeWindow("Cat Monitor", 640, 480)
这允许你手动调整窗口大小。
✅ 已完成模块回顾
模块 | 状态 | 说明 |
---|---|---|
猫咪录入 | ✅ | 支持多张猫脸图批量上传、特征提取和保存 |
猫咪识别 | ✅ | 最近邻相似度匹配分类 |
YOLOv11 + 摄像头 | ✅ | 实时检测猫是否进入区域并识别是哪只猫 |
区域判断 | ✅ | 判断猫是否进入指定如厕区域 |
RTSP 支持 | ✅ | 已支持 RTSP 摄像头接入(192.168.0.102) |
窗口缩放 | ✅ | 图像缩放显示已支持 |
✅ 运行说明
bash
cd cat_monitor/web
python app.py
-
浏览器访问:
✅ 已完成模块回顾
模块 | 状态 | 说明 |
---|---|---|
猫咪录入 | ✅ | 支持多张猫脸图批量上传、特征提取和保存 |
猫咪识别 | ✅ | 最近邻相似度匹配分类 |
YOLOv11 + 摄像头 | ✅ | 实时检测猫是否进入区域并识别是哪只猫 |
区域判断 | ✅ | 判断猫是否进入指定如厕区域 |
RTSP 支持 | ✅ | 已支持 RTSP 摄像头接入(192.168.0.102) |
窗口缩放 | ✅ | 图像缩放显示已支持 |