回到首页 返回首页
回到顶部 回到顶部
返回上一页 返回上一页

【灵感数字组】 屏幕专注度分析助手 —— 基于 Mind+ 与 SIoT 的屏幕使用时间统计 简单

头像 阿琛 2026.04.09 29 0

一、背景

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

ScreenShot_2026-04-08_231649_439.png

二、功能介绍

主要实现了以下功能:

1、自动截屏:运行 Python 脚本每 60 秒自动截取一次电脑屏幕。

2、AI 智能识别:利用在 Mind+ 中训练的图像分类模型,实时识别当前屏幕活动属于“写代码”、“文档处理”还是“聊天”。

3、专注度计算:Python计算有效工作时间(写代码+文档处理)占总时长的百分比,作为“专注度”指标。

4、数据可视化:通过 SIoT 物联网平台将数据推送至 Mind+ 可视化面板,以仪表盘、折线图、柱状图等形式直观展示各项活动时长和专注度变化趋势。

5、数据记录与报告:所有识别结果自动保存为 CSV 日志文件,程序结束后可生成详细的统计报告。

image.png

三、模型训练过程

本项目使用 Mind+ V2 的“模型训练”工具,训练了一个图像分类模型,步骤如下:

1、数据采集:分别截取“写代码”(VS Code、trae、PhpStorm、DataGrip、PythonIDLE、微信开发者工具)、“文档处理”(Word、Excel、PPT)、“聊天”(微信、飞书、QQ)三类屏幕图片,每类约 30-50 张。且每类图片采集全屏和非全屏各种类型,以便识别更加准确。

2、数据标注与训练:在 Mind+ 中创建三个类别并上传图片,设置训练轮次为 30,开始训练。最终模型准确率达到 90% 以上。

ScreenShot_2026-04-06_174016_118.png

四、程序设计流程(软硬件结合)

本作品主要使用 Python 编程,结合 Mind+ 可视化面板,未使用额外硬件,流程如下:

1、训练模型并保存:

将训练好的模型解压缩

image.png

2、启动本地SIoT服务:

配置对应的主题topic,准备做脚本交互对接。

ScreenShot_2026-04-08_231859_613.png

3、编写python脚本

(1)初始化:加载 ONNX 模型和 SIoT 连接。

image.png
image.png

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

image.png

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

image.png
image.png

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

image.png

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

image.png

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

ScreenShot_2026-04-08_232013_236.png

五、可视化面板设计

在 Mind+ 可视化面板中,我设计了以下组件来展示数据:

水波图和迷你面积图:分别显示“专注度”、“写代码时长”、“文档处理时长”、“聊天时长”。

折线图:展示“专注度”在过去 20 分钟内的变化趋势。

柱状图:对比“写代码”、“文档处理”、“聊天”三项活动的累计时长。

状态指示:通过“数字时钟”和“运行状态”文字提示系统运行情况。

ScreenShot_2026-04-08_231739_458.png

五、源码

以下分别是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()

附件

六、应用价值与展望

本作品为个人时间管理和效率提升提供了一个可量化、自动化的工具。未来可以进一步扩展:

增加更多识别类别(如浏览网页、视频会议)。

增加专注度低于阈值时的实时提醒功能(如播放提示音)。

结合硬件(如行空板)实现实体专注提示灯。

评论

user-avatar