【OpenCV】场景中人的识别与前端计数

ops/2024/9/24 16:27:44/

1.OpenCV代码设计讲解

突发奇想,搞个摄像头,识别一下实验室里面有几个人,计数一下(最终代码是累加计数,没有优化),拿OpenCV来玩一玩

首先,还是优先启动电脑摄像头,本项目将使用SSD进行人体检测。
deploy.prototxt与mobilenet_iter_73000.caffemodel自行从官网下载。

完整代码如下:

import cv2
import numpy as np# 加载MobileNet-SSD模型
net = cv2.dnn.readNetFromCaffe('deploy.prototxt', 'mobilenet_iter_73000.caffemodel')# 打开摄像头
cap = cv2.VideoCapture(0)while cap.isOpened():ret, frame = cap.read()if ret:# 创建blob,预处理输入数据# cv2.resize(frame, (300, 300)),将输入的 frame 图像调整为 300x300 像素的大小# 神经网络通常要求输入图像的尺寸固定,因此需要调整图像大小# frame:要处理的原始图像,(300, 300):目标图像的宽度和高度# 0.007843:这是一个缩放因子,用于将像素值缩放到适合神经网络的范围。# 127.5:是一个均值值,用于对像素进行归一化处理,使用 Caffe 训练的模型,输入图像通常会减去均值以中心化数据。blob = cv2.dnn.blobFromImage(cv2.resize(frame, (300, 300)), 0.007843, (300, 300), 127.5)net.setInput(blob)# 进行前向传播,得到检测结果detections = net.forward()# 获取帧的宽度和高度# 在 OpenCV 中,图像通常被表示为一个 NumPy 数组,其维度是 (height, width, channels)(h, w) = frame.shape[:2]# 具体结构由 cv2.dnn 模块的输出格式决定。# 对于物体检测任务,网络输出的数据维度一般是一个 4D 的张量 (NumPy 数组),其形状大致为(1, 1, N, 7)# 1 (Batch size):处理的图像批次数量,通常为 1,因为我们处理的是单张图像。# 1 (Number of classes):如果是单类检测,这里也是 1,表示输出的类别信息(可能是分类器的层数)。# N (Number of detections):这是该图像中检测到的对象数量。例如,网络可能检测到 100 个对象,N 就是 100。# 7 (Detection details):每个检测结果包含 7 个值,分别表示:# 第1个值: 检测结果的标签索引(通常是类别ID)。# 第2个值: 置信度 (confidence score)。# 第3-7个值: 边界框的坐标 (bounding box coordinates: x1, y1, x2, y2)。# 遍历检测结果for i in range(detections.shape[2]):confidence = detections[0, 0, i, 2]# 设置最低置信度阈值if confidence > 0.4:idx = int(detections[0, 0, i, 1])# 只检测人类(index 15是人类类别)if idx == 15:box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])(startX, startY, endX, endY) = box.astype("int")# 画框标记人类cv2.rectangle(frame, (startX, startY), (endX, endY), (0, 255, 0), 2)# 显示结果cv2.imshow("Human Detection", frame)# 按 'q' 键退出if cv2.waitKey(1) & 0xFF == ord('q'):breakelse:breakcap.release()
cv2.destroyAllWindows()

2.升级一下

给判定到的人数记录下来,因为不是什么重要数据,拿redis存一下,然后通过前端展示出来,效果如下:

在这里插入图片描述

代码如下:

import cv2
import numpy as np
import redis
from flask import Flask, jsonify
from flask_cors import CORS  # 引入 CORS# 初始化 Flask 应用
app = Flask(__name__)
CORS(app)  # 启用 CORS 支持# 连接 Redis 服务器
r = redis.Redis(host='localhost', port=6379, db=0)# 加载 MobileNet-SSD 模型
net = cv2.dnn.readNetFromCaffe('deploy.prototxt', 'mobilenet_iter_73000.caffemodel')# 摄像头检测函数
def detect_humans():cap = cv2.VideoCapture(0)while cap.isOpened():ret, frame = cap.read()if ret:# 创建 blob,预处理输入数据blob = cv2.dnn.blobFromImage(cv2.resize(frame, (300, 300)), 0.007843, (300, 300), 127.5)net.setInput(blob)# 进行前向传播,得到检测结果detections = net.forward()# 获取帧的宽度和高度(h, w) = frame.shape[:2]# 初始化检测到的人数human_count = 0# 遍历检测结果for i in range(detections.shape[2]):confidence = detections[0, 0, i, 2]# 设置最低置信度阈值if confidence > 0.4:idx = int(detections[0, 0, i, 1])# 只检测人类(index 15 是人类类别)if idx == 15:human_count += 1  # 增加检测到的人数box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])(startX, startY, endX, endY) = box.astype("int")# 画框标记人类cv2.rectangle(frame, (startX, startY), (endX, endY), (0, 255, 0), 2)# 将人数写入 Redis 缓存r.set('human_count', human_count)# 显示结果cv2.imshow("Human Detection", frame)# 按 'q' 键退出if cv2.waitKey(1) & 0xFF == ord('q'):breakelse:breakcap.release()cv2.destroyAllWindows()# 创建一个 API 路由来返回 Redis 中的人数
@app.route('/get_human_count', methods=['GET'])
def get_human_count():# 从 Redis 中获取人数human_count = r.get('human_count')if human_count:human_count = int(human_count)else:human_count = 0return jsonify({'human_count': human_count})if __name__ == '__main__':# 启动摄像头检测from threading import Threadt = Thread(target=detect_humans)t.daemon = Truet.start()# 启动 Flask 服务app.run(debug=True, host='0.0.0.0')

前端代码是Online Tutorials上学来的,自己稍微改了一下html的部分:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Human Detection Count</title><link rel="stylesheet" href="human_style.css">
</head>
<body><div class="card"><div class="rating"><h2><span class="counter" data-target="0">0</span><sup>%</sup><br>Human</h2><div class="block"></div></div></div><script>// 初始化区块const rating = document.getElementsByClassName('rating')[0];const block = document.getElementsByClassName('block');for (let i = 1; i < 100; i++) {rating.innerHTML += "<div class='block'></div>";block[i].style.transform = "rotate(" + 3.6 * i + "deg)";block[i].style.animationDelay = `${i / 40}s`;}// 获取 Redis 中的人数并更新function fetchHumanCount() {fetch('http://127.0.0.1:5000/get_human_count')  // 后端 API 路由.then(response => response.json()).then(data => {const counter = document.querySelector('.counter');const target = data.human_count;  // 获取 Redis 中的值counter.setAttribute('data-target', target);// 启动计数器const NumberCounter = () => {const value = +counter.innerText;if (value < target) {counter.innerText = Math.ceil(value + 1);setTimeout(() => {NumberCounter();}, 25);}};NumberCounter();}).catch(error => console.error('Error fetching human count:', error));}// 每秒更新一次人数setInterval(fetchHumanCount, 1000);</script>
</body>
</html>

css全是Online Tutorials大佬写的,跟着视频敲了一遍:

@import url('https://fonts.googleapis.com/css?family=Poppins:200,300,400,500,600,700,800,900&display=swap');
*
{margin: 0;padding: 0%;box-sizing: border-box;font-family: 'Poppins', sans-serif;
}
body
{display: flex;justify-content: center;align-items: center;min-height: 100vh;background: radial-gradient(#444,#222);}
.card
{position: relative;width: 200px;height: 200px;}
.card .rating
{position: relative;width: 100%;height: 100%;/* background: rgba(107, 170, 221, 0.2); */
}
.card .rating .block
{position: absolute;width: 2px;height: 15px;background: #000;left: 50%;transform-origin: 50% 100px;opacity: 0;animation: animate 0.1s linear forwards;
}
.card .rating .block:nth-child(-n+90)
{background:#0f0;box-shadow: 0 0 15px #0f0,0 0 30px #0f0;
}
@keyframes animate {to{opacity: 1;}
}.card .rating h2{position: absolute;top:50%;left: 50%;transform:translate(-50%,-50%);color: #fff;font-size: 1.2em;font-weight: 500;text-align: center;line-height: 1.5em;
}.card .rating h2 span{font-size: 2.5em;font-weight: 700;
}.card .rating h2 sup
{font-size: 1.5em;font-weight: 300;
}

今天就先不写详解了,歇会~


http://www.ppmy.cn/ops/115357.html

相关文章

Safari-常用快捷键(IPadOS版本)

cmd1 切换数字对应的的标签页cmdL 跳转到地址栏cmdf 页内搜索cmdr 重新载入当前页(刷新)cmdd 当前页加入书签cmdctrl1/2/3 显示书签/阅读列表/浏览记录cmdt 新增标签页cmdw 关闭当前页(感谢csdn自动保存功能)cmd“/-” 放大/缩小页面cmdoptb 左侧打开书签并进入编辑模式cmdshif…

网安新声 | 黎巴嫩BP机爆炸事件带来的安全新挑战与反思

网安加社区【网安新声】栏目&#xff0c;汇聚网络安全领域的权威专家与资深学者&#xff0c;紧跟当下热点安全事件、剖析前沿技术动态及政策导向&#xff0c;以专业视野和前瞻洞察&#xff0c;引领行业共同探讨并应对新挑战的策略与可行路径。 9月17日&#xff0c;黎巴嫩境内发…

责任链模式实现规则校验

1、项目中责任链模式实战 我们使用责任链模式实现对订单中参数的校验&#xff0c;首先校验订单id是否为空&#xff0c;然后校验下单人是否为空&#xff0c;最后检验收获地址是否为空。业务的流程图如下所示&#xff1a; 针对上述的业务&#xff0c;我们使用责任链的模式来实现…

信息技术服务认证的获证流程

认证的流程 准备阶段 企业了解认证要求&#xff0c;确定认证范围和目标。 组建认证工作小组&#xff0c;进行内部培训和宣传。 对企业的信息技术服务管理体系进行自我评估&#xff0c;找出差距和改进方向。 实施阶段 按照认证标准的要求&#xff0c;完善信息技术服务管理体系。…

Go 1.19.4 序列化和反序列化-Day 16

1. 序列化和反序列化 1.1 序列化 1.1.1 什么是序列化 序列化它是一种将程序中的数据结构&#xff08;map、slice、array等&#xff09;或对象状态转换成一系列字节序列的过程&#xff0c;这些字节可以被存储或通过网络发送。 在GO中&#xff0c;序列化通常涉及到将结构体或其…

数模方法论-无约束问题求解

一、基本概念 无约束问题在数学建模中是指优化过程中没有任何限制条件的情况。这种问题旨在寻找一个决策变量集合&#xff0c;使得某个目标函数&#xff08;如成本、效益或其他需要优化的量&#xff09;达到最大或最小值。具体来说&#xff0c;无约束问题通常可以表示为&#x…

CSS中的多种关系选择器

后代选择器 选择所有被E元素包含的F元素&#xff0c;中间用空格隔开。 例&#xff1b; <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title…

南京服务器测评【浪浪云】

前言 优质的服务器对于企业来说无疑是一把快速实现科技化成长的利剑。而南京&#xff0c;作为中国科技龙头之一的城市&#xff0c;也对服务器的需求愈发旺盛。而作为国内领先的云服务商&#xff0c;浪浪云致力于用科技培植企业的成长&#xff0c;其在南京的服务器便是企业数字化…