一、背景
在我们日常工作学习中,常常会不知不觉地花大量时间在聊天、浏览网页等低效事务上,导致工作或学习效率低下。如何客观、量化地了解自己的屏幕使用习惯,是提升专注力的关键。所以我利用 Mind+ V2 训练的 AI 模型,结合python脚本自动截屏与 SIoT 物联网可视化技术,实现了一个全自动的屏幕使用时间分析工具,帮助自己实时了解专注度,分析时间耗费趋势,并生成统计结果。

二、功能介绍
主要实现了以下功能:
1、自动截屏:运行 Python 脚本每 60 秒自动截取一次电脑屏幕。
2、AI 智能识别:利用在 Mind+ 中训练的图像分类模型,实时识别当前屏幕活动属于“写代码”、“文档处理”还是“聊天”。
3、专注度计算:Python计算有效工作时间(写代码+文档处理)占总时长的百分比,作为“专注度”指标。
4、数据可视化:通过 SIoT 物联网平台将数据推送至 Mind+ 可视化面板,以仪表盘、折线图、柱状图等形式直观展示各项活动时长和专注度变化趋势。
5、数据记录与报告:所有识别结果自动保存为 CSV 日志文件,程序结束后可生成详细的统计报告。

三、模型训练过程
本项目使用 Mind+ V2 的“模型训练”工具,训练了一个图像分类模型,步骤如下:
1、数据采集:分别截取“写代码”(VS Code、trae、PhpStorm、DataGrip、PythonIDLE、微信开发者工具)、“文档处理”(Word、Excel、PPT)、“聊天”(微信、飞书、QQ)三类屏幕图片,每类约 30-50 张。且每类图片采集全屏和非全屏各种类型,以便识别更加准确。
2、数据标注与训练:在 Mind+ 中创建三个类别并上传图片,设置训练轮次为 30,开始训练。最终模型准确率达到 90% 以上。

四、程序设计流程(软硬件结合)
本作品主要使用 Python 编程,结合 Mind+ 可视化面板,未使用额外硬件,流程如下:
1、训练模型并保存:
将训练好的模型解压缩

2、启动本地SIoT服务:
配置对应的主题topic,准备做脚本交互对接。

3、编写python脚本
(1)初始化:加载 ONNX 模型和 SIoT 连接。


(2)定时截屏:每 60 秒截取全屏,保存为临时文件。每次截图覆盖更新,不占用资源。

(3)模型推理:将截图传入模型,得到当前活动类别。


(4)数据计数与专注度计算:根据识别结果更新“写代码”、“文档处理”、“聊天”的累计次数,并计算专注度。

(5)MQTT 推送:通过 MQTT 协议将各项数据(累计次数、专注度)发送到 SIoT 服务器。

(6)可视化展示:Mind+ 可视化面板订阅 SIoT 中的对应 Topic,实时刷新图表和数据。

五、可视化面板设计
在 Mind+ 可视化面板中,我设计了以下组件来展示数据:
水波图和迷你面积图:分别显示“专注度”、“写代码时长”、“文档处理时长”、“聊天时长”。
折线图:展示“专注度”在过去 20 分钟内的变化趋势。
柱状图:对比“写代码”、“文档处理”、“聊天”三项活动的累计时长。
状态指示:通过“数字时钟”和“运行状态”文字提示系统运行情况。

五、源码
以下分别是python脚本(截屏、SIot、模型计算)、模型文件、大屏设计文件
import pyautogui
import time
import os
import cv2
import numpy as np
import onnxruntime as ort
import yaml
import paho.mqtt.client as mqtt
from datetime import datetime
# ==================== 配置区域 ====================
SCREENSHOT_INTERVAL = 60 # 截图间隔(秒)
SCREENSHOT_FOLDER = r"D:\screenshots"
MODEL_PATH = "best.onnx"
LABEL_PATH = "data.yaml"
DURATION_MINUTES = 0 # 0=无限运行
# SIoT MQTT 配置
SIOT_SERVER = "127.0.0.1"
SIOT_PORT = 1883
SIOT_USER = "siot"
SIOT_PWD = "dfrobot"
TOPIC_CODING = "siot/coding"
TOPIC_WRITING = "siot/writing"
TOPIC_CHATTING = "siot/chatting"
TOPIC_FOCUS = "siot/focus"
TOPIC_TOTAL = "siot/total"
TOPIC_STATUS = "siot/status"
# =================================================
def load_class_names(yaml_path):
"""加载类别标签,兼容 Mind+ 导出的格式"""
with open(yaml_path, "r", encoding="utf-8") as f:
data = yaml.safe_load(f)
# labels: {0: "写代码", 1: "文档处理", 2: "聊天"}
if 'labels' in data:
labels_dict = data['labels']
if isinstance(labels_dict, dict):
return [labels_dict[i] for i in sorted(labels_dict.keys())]
elif isinstance(labels_dict, list):
return labels_dict
elif 'names' in data:
return data['names']
elif 'Class_names' in data:
return list(data['Class_names'].values())
elif isinstance(data, dict):
keys = list(data.keys())
if keys and all(isinstance(k, int) for k in keys):
return [data[i] for i in sorted(keys)]
return list(data.values()) if data else []
elif isinstance(data, list):
return data
else:
raise ValueError(f"无法识别的 data.yaml 格式,内容:{data}")
class ScreenUsageTracker:
def __init__(self, model_path, label_path):
"""初始化:加载模型和标签"""
print("正在加载AI模型...")
self.session = ort.InferenceSession(model_path)
# 使用兼容函数加载标签
self.class_names = load_class_names(label_path)
print(f"加载的类别标签:{self.class_names}")
self.input_name = self.session.get_inputs()[0].name
self.output_name = self.session.get_outputs()[0].name
# 统计变量
self.coding_count = 0
self.writing_count = 0
self.chatting_count = 0
self.total_count = 0
self.records = []
self.start_time = None
print(f"模型加载成功!可识别 {len(self.class_names)} 个类别")
def preprocess_image(self, image_path):
"""图片预处理"""
img = cv2.imread(image_path)
if img is None:
return None
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img = cv2.resize(img, (224, 224))
img = img.astype(np.float32) / 255.0
img = np.transpose(img, (2, 0, 1))
img = np.expand_dims(img, axis=0)
return img
def predict(self, image_path):
"""识别单张图片"""
img = self.preprocess_image(image_path)
if img is None:
return "未知", 0.0
result = self.session.run([self.output_name], {self.input_name: img})
predicted_idx = np.argmax(result[0])
confidence = np.max(result[0])
if predicted_idx < len(self.class_names):
return self.class_names[predicted_idx], confidence
else:
return f"类别_{predicted_idx}", confidence
def take_screenshot(self):
"""截图并保存"""
os.makedirs(SCREENSHOT_FOLDER, exist_ok=True)
filepath = os.path.join(SCREENSHOT_FOLDER, "current.png")
screenshot = pyautogui.screenshot()
screenshot.save(filepath)
return filepath
def calculate_focus(self):
"""计算专注度(写代码+文档处理 占总数的百分比)"""
if self.total_count == 0:
return 0
effective = self.coding_count + self.writing_count
return int((effective / self.total_count) * 100)
def send_to_siot(self, topic, value):
"""发送数据到 SIoT"""
try:
client.publish(topic, str(value), qos=1)
print(f" 📤 发送 {topic} = {value}")
except Exception as e:
print(f" ❌ 发送失败: {e}")
def update_dashboard(self):
"""更新大屏上的所有数据"""
focus = self.calculate_focus()
self.send_to_siot(TOPIC_CODING, self.coding_count)
self.send_to_siot(TOPIC_WRITING, self.writing_count)
self.send_to_siot(TOPIC_CHATTING, self.chatting_count)
self.send_to_siot(TOPIC_TOTAL, self.total_count)
self.send_to_siot(TOPIC_FOCUS, focus)
# 柱状图专用:发送三个时长的组合数据
# 格式:"写代码时长,文档处理时长,聊天时长"
combined = f"{self.coding_count},{self.writing_count},{self.chatting_count}"
self.send_to_siot("siot/chart_data", combined)
print(f" 📤 发送 siot/chart_data = {combined}")
def run(self, duration_minutes=0):
"""主运行循环"""
self.start_time = datetime.now()
print(f"\n{'='*50}")
print(f"屏幕使用统计已启动")
print(f"开始时间:{self.start_time.strftime('%Y-%m-%d %H:%M:%S')}")
print(f"截图间隔:{SCREENSHOT_INTERVAL} 秒")
print(f"SIoT 服务器:{SIOT_SERVER}:{SIOT_PORT}")
if duration_minutes > 0:
print(f"计划运行:{duration_minutes} 分钟")
print(f"{'='*50}\n")
self.send_to_siot(TOPIC_STATUS, 1)
try:
count = 0
while True:
if duration_minutes > 0:
elapsed = (datetime.now() - self.start_time).total_seconds() / 60
if elapsed >= duration_minutes:
print(f"\n计划运行时间已到,自动停止")
break
screenshot_path = self.take_screenshot()
class_name, confidence = self.predict(screenshot_path)
# 根据识别结果更新计数(中文标签匹配)
if class_name == "写代码":
self.coding_count += 1
elif class_name == "文档处理":
self.writing_count += 1
elif class_name == "聊天":
self.chatting_count += 1
else:
print(f" 未匹配类别:{class_name}")
self.total_count += 1
count += 1
focus = self.calculate_focus()
timestamp = datetime.now()
self.records.append({
'time': timestamp,
'class': class_name,
'confidence': confidence
})
print(f"[{timestamp.strftime('%H:%M:%S')}] #{count} → {class_name} ({confidence:.2%}) | 专注度: {focus}%")
self.update_dashboard()
time.sleep(SCREENSHOT_INTERVAL)
except KeyboardInterrupt:
print(f"\n\n用户手动停止...")
self.send_to_siot(TOPIC_STATUS, 0)
self.end_time = datetime.now()
print(f"\n统计结束,共记录 {len(self.records)} 次")
self.save_log()
self.print_report()
def save_log(self):
"""保存详细日志到CSV文件"""
import csv
log_file = f"screen_log_{self.start_time.strftime('%Y%m%d_%H%M%S')}.csv"
with open(log_file, 'w', newline='', encoding='utf-8-sig') as f:
writer = csv.writer(f)
writer.writerow(['时间', '识别结果', '置信度'])
for r in self.records:
writer.writerow([
r['time'].strftime('%Y-%m-%d %H:%M:%S'),
r['class'],
f"{r['confidence']:.3f}"
])
print(f"详细日志已保存到:{log_file}")
def print_report(self):
"""打印最终报告"""
if self.total_count == 0:
return
duration_minutes = self.total_count
effective = self.coding_count + self.writing_count
focus = (effective / self.total_count) * 100
print(f"\n{'='*50}")
print(f" 最终报告")
print(f"{'='*50}")
print(f"运行时长:{duration_minutes} 分钟")
print(f"写代码:{self.coding_count} 分钟 ({self.coding_count/duration_minutes*100:.1f}%)")
print(f"文档处理:{self.writing_count} 分钟 ({self.writing_count/duration_minutes*100:.1f}%)")
print(f"聊天:{self.chatting_count} 分钟 ({self.chatting_count/duration_minutes*100:.1f}%)")
print(f"专注度:{focus:.1f}%")
# ==================== 初始化 MQTT 客户端 ====================
print(f"正在连接 SIoT 服务器 {SIOT_SERVER}:{SIOT_PORT}...")
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
client.username_pw_set(SIOT_USER, SIOT_PWD)
client.connect(SIOT_SERVER, SIOT_PORT, 60)
client.loop_start()
print("MQTT 连接成功!")
# ==================== 检查模型文件 ====================
if not os.path.exists(MODEL_PATH):
print(f"错误:找不到模型文件 {MODEL_PATH}")
input("按回车键退出...")
exit(1)
if not os.path.exists(LABEL_PATH):
print(f"错误:找不到标签文件 {LABEL_PATH}")
input("按回车键退出...")
exit(1)
# ==================== 运行主程序 ====================
tracker = ScreenUsageTracker(MODEL_PATH, LABEL_PATH)
tracker.run(duration_minutes=DURATION_MINUTES)
print("\n程序结束,按回车键退出...")
input()
附件
附件
六、应用价值与展望
本作品为个人时间管理和效率提升提供了一个可量化、自动化的工具。未来可以进一步扩展:
增加更多识别类别(如浏览网页、视频会议)。
增加专注度低于阈值时的实时提醒功能(如播放提示音)。
结合硬件(如行空板)实现实体专注提示灯。

返回首页
回到顶部



评论