猫咪如厕检测与分类识别系统系列【四】融合检测日志输出及前端展示界面制作
✅ 前情提要
家里养了三只猫咪,其中一只布偶猫经常出入厕所。但因为平时忙于学业,没法时刻关注牠的行为。我知道猫咪的如厕频率和时长与健康状况密切相关,频繁如厕可能是泌尿问题,停留过久也可能是便秘或不适。为了更科学地了解牠的如厕习惯,我计划搭建一个基于视频监控和AI识别的系统,自动识别猫咪进出厕所的行为,记录如厕时间和停留时长,并区分不同猫咪。这样即使我不在家,也能掌握猫咪的健康状态,更安心地照顾它们。
已完成工作:
✅猫咪如厕检测与分类识别系统系列【一】 功能需求分析及猫咪分类特征提取
✅猫咪如厕检测与分类识别系统系列【二】多图上传及猫咪分类特征提取更新
✅猫咪如厕检测与分类识别系统系列【三】 融合yolov11目标检测
计划工作:
✅ 猫咪管理功能:已完成猫咪照片上传与名称登记模块。
✅ 多图上传与分类特征提取:已支持批量上传猫咪图像并自动更新个体特征库。
🔄 目标检测与事件识别集成(YOLOv11):功能开发中,已实现猫咪行为自动识别,正在集成至页面。
⏳ 检测区域绘制功能:待开发,计划支持用户自定义如厕检测区域。
⏳ 事件行为记录模块:待完善,将实现如厕进出时间、停留时长等事件记录功能。
⏳ 检测结果推流展示:待更新,计划支持算法结果实时推流。
⏳ 整体运行结果推流整合:待更新,计划集成检测图像与系统状态为统一视频流输出。
本次将继续制作 实时检测模块中的Flask 页面展示记录:🔜 现在我们要继续完成的:
✅ 1. 如厕事件记录系统:
-
记录猫进入、
-
显示:
-
猫咪名字
-
进入时间
-
离开时间
-
如厕时长
-
图片预览
-
-
可按时间筛选、导出记录
✅ 第一步:添加如厕记录系统(记录 + 存图)
🔧 toilet_logger.py
(事件记录模块)
import os
import csv
from datetime import datetime
class ToiletLogger:
def __init__(self, save_dir="records", csv_path="records/toilet_log.csv"):
self.save_dir = save_dir
self.csv_path = csv_path
os.makedirs(save_dir, exist_ok=True)
# 创建CSV头
if not os.path.exists(self.csv_path):
with open(self.csv_path, 'w', newline='') as f:
writer = csv.writer(f)
writer.writerow(["Name", "Enter Time", "Exit Time", "Duration(s)", "Enter Image", "Exit Image"])
def log(self, name, entry_time, exit_time, enter_img, exit_img):
duration = round(exit_time - entry_time, 2)
enter_time_str = datetime.fromtimestamp(entry_time).strftime("%Y-%m-%d %H:%M:%S")
exit_time_str = datetime.fromtimestamp(exit_time).strftime("%Y-%m-%d %H:%M:%S")
enter_path = os.path.join(self.save_dir, enter_img)
exit_path = os.path.join(self.save_dir, exit_img)
with open(self.csv_path, 'a', newline='') as f:
writer = csv.writer(f)
writer.writerow([name, enter_time_str, exit_time_str, duration, enter_path, exit_path])
✅ 第二步:在 main.py
检测逻辑中调用记录器
加入:
from toilet_logger import ToiletLogger
logger = ToiletLogger()
然后在状态变化处加上记录逻辑:
# 进入区域
if detected and not cat_present:
entry_time = time.time()
cat_enter_img = f"{cat_name}_enter_{int(entry_time)}.jpg"
cv2.imwrite(f"records/{cat_enter_img}", frame)
print(f"🐱 {cat_name} 进入区域")
# 离开区域
if not detected and cat_present:
exit_time = time.time()
cat_exit_img = f"{cat_name}_exit_{int(exit_time)}.jpg"
cv2.imwrite(f"records/{cat_exit_img}", frame)
duration = round(exit_time - entry_time, 2)
logger.log(cat_name, entry_time, exit_time, cat_enter_img, cat_exit_img)
print(f"🚪 {cat_name} 离开区域,用时 {duration} 秒")
✅ 第三步:Flask 页面展示记录
页面功能:
-
展示如厕记录表格
-
显示进入/离开时间、图片、猫名
你可以先测试下这部分如厕记录逻辑是否正常工作:
🐾 猫进入 → 保存图片 → 离开 → 记录进 CSV → records/
下有图像和日志。
目录结构:
已分析
# 项目结构
dirs = [
"cat_monitor/recognizer",
"cat_monitor/web/templates",
"cat_monitor/web/static/uploads",
"cat_monitor/data/embeddings",
"cat_monitor/records"
]
#main.py
import cv2
import time
import numpy as np
from recognizer.embedder import CatEmbedder
from recognizer.database import CatDatabase
from recognizer.matcher import CatMatcher
from toilet\_logger import ToiletLogger
from ultralytics import YOLO
# 初始化模型与识别器
model = YOLO("yolov11.pt") \# 替换为你的模型路径
embedder = CatEmbedder()
db = CatDatabase()
matcher = CatMatcher(db)
logger = ToiletLogger()
# RTSP 摄像头地址
rtsp_url = "rtsp://192.168.0.102:554/stream1"
cap = cv2.VideoCapture(rtsp_url)
cap.set(cv2.CA\_PROP_BUFFERSIZE, 1)
# 区域设定(如厕区域)
TOILET_REGION = (100, 100, 400, 400)
# 状态追踪
cat_present= False
entry_time = None
cat_name = "Unknown"
cv2.namedWindow("Cat Monitor", cv2.WINDOW\_NORMAL)
cv2.resizeWindow("Cat Monitor", 960, 720)
while True:
ret, frame = cap.read()
if not ret:
✅ 主程序 main.py
已写入,支持:
-
RTSP 视频流读取
-
YOLOv11 检测 + 区域判断
-
猫咪识别(调用分类模块)
-
如厕进入/离开状态跟踪
-
自动保存图像并写入记录日志
接下来我会继续添加:
-
toilet_logger.py
(如厕记录模块) -
分类模块:
embedder.py
,database.py
,matcher.py
-
Flask 前端页面(用于展示如厕记录)
如厕记录前端界面:
# app.py (Flask 前端展示记录)
from flask import Flask, render\_template
import csv
app = Flask(__name__)
CSV_FILE = "records/toilet_log.csv"
@app.route('/')
def index():
records = []
try:
with open(CSV_FILE, newline='') as f:
reader = csv.DictReader(f)
for row in reader:
records.append(row)
except FileNotFoundError:
pass
return render_template('index.html', records=records)
if __name__ == '__main__':
app.run(debug=True)
✅ Flask 后端展示代码已完成(app.py
)。
Flask 后端页面代码:
html_code = """
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>猫咪如厕记录</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
table { width: 100%; border-collapse: collapse; margin-top: 20px; }
th, td { border: 1px solid #ccc; padding: 8px; text-align: center; }
th { background-color: #f2f2f2; }
input[type="text"] { padding: 6px; width: 200px; margin-right: 10px; }
.thumbnail { height: 80px; cursor: pointer; }
.modal { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%;
background-color: rgba(0, 0, 0, 0.7); align-items: center; justify-content: center; }
.modal img { max-width: 80%; max-height: 80%; }
</style>
</head>
<body>
<h2>🐱 猫咪如厕记录</h2>
<input type="text" id="filterInput" placeholder="输入猫咪名字筛选..." onkeyup="filterTable()">
<table id="recordTable">
<thead>
<tr>
<th>猫咪</th>
<th>进入时间</th>
<th>离开时间</th>
<th>如厕时长 (秒)</th>
<th>进入图</th>
<th>离开图</th>
</tr>
</thead>
<tbody>
{% for r in records %}
<tr>
<td>{{ r['Name'] }}</td>
<td>{{ r['Enter Time'] }}</td>
<td>{{ r['Exit Time'] }}</td>
<td>{{ r['Duration(s)'] }}</td>
<td><img src="{{ '/' + r['Enter Image'] }}" class="thumbnail" onclick="showModal(this.src)"></td>
<td><img src="{{ '/' + r['Exit Image'] }}" class="thumbnail" onclick="showModal(this.src)"></td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="modal" id="imgModal" onclick="hideModal()">
<img id="modalImg" src="">
</div>
<script>
function filterTable() {
let input = document.getElementById("filterInput").value.toLowerCase();
let rows = document.querySelectorAll("#recordTable tbody tr");
rows.forEach(row => {
let name = row.cells[0].textContent.toLowerCase();
row.style.display = name.includes(input) ? "" : "none";
});
}
function showModal(src) {
document.getElementById("modalImg").src = src;
document.getElementById("imgModal").style.display = "flex";
}
function hideModal() {
document.getElementById("imgModal").style.display = "none";
}
</script>
</body>
</html>
"""
with open("cat_monitor/web/templates/index.html", "w", encoding="utf-8") as f:
f.write(html_code)
"Flask 页面 HTML 已写入完成,支持筛选、图片预览。"
结果
✅ Flask 页面 index.html
已完成,具备以下功能:
-
猫咪名字筛选(实时搜索)
-
如厕记录表格展示(进入时间 / 离开时间 / 时长)
-
进入图 & 离开图缩略图展示
-
点击图片放大预览(弹窗模式)