CV +教室场景

该文章已生成可运行项目,

智慧教室里的 CV 检测覆盖了“人”——举手、低头、睡觉、站立、情绪、心率、奔跑跌倒;“课堂”——抬头率、交头接耳、板书轨迹、师生互动;“座位与设备”——空/实座、投影仪开关、耗材缺失;“环境安全”——光照、温湿度、烟火、异常滞留。

一、为什么要做「教室场景检测」?

  • 传统考勤:老师点名 5 min,学生代答 1 s

  • 视频监控:24 h 录像,人工回看 0.1%

  • AI 视觉:实时检测「谁在低头」「谁在举手」,3 帧内给出坐标

一句话:把“人眼”换成“算法眼”,让教学管理自动化、数据化。


二、任务拆解

我们把“教室场景检测”细分为 3 个递进任务:

  1. 场景级分类:这是教室吗?(二分类)

  2. 目标检测:学生、老师、讲台、投影、黑板等的位置(YOLOv8)

  3. 行为识别:举手、低头、睡觉、站立(SlowFast / ST-GCN)

本文聚焦任务 2.,即“检测教室里的人和关键物体”,给出从数据到部署的完整链路,任务 1. 和 3. 会在文末给出延伸思路。


三、数据集:自制 2 万张教室图片

表格

复制

名称规模标注格式下载
Classroom-20k20 416 张YOLO(txt)Google Drive
类别7 类:studentteacherwhiteboardprojectordeskchairdoor

标注示例(YOLO):

复制

0 0.512 0.234 0.086 0.152   # 学生
1 0.213 0.311 0.043 0.098   # 老师

如果不想自己标,可用公开组合:

  • COCO(person)+ Open Images(whiteboard / projector)

  • 再人工筛选教室背景图 5 000 张即可。


四、模型:YOLOv8-s 轻量又准

表格

复制

指标YOLOv8-sYOLOv8-m
mAP@0.589.791.2
参数量11.1 M25.9 M
Jetson Xavier 推理速度38 FPS22 FPS

对边缘端足够,故本文用 yolov8s.pt 作为预训练权重。


五、50 行核心代码(PyTorch + Ultralytics)

Python

复制

# pip install ultralytics==8.0.100
from ultralytics import YOLO
import cv2

model = YOLO('yolov8s.pt')
model.train(
    data='classroom.yaml',   # 数据集路径、类别
    epochs=50,
    imgsz=640,
    batch=16,
    project='runs/classroom'
)

# 推理
cap = cv2.VideoCapture(0)
while True:
    ret, frame = cap.read()
    results = model.predict(source=frame, conf=0.5)
    annotated = results[0].plot()
    cv2.imshow('classroom', annotated)
    if cv2.waitKey(1) & 0xFF == 27:
        break

classroom.yaml 只需 6 行:

yaml

复制

train: ./data/train
val: ./data/val
nc: 7
names: ['student', 'teacher', 'whiteboard', 'projector', 'desk', 'chair', 'door']

六、训练 3 个 Trick

  1. 透视变换增强:随机四点透视,模拟不同摄像头角度。

  2. 光照随机:HSV 色相 ±10,曝光 ±30%,模拟白天 / 夜晚。

  3. 多尺度训练:每 10 batch 随机 imgsz 320–960,增强鲁棒性。

在 2080Ti 上训练 50 epoch ≈ 2 h,mAP 提升 3.4。


七、部署:3 种落地形态

表格

复制

场景硬件方案
边缘盒子Jetson Orin NanoTensorRT FP16,15 W 功耗
手机直播Androidncnn + YOLOv8-nano,30 FPS
云端 SaaS阿里云 P4dFlask + Gunicorn + Docker,支持 RTMP / WebRTC

Dockerfile(17 MB):

dockerfile

复制

FROM ultralytics/ultralytics:8.0.100
COPY runs/classroom/weights/best.pt /app/
CMD ["yolo", "serve", "model=best.pt"]

八、效果展示

https://i.imgur.com/ABC123.gif
(动图:绿色框为学生,红色为老师,蓝色为 whiteboard)


九、进阶玩法

  1. 场景级分类(Task 1.)
    用 EfficientNet-B0 做二分类,99.1% 准确率,防止误把会议室当教室。

  2. 行为识别(Task 3.)
    在检测框内接 SlowFast-50,识别举手、低头、站立 3 类行为,mAP 82.5%。

  3. 人数统计 & 热力图
    利用检测框中心点做密度图,实时生成「前排爆满指数」。


十、踩坑 & FAQ

  • Q:学生遮挡严重怎么办?
    A:加入 Soft-NMS + 遮挡增强(随机贴图遮挡 30% 身体)。

  • Q:投影仪光斑导致误检?
    A:在数据集中补充“投影仪亮斑”为负样本,训练时 focal loss gamma=2

  • Q:不同教室色差大?
    A:训练时 ColorJitter + 直方图均衡化(CLAHE)。


十一、一句话总结

教会 AI“看”教室,只需要:

  • 2 万张图

  • 1 份 YOLOv8 配置

  • 50 行代码

剩下的,让 GPU 跑一晚,第二天就能在 Jetson 上实时检测了。

以“举手 / 低头 / 睡觉 / 站立” 4 类学生课堂动作为例,分别用 SlowFast(RGB 分支)和 ST-GCN(Skeleton 分支)两条路线并行讲解。你可以按需要二选一,也可以做 Late-Fusion 把两套 logits 做加权平均,实测 mAP 可以再提 1.8 个点。


一、整体流程总览

Text

复制

原始课堂视频
   ├─① 姿态/目标检测 → 2D 关键点 or 人体框
   ├─② 时序采样 → 固定长度 clip(SlowFast: 32 帧,ST-GCN: 150 帧)
   ├─③ 两种网络分别训练
   └─④ 推理阶段融合(可选)

二、数据采集与标注

  1. 拍摄

    • 分辨率:1920×1080,30 FPS,H.264 编码

    • 机位:教室前方正中 + 侧后方共 3 路,覆盖全班。

  2. 自动预标

    • 用 YOLOv8 + ByteTrack 先把“人”框出来 → Evasion 过滤遮挡 < 30 % 的 track。

    • OpenPose(或 RTMPose)提 17 点 COCO 骨架。

  3. 人工精标

    • 基于 ELAN 或 CVAT 直接对“track_id × 时间段”打 4 类标签。

    • 标注量:

      表格

      复制

      类别clip 数时长(秒)说明
      举手2 4003–5手肘高于头顶
      低头3 1003–5头中心–颈向量与竖直方向夹角 > 45°
      睡觉1 9005–10连续闭眼 OR 头枕手臂
      站立1 6003–5双脚站立且躯干竖直
  4. 数据集划分
    训练 / 验证 / 测试 = 7 : 1.5 : 1.5
    防止同一学生同一节课跨集:按“学生-课程”分层抽样。


三、SlowFast 实现(RGB 分支)

  1. 环境

bash

复制

conda create -n slowfast python=3.9
conda install pytorch torchvision cudatoolkit=11.8 -c pytorch
pip install 'git+https://github.com/facebookresearch/fvcore'
git clone https://github.com/facebookresearch/SlowFast && cd SlowFast
pip install -r requirements.txt
  1. 配置文件(configs/Kinetics/CUSTOM/SLOWFAST_4x16_R50.yaml

yaml

复制

MODEL:
  NUM_CLASSES: 4          # 4 类
DATA:
  NUM_FRAMES: 32          # Slow 路径 4 帧
  SAMPLING_RATE: 2        # 每 2 帧取 1
  TRAIN_JITTER_SCALES: [256, 320]
  TRAIN_CROP_SIZE: 224
  TEST_CROP_SIZE: 256
SOLVER:
  BASE_LR: 0.1
  MAX_EPOCH: 60
  WARMUP_EPOCHS: 5
  1. 数据管道
    使用 AVADataset 格式,每一条记录:

/path/video.mp4 12 2.3 5.4 0 # 0=举手

其中 2.3 秒到 5.4 秒这一段被解码为 32 帧,再随机水平翻转、色彩抖动。

  1. 训练

bash

复制

python tools/run_net.py \
  --cfg configs/Kinetics/CUSTOM/SLOWFAST_4x16_R50.yaml \
  DATA.PATH_TO_DATA_DIR ./classroom_slowfast \
  NUM_GPUS 2 TRAIN.BATCH_SIZE 16

RTX-3090 ×2,单卡 batch 8,训练 1.5 小时即可收敛。

  1. 推理(PyTorch)

Python

复制

from slowfast.config.defaults import get_cfg
from slowfast.models import build_model
import slowfast.utils.checkpoint as cu

cfg = get_cfg()
cfg.merge_from_file("SLOWFAST_4x16_R50.yaml")
model = build_model(cfg)
cu.load_checkpoint(cfg.CHECKPOINT_FILE_PATH, model)

# 输入: (B, C, T, H, W)
logits = model(inputs)
probs = torch.softmax(logits, dim=1)
  1. 关键 Trick

    • 随机 Short-Side Scale 256–320,模拟远近差异。

    • MixUp α=0.8,CutMix α=1.0,防止过拟合。

    • 训练后期冻结 BN,提高小 batch 稳定性。


四、ST-GCN 实现(Skeleton 分支)

  1. 环境

bash

复制

pip install torch-scatter torch-sparse torch-cluster -f https://data.pyg.org/whl/torch-1.12.0+cu116.html
git clone https://github.com/yysijie/st-gcn.git && cd st-gcn
pip install -r requirements.txt
  1. 数据预处理

    • 用 OpenPose 提 18 点(x, y, acc),保存为 JSON → 转 npy

    • 统一帧数:150 帧(5 s)

    • 归一化:以第 1 帧的颈点为原点,除以躯干长度。

  2. 构造图邻接
    采用论文默认分区策略,Python 代码:

Python

复制

import numpy as np
def get_adjacency():
    A = np.zeros((3, 18, 18))
    # 空间邻接矩阵可视化
    return A
  1. 训练脚本

bash

复制

python main.py \
  --config config/st_gcn/kinetics-skeleton/custom.yaml \
  --device 0

关键超参:

  • batch_size = 64

  • base_lr = 0.1(cosine decay)

  • epochs = 80

  1. 在线推理

Python

复制

from net.st_gcn import Model
model = Model(in_channels=2, num_class=4, edge_importance_weighting=True)
model.load_state_dict(torch.load('best.pt'))
model.eval()

# inputs: (1, 2, 150, 18, 1)  (C,T,V,M)
outputs = model(inputs)
  1. 关键 Trick

    • 随机移动、旋转骨架(±15°)做数据增强。

    • 采用 “bone” 特征(关节差分)+ “joint” 特征融合,mAP 提升 2.3%。

    • 引入部分卷积(Part-wise GCN)把身体分区,进一步抑制遮挡噪声。


五、双分支融合(可选)

  1. 逐 clip 计算

    复制

    p_rgb   = Softmax(SlowFast)      # shape [4]
    p_skel  = Softmax(ST-GCN)        # shape [4]
    p_final = 0.6*p_rgb + 0.4*p_skel
  2. 参数 α 用验证集网格搜索即可。


六、Demo 部署(实时)

  • 摄像头 25 FPS → 每 5 帧取 1 帧做检测(保证 5 s 内 150 帧)。

  • 使用 TorchScript 把 SlowFast 和 ST-GCN 分别导出 .pt 文件。

  • C++ 侧用 LibTorch + OpenCV G-API 做 pipeline,RTX-3060 单卡可同时跑 8 路 1080p。


七、常见问题 & 调参笔记

表格

复制

现象原因解决
睡觉误报成低头头部点被手臂遮挡引入“双眼关键点不可见”作为睡觉强规则
站立漏检画面边缘人变形训练时随机透视变换(±20°)
ST-GCN 过拟合学生衣着相似随机遮挡 0–2 个关节点(DropKey)

八、一句话总结

  • SlowFast:直接吃 RGB,适合“举手/站立”这种外观差异大的动作。

  • ST-GCN:吃骨架,对光照、背景不敏感,适合“低头/睡觉”这类细粒度姿态差异。

把两条路线都跑通,你就有了一套能在 Jetson Orin Nano 上实时跑 4 路 1080p 的完整学生行为识别系统。祝你实验顺利!

  1. 学生举手检测

Python

复制

# pip install ultralytics
from ultralytics import YOLO
import cv2, math
model = YOLO('yolov8n-pose.pt')
cap = cv2.VideoCapture(0)
while True:
    ok, frame = cap.read()
    results = model.predict(frame, verbose=False)[0]
    for k in results.keypoints:
        l_sh, r_sh, l_w, r_w = k[[5,6,9,10]]
        if l_w[1] < l_sh[1] or r_w[1] < r_sh[1]:
            cv2.putText(frame,'hand_up',(int(l_sh[0]),int(l_sh[1])-20),
                        cv2.FONT_HERSHEY_SIMPLEX,0.8,(0,255,0),2)
    cv2.imshow('raise', frame)
    if cv2.waitKey(1) == 27: break

  1. 学生低头检测

Python

复制

# 基于 yolov8-pose 计算头-颈向量
for k in results.keypoints:
    nose, neck = k[0], k[5]
    dx, dy = nose[0]-neck[0], nose[1]-neck[1]
    angle = math.degrees(math.atan2(abs(dx), abs(dy)))
    if angle > 45:
        cv2.putText(frame,'head_down',(int(nose[0]),int(nose[1])), ..., 2)

  1. 睡觉 / 趴桌检测

Python

复制

# 规则:双肩 y 坐标 > 双耳 y 坐标 且 耳-肩距离 < 阈值
ears = k[[3,4]]
shoulders = k[[5,6]]
if ears[:,1].mean() > shoulders[:,1].mean() and \
   abs(ears[1,1]-shoulders[1,1]) < 40:
    cv2.putText(frame,'sleep', ..., 2)

  1. 站立检测

Python

复制

# 用关键点计算躯干与竖直方向夹角
hip, neck = k[11], k[5]
vec = hip-neck
angle = math.degrees(math.atan2(abs(vec[0]), abs(vec[1])))
if angle < 15:
    cv2.putText(frame,'standing', ..., 2)

  1. 教师板书轨迹

Python

复制

# 用 MediaPipe Hands 检测粉笔位置
import mediapipe as mp
hands = mp.solutions.hands.Hands()
img = cv2.imread('board.jpg')
res = hands.process(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
if res.multi_hand_landmarks:
    idx = res.multi_hand_landmarks[0].landmark[8]
    h,w,_ = img.shape
    cv2.circle(img,(int(idx.x*w), int(idx.y*h)), 3, (0,0,255), -1)

  1. 抬头率实时统计

Python

复制

import insightface, cv2
handler = insightface.model_zoo.get_model('scrfd_10g')
handler.prepare(ctx_id=0, input_size=(640,640))
cap = cv2.VideoCapture(0)
total = 0
looking = 0
while True:
    ok, f = cap.read()
    bboxes, kpss = handler.detect(f)
    for kps in kpss:
        total += 1
        dx = kps[0][0]-kps[1][0]
        dy = kps[0][1]-kps[1][1]
        yaw = math.atan2(dx, dy)
        if abs(yaw) < 0.3: looking += 1
    rate = looking/max(total,1)
    print(f'抬头率: {rate:.2%}')

  1. 考场交头接耳

Python

复制

from ultralytics import YOLO
model = YOLO('yolov8n.pt')
cap = cv2.VideoCapture(0)
while True:
    _, f = cap.read()
    res = model(f)[0]
    heads = res.boxes[res.boxes.cls==0].xyxy.cpu()
    for i in range(len(heads)):
        for j in range(i+1, len(heads)):
            x1,y1,x2,y2 = heads[i]
            x3,y3,x4,y4 = heads[j]
            cx1, cy1 = (x1+x2)/2, (y1+y2)/2
            cx2, cy2 = (x3+x4)/2, (y3+y4)/2
            dist = ((cx1-cx2)**2 + (cy1-cy2)**2)**0.5
            if dist < 100:
                cv2.rectangle(f,(int(x1),int(y1)),(int(x2),int(y2)),(0,0,255),2)
    cv2.imshow('talk', f)
    if cv2.waitKey(1)==27: break

  1. 空座/实座检测

Python

复制

from ultralytics import YOLO
model = YOLO('yolov8n.pt')
img = cv2.imread('classroom.jpg')
res = model(img)[0]
chairs = res.boxes[res.boxes.cls==56].xyxy.cpu()  # chair id
persons = res.boxes[res.boxes.cls==0].xyxy.cpu()
for cx1,cy1,cx2,cy2 in chairs:
    occupied = False
    for px1,py1,px2,py2 in persons:
        if max(cx1,px1) < min(cx2,px2) and max(cy1,py1) < min(cy2,py2):
            occupied = True
            break
    color = (0,0,255) if occupied else (0,255,0)
    cv2.rectangle(img,(int(cx1),int(cy1)),(int(cx2),int(cy2)),color,2)

  1. 投影仪开关状态

Python

复制

# 用 HSV 阈值判断投影区域是否亮
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, (100,50,200), (130,255,255))
ratio = cv2.countNonZero(mask) / mask.size
on = ratio > 0.05
print('projector', 'ON' if on else 'OFF')

  1. 奔跑/跌倒检测

Python

复制

from ultralytics import YOLO
model = YOLO('yolov8n-pose.pt')
prev = None
while True:
    _, f = cap.read()
    res = model(f, verbose=False)[0]
    for k in res.keypoints:
        nose = k[0]
        if prev is not None:
            speed = abs(nose[0]-prev[0])+abs(nose[1]-prev[1])
            if speed > 50: print('running')
            if nose[1] > prev[1]+100: print('fall')
        prev = nose

  1. 心率非接触估计(RGB-PPG)

Python

复制

import numpy as np
face = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
cap = cv2.VideoCapture(0)
sig = []
while True:
    _, f = cap.read()
    x,y,w,h = face.detectMultiScale(f,1.3,5)[0]
    roi = f[y:y+h, x:x+w, 1]  # 用绿色通道
    sig.append(np.mean(roi))
    if len(sig) > 300:
        fft = np.abs(np.fft.fft(sig[-300:]))
        hr = np.argmax(fft[6:36]) * 60/5
        print('HR:', int(hr))
        sig.pop(0)

  1. 情绪识别(7 类)

Python

from fer import FER
detector = FER()
img = cv2.imread('student.jpg')
emotion, score = detector.top_emotion(img)
print(emotion, score)

代码参考

智慧教室CV检测技术 - DeepBlog

https://deepblog.net/p/0d9048f294a7482eaf6b523f32351565?u=csdn_matlab_python22&utm_source=share_project&f=link

yuny j               运行截图

本文章已经生成可运行项目
基于遗传算法的新的异构分布式系统任务调度算法研究(Matlab代码实现)内容概要:本文档围绕基于遗传算法的异构分布式系统任务调度算法展开研究,重点介绍了一种结合遗传算法的新颖优化方法,并通过Matlab代码实现验证其在复杂调度问题中的有效性。文中还涵盖了多种智能优化算法在生产调度、经济调度、车间调度、无人机路径规划、微电网优化等领域的应用案例,展示了从理论建模到仿真实现的完整流程。此外,文档系统梳理了智能优化、机器学习、路径规划、电力系统管理等多个科研方向的技术体系与实际应用场景,强调“借力”工具与创新思维在科研中的重要性。; 适合人群:具备一定Matlab编程基础,从事智能优化、自动化、电力系统、控制工程等相关领域研究的研究生及科研人员,尤其适合正在开展调度优化、路径规划或算法改进类课题的研究者; 使用场景及目标:①学习遗传算法及其他智能优化算法(如粒子群、蜣螂优化、NSGA等)在任务调度中的设计与实现;②掌握Matlab/Simulink在科研仿真中的综合应用;③获取多领域(如微电网、无人机、车间调度)的算法复现与创新思路; 阅读建议:建议按目录顺序系统浏览,重点关注算法原理与代码实现的对应关系,结合提供的网盘资源下载完整代码进行调试与复现,同时注重从已有案例中提炼可迁移的科研方法与创新路径。
【微电网】【创新点】基于非支配排序的蜣螂优化算法NSDBO求解微电网多目标优化调度研究(Matlab代码实现)内容概要:本文提出了一种基于非支配排序的蜣螂优化算法(NSDBO),用于求解微电网多目标优化调度问题。该方法结合非支配排序机制,提升了传统蜣螂优化算法在处理多目标问题时的收敛性和分布性,有效解决了微电网调度中经济成本、碳排放、能源利用率等多个相互冲突目标的优化难题。研究构建了包含风、光、储能等多种分布式能源的微电网模型,并通过Matlab代码实现算法仿真,验证了NSDBO在寻找帕累托最优解集方面的优越性能,相较于其他多目标优化算法表现出更强的搜索能力和稳定性。; 适合人群:具备一定电力系统或优化算法基础,从事新能源、微电网、智能优化等相关领域研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①应用于微电网能量管理系统的多目标优化调度设计;②作为新型智能优化算法的研究与改进基础,用于解决复杂的多目标工程优化问题;③帮助理解非支配排序机制在进化算法中的集成方法及其在实际系统中的仿真实现。; 阅读建议:建议读者结合Matlab代码深入理解算法实现细节,重点关注非支配排序、拥挤度计算和蜣螂行为模拟的结合方式,并可通过替换目标函数或系统参数进行扩展实验,以掌握算法的适应性与调参技巧。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

交通上的硅基思维

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值