当前位置: 首页 > backend >正文

猫咪如厕检测与分类识别系统系列【十二】猫咪进出事件逻辑及日志优化

前情提要


家里养了三只猫咪,其中一只布偶猫经常出入厕所。但因为平时忙于学业,没法时刻关注牠的行为。我知道猫咪的如厕频率和时长与健康状况密切相关,频繁如厕可能是泌尿问题,停留过久也可能是便秘或不适。为了更科学地了解牠的如厕习惯,我计划搭建一个基于视频监控和AI识别的系统,自动识别猫咪进出厕所的行为,记录如厕时间和停留时长,并区分不同猫咪。这样即使我不在家,也能掌握猫咪的健康状态,更安心地照顾它们。

🎓 各位的关注与点赞是我持续分享的最大动力,衷心感谢大家的支持!
📢 欢迎正在攻读硕博学位的同学,或是对人工智能充满热情的朋友们,关注我的个人公众号。在这里,我将持续更新博士期间阅读的前沿论文解读、项目实战经验分享,以及我对AI技术趋势的思考与探讨。
✨ 无论你是科研工作者、工程开发者,还是AI初学者,都能在这里找到干货与灵感。让我们一起交流、成长、探索人工智能的无限可能!

已完成工作:

✅猫咪如厕检测与分类识别系统系列【一】 功能需求分析及猫咪分类特征提取
✅猫咪如厕检测与分类识别系统系列【二】多图上传及猫咪分类特征提取更新
✅猫咪如厕检测与分类识别系统系列【三】 融合yolov11目标检测
✅猫咪如厕检测与分类识别系统系列【四】融合检测日志输出及前端展示界面制作
✅猫咪如厕检测与分类识别系统系列【五】信息存储数据库改进+添加猫咪页面制作+猫咪躯体匹配算法架构更新
✅猫咪如厕检测与分类识别系统系列【六】分类模型训练+混合检测分类+未知目标自动更新

✅猫咪如厕检测与分类识别系统系列【七】 当前阶段总结报告

✅猫咪如厕检测与分类识别系统系列【八】 检测推理事件整合+视频推流架构分析

✅猫咪如厕检测与分类识别系统系列【九】 视频检测区域在线绘制+支持摄像头+网络摄像头+整体构建【上】

✅猫咪如厕检测与分类识别系统系列【九】 视频检测区域在线绘制+支持摄像头+网络摄像头+整体构建【下】

✅猫咪如厕检测与分类识别系统系列【十】 视频检测区域动态监测及实时更新
✅猫咪如厕检测与分类识别系统系列【十一】区域进入事件相应逻辑鲁棒性更新

本小节继续更新猫咪进出事件逻辑及日志优化


def log_enter(self, name, enter_time, enter_img, method="classifier", score=0.0):time_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S")with open("enter_log.txt", "a", encoding="utf-8") as f:f.write(f"[{time_str}] 🟢 {name} 进入区域(方法:{method},评分:{score:.2f}) 图像:{enter_img}\\n")

⚠️ 确保此方法放在类 ToiletLogger 里。


✅ 2. 确认导入的是 更新后的 toilet_logger.py
main.pysession_tracker.py 中导入时:

from toilet_logger import ToiletLogger

确保这个模块是刚刚更新的那一个路径下的( cat_monitor/toilet_logger.py


✅ 3. 重启环境

如果已经改了文件但还在报错,有可能是旧模块缓存没有刷新(特别是在 notebook / 热加载情况下):

# 重启 Python 解释器,确保 log_enter 方法生效

✅ 快速验证(临时测试)

在项目根目录开一个 Python 控制台测试是否含有该方法:

from toilet_logger import ToiletLogger
logger = ToiletLogger()
print(hasattr(logger, "log_enter"))  # 应输出 True

如厕记录没有被写入数据库或日志 ,很可能是以下某一部分逻辑出了问题,我们逐一定位:


✅ 一步步排查方向:

✅ 1. 是否进入了 update() 中 in_region 的判断逻辑?

print(f"[DEBUG] 猫进入区域: {cat_name}, 状态: {session['status']}")

确认是不是根本没触发记录逻辑。


✅ 2. 是否正确调用了 log()log_enter()
可以在 CatSessionTracker 中加一行:

print(f"[记录中] 调用 logger.log_enter for {cat_name}")
print(f"[记录中] 调用 logger.log for {cat_name}")

✅ 3. logger.log() 中是否有异常但被静默?

临时加上异常捕获打印看看:

try:self.logger.log(...)
except Exception as e:print(f"[错误] 写入日志失败:{e}")

✅ 4. 检查数据库是否真的写入失败

打开 records/toilet_log.db,手动查询是否有数据:

sqlite3 records/toilet_log.db
sqlite> SELECT * FROM toilet_logs ORDER BY rowid DESC LIMIT 5;

✅ 最快的恢复方案:

  • 输出当前应使用的 CatSessionTracker + toilet_logger.py

  • 包含打印和异常捕获

  • 确保进入一次就有图有日志

  • 包括一次完整的检测流程样例


经过全量排查并还原一个稳定可运行的检测 + 记录系统核心逻辑


🎯 目标

阶段行为是否记录截图保存到
猫进入区域✅ 立即记录enter_log.txt
猫离开区域✅ 如厕结束toilet_log.db

✅ 步骤

输出两段最关键的更新版代码,确保能运行:


✅ 1. CatSessionTracker(完整 + 带 debug 打印)

import time
import cv2
import os
from config.paths import RECORDS_DIR
from collections import defaultdictclass CatSessionTracker:def __init__(self, logger, min_interval=10, min_duration=10):self.logger = loggerself.min_interval = min_intervalself.min_duration = min_durationself.sessions = defaultdict(lambda: {"status": "IDLE","entry_time": None,"last_exit_time": 0,"method": "classifier","score": 0.0})def update(self, frame, result):annotated, cat_name, in_region, method, score = resultif cat_name == "Unknown":print("[跳过] 未识别猫")return annotatedsession = self.sessions[cat_name]now = time.time()# 🟢 猫进入if in_region:if session["status"] == "IDLE" and (now - session["last_exit_time"]) > self.min_interval:print(f"🟢 猫进入:{cat_name}")session["status"] = "ENTERED"session["entry_time"] = nowsession["method"] = methodsession["score"] = scoreenter_img = f"{cat_name}_enter_{int(now)}.jpg"enter_path = os.path.join("static/records", enter_img)cv2.imwrite(os.path.join(RECORDS_DIR, enter_img), frame)session["enter_img"] = enter_pathself.logger.log_enter(cat_name, now, enter_path, method, score)# 🔴 猫离开elif session["status"] == "ENTERED":session["status"] = "IDLE"exit_time = nowsession["last_exit_time"] = exit_timeduration = exit_time - session["entry_time"]if duration >= self.min_duration:print(f"🔴 猫离开:{cat_name},如厕 {int(duration)} 秒")exit_img = f"{cat_name}_exit_{int(now)}.jpg"exit_path = os.path.join("static/records", exit_img)cv2.imwrite(os.path.join(RECORDS_DIR, exit_img), frame)try:self.logger.log(cat_name,session["entry_time"],exit_time,session["enter_img"],exit_path,session["method"],session["score"])except Exception as e:print(f"❌ 写入数据库失败: {e}")else:print(f"⚠️ 猫 {cat_name} 如厕时间太短({int(duration)} 秒),未记录")return annotated

✅ 2. result 的生成方式(在 detectormain.py 中应输出的是):

result = (annotated_frame, cat_name, in_region, method, score)
tracker.update(frame, result)

🧠 然而以上代码仍然“没反应”,提供摄像头流和区域判断部分的代码(process_frame

没检测到猫咪的时候一直返回的 if cat_name == “Unknown”:
return annotated 根本没有执行到出去

问题的根源是:

没有检测到猫咪 或识别失败为 "Unknown" 时,CatSessionTracker.update() 直接 return annotated完全跳过了“猫是否离开”的判断逻辑


❗这会导致的问题

  • 猫已经离开了,但因为没有检测出猫,所以逻辑无法触发“离开”状态转移

  • session["status"] 一直卡在 ENTERED,无法恢复为 IDLE

  • 所有如厕流程卡死在中途


✅ 正确的修复方案:即使识别失败也要判断是否离开区域

我们只需小改这个判断逻辑:


✅ 原错误代码:

if cat_name == "Unknown":return annotated

✅ 修复代码:

if cat_name == "Unknown":# 即使识别失败,也可能是上一只猫已经离开for name, session in self.sessions.items():if session["status"] == "ENTERED":now = time.time()duration = now - session["entry_time"]if duration > self.min_duration:print(f"🔴 {name} 猫可能已离开(当前检测不到),如厕 {int(duration)} 秒")session["status"] = "IDLE"session["last_exit_time"] = now# 可选择:记录“模糊离开”日志或跳过写入return annotated

✅ 或者简洁一点:

if cat_name == "Unknown":# 只有状态为 ENTERED 的猫,且当前帧没有识别出它 → 视为离开for name, session in self.sessions.items():if session["status"] == "ENTERED":print(f"🔴 猫 {name} 可能离开,但当前未识别出任何猫,自动重置状态")session["status"] = "IDLE"session["last_exit_time"] = time.time()return annotated

🎯 只需要:

CatSessionTracker.update() 中找到:

if cat_name == "Unknown":return annotated

并改为上述“兜底处理所有 session”的逻辑即可 ✅


但是这样后面的记录如厕 根本执行不到,
所以我们不能一刀切地在 "Unknown" 时直接 return,否则就永远不会触发:

  • 离开区域的判断

  • 记录如厕行为的逻辑


✅ 正确的处理方式是:

始终保留上一个已知猫的状态 ,即使当前帧无法识别出猫,也要根据“区域是否空了”来判断猫是否已经离开!


🎯完美兼容“断识别”的情况:

✅ 修改逻辑:

# 猫识别失败时,不直接 return,而是判断之前是否有猫在区域中
if cat_name == "Unknown":for name, session in self.sessions.items():if session["status"] == "ENTERED":# 当前区域为空,说明猫可能已经离开if not in_region:now = time.time()duration = now - session["entry_time"]if duration >= self.min_duration:print(f"🔴 猫 {name} 离开(未被识别),记录如厕 {int(duration)} 秒")session["status"] = "IDLE"session["last_exit_time"] = now# 截图保存exit_img = f"{name}_exit_{int(now)}.jpg"exit_path = os.path.join("static/records", exit_img)cv2.imwrite(os.path.join(RECORDS_DIR, exit_img), frame)self.logger.log(name,session["entry_time"],now,session.get("enter_img", ""),exit_path,session.get("method", "unknown"),session.get("score", 0.0))else:print(f"⚠️ 猫 {name} 离开(未被识别),但如厕时间太短")session["status"] = "IDLE"session["last_exit_time"] = nowreturn annotated

✅ 整体思路简述

  1. 如果识别不出猫(unknown)

  2. 就回头看看之前哪只猫状态是 "ENTERED"

  3. 如果区域现在是空的 → 猫离开 → 自动记录 ✅


以下是 修复后的完整版本 CatSessionTracker.py ,支持:

  • 进入时记录 enter 图 & 写日志

  • 离开时记录 exit 图 & 写数据库

  • 即使识别失败,只要区域空了也能记录“猫已离开”


CatSessionTracker.py

import time
import cv2
import os
from config.paths import RECORDS_DIR
from collections import defaultdictclass CatSessionTracker:def __init__(self, logger, min_interval=10, min_duration=10):self.logger = loggerself.min_interval = min_interval  # 两次如厕最短间隔self.min_duration = min_duration  # 最短如厕时间self.sessions = defaultdict(lambda: {"status": "IDLE","entry_time": None,"last_exit_time": 0,"method": "classifier","score": 0.0})def update(self, frame, result):annotated, cat_name, in_region, method, score = resultnow = time.time()# 🟡 猫识别失败时,也检查是否有猫要离开if cat_name == "Unknown":for name, session in self.sessions.items():if session["status"] == "ENTERED" and not in_region:duration = now - session["entry_time"]if duration >= self.min_duration:print(f"🔴 猫 {name} 离开(未被识别),记录如厕 {int(duration)} 秒")session["status"] = "IDLE"session["last_exit_time"] = nowexit_img = f"{name}_exit_{int(now)}.jpg"exit_path = os.path.join("static/records", exit_img)cv2.imwrite(os.path.join(RECORDS_DIR, exit_img), frame)self.logger.log(name,session["entry_time"],now,session.get("enter_img", ""),exit_path,session.get("method", "unknown"),session.get("score", 0.0))else:print(f"⚠️ 猫 {name} 离开时间过短,未记录")session["status"] = "IDLE"session["last_exit_time"] = nowreturn annotated# 🟢 猫进入区域session = self.sessions[cat_name]if in_region:if session["status"] == "IDLE" and (now - session["last_exit_time"]) > self.min_interval:print(f"🟢 猫进入:{cat_name}")session["status"] = "ENTERED"session["entry_time"] = nowsession["method"] = methodsession["score"] = scoreenter_img = f"{cat_name}_enter_{int(now)}.jpg"enter_path = os.path.join("static/records", enter_img)cv2.imwrite(os.path.join(RECORDS_DIR, enter_img), frame)session["enter_img"] = enter_pathself.logger.log_enter(cat_name, now, enter_path, method, score)# 🔴 猫识别正常,但已经不在区域 → 离开elif session["status"] == "ENTERED":exit_time = nowduration = exit_time - session["entry_time"]if duration >= self.min_duration:print(f"🔴 猫离开:{cat_name},如厕 {int(duration)} 秒")session["status"] = "IDLE"session["last_exit_time"] = exit_timeexit_img = f"{cat_name}_exit_{int(now)}.jpg"exit_path = os.path.join("static/records", exit_img)cv2.imwrite(os.path.join(RECORDS_DIR, exit_img), frame)self.logger.log(cat_name,session["entry_time"],exit_time,session.get("enter_img", ""),exit_path,method,score)else:print(f"⚠️ 猫 {cat_name} 离开时间太短,未记录")session["status"] = "IDLE"session["last_exit_time"] = exit_timereturn annotated

http://www.xdnf.cn/news/410.html

相关文章:

  • 【sylar-webserver】8 HOOK模块
  • Linux-进度条小程序
  • 【笔记】网路安全管理-实操
  • FiftyOne 管理数据
  • React-useRef
  • 实现Azure Data Factory安全地请求企业内部API返回数据
  • 图灵奖得主LeCun:DeepSeek开源在产品层是一种竞争,但在基础方法层更像是一种合作;新一代AI将情感化
  • Ubuntu20.04下Docker方案实现多平台SDK编译
  • 国网B接口协议图像数据上报通知接口流程详解以及上报失败原因(电网B接口)
  • 【LeetCode 热题 100】双指针 系列
  • 【leetcode100】分割等和子集
  • systemctl管理指令
  • 为什么信号完整性对于高速连接器设计至关重要?
  • 计算机三级:信息安全基础技术与原理(2.1密码技术简单梳理)
  • 上海市计算机学会竞赛平台2023年7月月赛丙组题目解题报告
  • asp.net core webapi+efcore
  • SQL系列:常用函数
  • ProfiNet转DeviceNet边缘计算网关多品牌集成实践:污水处理厂设备网络融合全流程解析
  • leetcode 674. Longest Continuous Increasing Subsequence
  • 包含物体obj与相机camera的 代数几何代码解释
  • Flutter 弹窗队列管理:实现一个线程安全的通用弹窗队列系统
  • 学习笔记十七——Rust 支持面向对象编程吗?
  • Yue生成中文歌词
  • Mybatis
  • 数据结构0基础学习堆
  • AcWing 11:背包问题求方案数 ← 0-1背包
  • 与终端同居日记:Linux指令の进阶撩拨手册
  • docker底层原理
  • 如何给云开发生成的智能体增加权限判断
  • AtCoder ABC402 A~D 题解