精彩专栏推荐订阅:在下方主页👇🏻👇🏻👇🏻👇🏻
💖🔥作者主页 :计算机毕设木哥🔥 💖
文章目录
- 一、项目介绍
- 二、视频展示
- 三、开发环境
- 四、系统展示
- 五、代码展示
- 六、项目文档展示
- 七、项目总结
- [<font color=#fe2c24 >大家可以帮忙点赞、收藏、关注、评论啦 👇🏻](#fe2c24 >大家可以帮忙点赞、收藏、关注、评论啦 👇🏻)
一、项目介绍
电梯轿厢空间狭小,电动车一旦被推进楼道或电梯,起火后逃生通道很快被堵住,物业靠人盯监控很难做到全天候。普通监控只能看到画面,无法自动区分自行车、电瓶车和人员,事后追溯也要一帧一帧翻录像。本文通过开发一个基于深度学习的电梯电动车违规进入检测系统,用以帮助解决电梯场景下违规车辆识别与记录追溯的问题。
系统以 YOLO26m 模型为核心,用 SGD 优化器配合 Mosaic、HSV、翻转等增强,导出 ONNX 后由 onnxruntime 在 Django 后端完成推理,前端用 Vue 与 ECharts 呈现。后端按 preprocess.json 做 letterbox 预处理,对输出解码、NMS 和坐标反算,再用 Energy Score 对域外图片做拒识;图片页支持多图队列与三并发,视频页按时间抽帧并把帧与框合成到同一画布,摄像头页接实时流逐帧识别,结果连同类别、置信度、耗时写入历史表,聚合出小时趋势、类别分布、置信度与星期热力五张大屏图表。
经过图片、视频、摄像头三条链路和不同光照、遮挡样张的测试,本系统能满足物业值班人员、小区管理员和住户的日常使用需求。值班人员可以快速确认违规画面并留存证据,管理员能从历史记录里回看高频时段与车辆类别,住户也能通过识别结果了解电梯内的通行情况。系统把模型训练、推理部署和业务管理串成一条线,为电梯电动车治理提供了一个可落地、可复盘的辅助工具。
二、视频展示
基于深度学习的电梯电动车违规进入检测研究
三、开发环境
模型与训练:YOLO26m 目标检测模型,PyTorch,Ultralytics,SGD 优化器,Mosaic / HSV / 翻转数据增强
图像与数据处理:OpenCV,NumPy,Pandas,scikit-learn,cleanlab(数据错标审计)
模型导出与推理:ONNX,onnxruntime,NMS 在后端运行时执行
后端:Django,PyMySQL,django-cors-headers,MySQL(utf8mb4)
前端:Vue,Element UI,ECharts,axios
运行与构建:Node.js
四、系统展示
系统页面模块展示:






模型结果图:







五、代码展示
bash
def load_decisions():
"""读 lr_finder.json 的 decisions ------ 正式训练配置的唯一来源(#154 落点 c)。"""
import json
if not os.path.isfile(LR_FINDER_JSON):
log(f"[main] FAIL:短扫产物不存在 {LR_FINDER_JSON} ------ 正式训练前必跑一次短扫(#35)")
sys.exit(1)
with open(LR_FINDER_JSON, encoding="utf-8") as f:
jd = json.load(f)
dec = jd.get("decisions")
if not isinstance(dec, dict) or not dec.get("optimizer"):
log("[main] FAIL:lr_finder.json 缺 decisions.optimizer ------ 短扫未定稿,不准开正式训练")
sys.exit(1)
return jd, dec
def load_bench():
"""读 bench 实测定稿(batch / grad_ckpt / imgsz / amp)------短扫与正式同配置。"""
import json
with open(BENCH_JSON, encoding="utf-8") as f:
return json.load(f)
def dataset_hash():
"""数据集指纹(防 best.pt 换数据集)------split 文件 + 标签目录 md5。"""
import hashlib
h = hashlib.md5()
h.update(DATA_YAML.encode("utf-8"))
labels_dir = os.path.join(MODEL_TRAINING, "data", "labels")
if os.path.isdir(labels_dir):
for name in sorted(os.listdir(labels_dir)):
if name.endswith(".txt") and name.split(".")[0] in {"train", "val", "test"}:
h.update(name.encode("utf-8"))
with open(os.path.join(labels_dir, name), "rb") as f:
h.update(f.read())
return h.hexdigest()[:12]
python
def letterbox(im, new_shape, stride, pad_value, center, scaleup, interpolation):
"""LetterBox 缩放填充:与训练侧预处理保持同一种几何变换。"""
import cv2
h, w = im.shape[:2]
r = min(new_shape[0] / h, new_shape[1] / w)
if not scaleup:
r = min(r, 1.0)
new_unpad = (int(round(w * r)), int(round(h * r)))
if (w, h) != new_unpad:
im = cv2.resize(im, new_unpad, interpolation=interpolation)
dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1]
if center:
top, bottom = dh // 2, dh - dh // 2
left, right = dw // 2, dw - dw // 2
else:
top, bottom = 0, dh
left, right = 0, dw
im = cv2.copyMakeBorder(im, top, bottom, left, right, cv2.BORDER_CONSTANT, value=(pad_value, pad_value, pad_value))
return im, r, (left, top)
def preprocess(img_bgr, cfg):
"""按该模型 preprocess.json 逐字段复刻训练侧预处理:BGR → LetterBox → RGB → /255 → NCHW。
返回 (tensor, meta):meta 带 letterbox 的缩放比 r 与左/上填充 (left, top),
供后处理把检测框从网络输入空间反算回原图像素坐标(不回算则框整体偏移)。
"""
import cv2
size = cfg.get('input_size') or [3, cfg.get('imgsz', 512), cfg.get('imgsz', 512)]
h, w = size[1], size[2]
lb = cfg.get('letterbox') or {}
stride = int(lb.get('stride', 32))
scaleup = bool(lb.get('scaleup', True))
center = bool(lb.get('center', True))
pad_value = float(lb.get('padding_value', 114))
interp = cv2.INTER_LINEAR
if not scaleup:
r = min(h / img_bgr.shape[0], w / img_bgr.shape[1])
new_unpad = (int(round(img_bgr.shape[1] * r)), int(round(img_bgr.shape[0] * r)))
dh, dw = h - new_unpad[1], w - new_unpad[0]
top, bottom = dh // 2, dh - dh // 2
left, right = dw // 2, dw - dw // 2
img_bgr = cv2.copyMakeBorder(img_bgr, top, bottom, left, right,
cv2.BORDER_CONSTANT, value=(pad_value, pad_value, pad_value))
else:
img_bgr, r, (left, top) = letterbox(img_bgr, (h, w), stride, pad_value, center, scaleup, interp)
if (img_bgr.shape[0] != h) or (img_bgr.shape[1] != w):
img_bgr = cv2.resize(img_bgr, (w, h), interpolation=interp)
if str(cfg.get('color_order', 'RGB')).upper() == 'RGB':
img_bgr = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
arr = img_bgr.astype(np.float32) * float(cfg.get('scale', 1.0 / 255))
mean = np.array(cfg.get('mean', [0, 0, 0]), dtype=np.float32)
std = np.array(cfg.get('std', [1, 1, 1]), dtype=np.float32)
arr = (arr - mean) / std
arr = arr.transpose(2, 0, 1)
tensor = np.ascontiguousarray(arr[np.newaxis], dtype=np.float32)
return tensor, {'r': float(r), 'left': int(left), 'top': int(top),
'net_h': int(h), 'net_w': int(w)}
def unletterbox_boxes(boxes, prep):
"""把网络输入空间的 xyxy 框反算回原图像素坐标:x_orig = (x_net - left) / r。"""
if not boxes:
return boxes
r = float(prep.get('r') or 1.0)
if r <= 0:
r = 1.0
left = float(prep.get('left') or 0)
top = float(prep.get('top') or 0)
out = []
for b in boxes:
out.append([(b[0] - left) / r, (b[1] - top) / r,
(b[2] - left) / r, (b[3] - top) / r])
return out
def nms(boxes, scores, iou_thr, max_det):
"""标准非极大值抑制(框为 xyxy 像素坐标)。"""
if len(boxes) == 0:
return []
x1, y1, x2, y2 = boxes[:, 0], boxes[:, 1], boxes[:, 2], boxes[:, 3]
areas = np.clip((x2 - x1) * (y2 - y1), 0, None)
order = scores.argsort()[::-1]
keep = []
while order.size > 0 and len(keep) < max_det:
i = order[0]
keep.append(i)
if order.size == 1:
break
xx1 = np.maximum(x1[i], x1[order[1:]])
yy1 = np.maximum(y1[i], y1[order[1:]])
xx2 = np.minimum(x2[i], x2[order[1:]])
yy2 = np.minimum(y2[i], y2[order[1:]])
inter = np.clip(xx2 - xx1, 0, None) * np.clip(yy2 - yy1, 0, None)
union = areas[i] + areas[order[1:]] - inter
iou = inter / np.clip(union, 1e-9, None)
order = order[1:][iou <= iou_thr]
return keep
def postprocess_detection(out, meta, work_point):
"""原始张量 (B, 4+nc, anchors) → xyxy 像素框 + 类别 + 置信度;NMS 在运行时执行。"""
arr = np.asarray(out, dtype=np.float32)
if arr.ndim == 3:
arr = arr[0]
nc = int(meta.get('num_classes') or 3)
if arr.shape[0] < 4 + nc:
return []
dbox = arr[:4, :]
cls = arr[4:4 + nc, :]
# 分类段已是 sigmoid 概率
if float(np.max(cls)) > 1.0 + 1e-4:
cls = 1.0 / (1.0 + np.exp(-cls))
conf_thr = float(work_point.get('conf', 0.6))
iou_thr = float(work_point.get('iou', 0.45))
max_det = int(work_point.get('max_det', 300))
# dbox 为 cx,cy,w,h(输入图像素空间)
cx, cy, bw, bh = dbox[0], dbox[1], dbox[2], dbox[3]
x1 = (cx - bw / 2.0)
y1 = (cy - bh / 2.0)
x2 = (cx + bw / 2.0)
y2 = (cy + bh / 2.0)
out_rows = []
for ci in range(nc):
scores = cls[ci]
mask = scores >= conf_thr
if not np.any(mask):
continue
idx = np.nonzero(mask)[0]
b = np.stack([x1[idx], y1[idx], x2[idx], y2[idx]], axis=1)
s = scores[idx]
keep = nms(b, s, iou_thr, max_det)
for k in keep:
out_rows.append({'cls': ci, 'conf': float(s[k]),
'bbox': [float(b[k][0]), float(b[k][1]), float(b[k][2]), float(b[k][3])]})
out_rows.sort(key=lambda r: r['conf'], reverse=True)
return out_rows[:max_det]
六、项目文档展示

七、项目总结
本课题面向电梯轿厢内电动车违规进入的治理场景,使用 5461 张电梯监控图片、11676 个标注实例训练 YOLO26m 检测模型,按自行车、电瓶车、人员三类输出检测框与置信度,并以 conf=0.6、iou=0.45 为工作点完成 NMS,导出 ONNX 后接入 Django 后端完成预处理、推理、坐标反算与域外拒识。系统前端用 Vue 与 ECharts 实现图片多图并发识别、视频抽帧帧同步合成、摄像头实时识别,识别结果写入历史表并按账号分流,支持类别筛选、详情查看与软删除,大屏从真实记录聚合小时趋势、类别分布、置信度与星期热力五张图。测试表明该方案能把模型训练、部署推理和业务管理串成闭环,为物业值班人员、小区管理员和住户提供可追溯的辅助手段,也为同类电梯场景的视觉检测落地提供参考
大家可以帮忙点赞、收藏、关注、评论啦 👇🏻
💖🔥作者主页 :计算机毕设木哥🔥 💖