2024年研究生数学建模“华为杯”E题——肘部法则、k-means聚类、目标检测(python)、ARIMA、逻辑回归、混淆矩阵(附:目标检测代码)
文章目录
- 一、情况介绍
- 二、思路情况
- 二、代码展示
- 三、感受
一、情况介绍
前几天也是参加了研究生数学建模竞赛(也就是华为杯),也是和本校的两个数学学院的朋友在网上组的队伍。昨天(9.25)通宵干完论文(一条烂命就是干!),我们选择的是E题,题目内容简单点就是,高速公路上可能会堵车,对堵车情况进行预测,并且启动应急车道舒缓交通压力,通过的是4个监测点的视频。
emmm,这次比赛我想法是,队长担任模型,我是负责出图、做软件(知道我的人应该清楚,我本科就是软件),结果比赛第二天下午的时候,论文进展几乎为0,我就清楚了,这个队伍没有一个适合的模型,然后我就上了,当一次“不正规”的模型(至于为什么不正规,我下面会讲,不过也是圆梦了我本科的一个想法,就是软件转模型,当一次模型)
二、思路情况
这里我就不展示我的摘要了,哈哈哈哈,当然我的摘要,也是被指导老师夸奖了。
这里我说下思路情况:
对于第一问:第一小问首先就是数据获取,和队友讨论下,确定了三个参数:车流量、车速、车流密度。(原本是想继续加的,但是实在想不到什么比较好的参数),嗯花了2天时间数据才处理完成,中间磕磕绊绊,出的数据都不符合实际,最后准备造的时候,发现出的数据正常了,然后出成折线图,对各个时间段的各参数的情况进行分析。比如什么时段到什么时段车速多少、车密度和之前对比怎么样。第二小问,首先使用肘部法则,确定聚类数(我看下图,确定为5),之后使用k-means聚类聚5类,最后按照数据使用ARIMA时间序列模型,预测5分钟的数据。第三小问,使用交叉验证验证有效性。
对于第二问:先说下,对于这题重点是,提供理论依据,想了下又需要第一问产生关联,所以就是介绍了基于时间序列预测结果的逻辑回归模型,并且都是二分类问题,把理论依据给说明了
对于第三问:第一小问,就是首先用k-means聚类两类,之后使用逻辑回归,两者进行对比。第二小问,量化的话,我们使用了混淆矩阵,放入k-means作为真实值,逻辑回归作为预测值,带入混淆矩阵。
对于第四问:我乍一看,再一分析,我以为是最优化问题,有目标函数,也有约束条件。但是约束条件太空洞了。没有什么预算之类的,反正我没想到怎么搞,最后是确定预测精度随着时间的变化,确定安装视频监控点的位置。
二、代码展示
对于问题一,使用yolov5算法对视频数据进行检测
import torch
import cv2
import numpy as np
import matplotlib.pyplot as plt
import os
import pandas as pd
from scipy.spatial import distance
import warnings
# 忽略特定的FutureWarning
warnings.filterwarnings('ignore', category=FutureWarning)
# 加载YOLOv5模型
model = torch.hub.load('ultralytics/yolov5', 'yolov5s', pretrained=True)
# 定义观测区域的长度(单位:公里),假设为50米
observation_length = 0.05 # 50米等于0.05公里
# 初始化OpenCV的多目标追踪器
tracker = cv2.legacy.MultiTracker_create()
# 追踪车辆的结构
class Vehicle:
def __init__(self, bbox, tracker):
self.bbox = bbox # 车辆的检测框
self.tracker = tracker # 对应的追踪器
self.positions = [] # 存储每帧车辆中心的位置
def add_position(self, center):
self.positions.append(center)
def calculate_speed(self, fps):
# 如果追踪到的点少于2个,无法计算速度
if len(self.positions) < 2:
return 0
# 计算速度,根据前后位置和时间差
prev_center = self.positions[-2]
current_center = self.positions[-1]
pixel_distance = distance.euclidean(prev_center, current_center)
speed = (pixel_distance / 1000) * fps / observation_length # km/h
return speed
# 初始化车辆列表
vehicles = []
def process_video_yolov5(video_path):
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
print(f"无法打开视频文件 {video_path}")
return
fps = int(cap.get(cv2.CAP_PROP_FPS)) # 获取视频帧率
frame_count = 0
vehicle_count_per_25_frames = [] # 每25帧的车辆总数
speed_per_25_frames = [] # 每25帧通过蓝线的车辆平均速度
density_per_25_frames = [] # 每25帧的车辆密度(蓝线以下车辆数)
total_vehicle_count = 0
# 获取视频帧的宽度和高度,用于绘制蓝线
frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
blue_line_y = int(frame_height * 0.25) # 在视频四分之一处画一根水平蓝线
while True:
ret, frame = cap.read()
if not ret:
break
# 每25帧重新检测一次车辆,并更新跟踪器
if frame_count % 25 == 0:
# 使用YOLOv5进行车辆检测
results = model(frame)
detections = results.xyxy[0].cpu().numpy() # [x1, y1, x2, y2, conf, cls]
# 清空旧的跟踪器并添加新检测到的车辆
vehicles.clear()
for *box, conf, cls in detections:
if int(cls) in [2, 3, 5, 7]: # 汽车, 卡车等车辆类
x1, y1, x2, y2 = map(int, box)
bbox = (x1, y1, x2 - x1, y2 - y1) # 计算检测框
tracker = cv2.TrackerCSRT_create() # 使用CSRT追踪器
tracker.init(frame, bbox)
vehicle = Vehicle(bbox, tracker)
vehicles.append(vehicle)
total_vehicle_count += 1
else:
# 更新车辆的追踪位置
for vehicle in vehicles:
success, bbox = vehicle.tracker.update(frame)
if success:
# 计算中心点并保存
x, y, w, h = map(int, bbox)
center_x, center_y = (x + x + w) // 2, (y + y + h) // 2
vehicle.add_position((center_x, center_y))
# 绘制蓝线
cv2.line(frame, (0, blue_line_y), (frame_width, blue_line_y), (255, 0, 0), 2)
frame_count += 1
# 每25帧,计算一次车辆数量、通过蓝线的车辆平均速度和车辆密度
if frame_count % 25 == 0:
# 统计当前帧的车辆数
current_vehicle_count = len(vehicles)
vehicle_count_per_25_frames.append(current_vehicle_count)
# 计算每辆车的平均速度
speeds = [vehicle.calculate_speed(fps) for vehicle in vehicles]
avg_speed = np.mean(speeds) if speeds else 0
speed_per_25_frames.append(avg_speed)
# 计算蓝线以下的车辆密度
vehicles_below_line = sum(1 for vehicle in vehicles if vehicle.bbox[1] > blue_line_y)
density_per_25_frames.append(vehicles_below_line)
print(f"每25帧车辆总数: {current_vehicle_count}, 平均速度: {avg_speed} km/h, 蓝线以下车辆数: {vehicles_below_line}")
# 显示结果
cv2.imshow('Vehicle Detection with YOLOv5', frame)
# 按 'q' 键退出
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
print(f"视频 {video_path} 处理完成,车辆总数: {total_vehicle_count}")
return vehicle_count_per_25_frames, speed_per_25_frames, density_per_25_frames
# 绘制并保存车流密度、流量、速度的折线图到桌面,并生成Excel文件
def save_to_excel_and_plot(vehicle_counts, speed_counts, density_counts):
time_points = list(range(1, len(vehicle_counts) + 1)) # X轴为每25帧的时间点
# 创建DataFrame
df = pd.DataFrame({
'Time (every 25 frames)': time_points,
'Vehicle Count (Flow)': vehicle_counts,
'Average Speed (km/h)': speed_counts,
'Vehicle Density (below blue line)': density_counts # 添加蓝线以下车辆密度列
})
# 获取桌面路径
desktop_path = os.path.join(os.path.join(os.environ['USERPROFILE']), 'Desktop') # Windows
save_dir = desktop_path
if not os.path.exists(save_dir):
os.makedirs(save_dir)
# 保存Excel文件
excel_path = os.path.join(save_dir, 'vehicle_data_with_density.xlsx')
df.to_excel(excel_path, index=False)
print(f"数据已保存到 {excel_path}")
# 绘制折线图
plt.figure(figsize=(10, 6))
# 绘制车辆流量
plt.plot(time_points, vehicle_counts, marker='o', linestyle='-', color='b', label='Vehicle Count')
# 绘制平均速度
plt.plot(time_points, speed_counts, marker='s', linestyle='-', color='g', label='Average Speed')
# 绘制蓝线以下车辆密度
plt.plot(time_points, density_counts, marker='^', linestyle='-', color='r', label='Vehicle Density (below blue line)')
plt.title('Vehicle Data over Time (every 25 frames)')
plt.xlabel('Time (every 25 frames)')
plt.ylabel('Value')
plt.grid(True)
plt.legend()
# 保存图表到桌面
save_path = os.path.join(save_dir, 'vehicle_data_with_density_plot.png')
plt.savefig(save_path)
print(f"图表已保存到 {save_path}")
plt.show()
# 处理视频
video_paths = ['20240501_20240501135236_20240501160912_135235.mp4']
for video_path in video_paths:
vehicle_counts, speed_counts, density_counts = process_video_yolov5(video_path)
save_to_excel_and_plot(vehicle_counts, speed_counts, density_counts)
下面这个代码是对于之前的物体识别进行的初稿,出的效果很好看
import torch
import cv2
import numpy as np
# 加载YOLOv5模型
model = torch.hub.load('ultralytics/yolov5', 'yolov5s', pretrained=True)
def process_video_yolov5(video_path):
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
print(f"无法打开视频文件 {video_path}")
return
vehicle_count = 0
while True:
ret, frame = cap.read()
if not ret:
break
# 使用YOLOv5进行检测
results = model(frame)
# 解析检测结果,results.pandas().xyxy 返回检测结果的 DataFrame
detections = results.xyxy[0].cpu().numpy() # [x1, y1, x2, y2, conf, cls]
for *box, conf, cls in detections:
# 检测类别ID,2: 汽车, 3: 摩托车, 5: 公共汽车, 7: 卡车
if int(cls) in [2, 3, 5, 7]:
x1, y1, x2, y2 = map(int, box)
vehicle_count += 1
# 在图像上绘制检测框
cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
# 显示结果
cv2.imshow('Vehicle Detection with YOLOv5', frame)
# 按 'q' 键退出
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
print(f"视频 {video_path} 处理完成,车辆总数: {vehicle_count}")
# 处理视频
video_paths = ['20240501_20240501135236_20240501160912_135235.mp4']
for video_path in video_paths:
process_video_yolov5(video_path)
两者结合就是下面这个
三、感受
比赛真的是很锻炼人的能力,但是也是收获颇丰。
首先就是遇到两个很好很好的朋友,zxz,lsy,两个队友,哈哈哈哈哈感觉,比完赛感觉我们都认识三年了一样,很熟了。
其次就是我安装完成了torch、pycharm、以及虚拟环境的概念的、库的安装(这一点真的很关键,因为我也在想我发论文准备看着机器视觉的方向发展,而且我之前的深度之眼的专栏,也是到了安装anacoda、pycharm、cuda(原来我的电脑nvidia本来就有)、ptorch,之后就卡住了,因为感觉分不清他们,几个怕安装错了
然后,我队友也是帮我安装好类似公式编辑器的东西,还有一个公式识别的网站,两个搭配起来真的超级好用。
还有就是论文写作,也是相应的锻炼了自己论文写作能力把,也是完成了本科想当模型的梦想。哈哈哈哈哈,不想当模型的软件,不是好软件。也是写了数学建模方向的第一次的摘要,写的也是十分的充实,很开心的被认可了。(唯一的遗憾就是流程图、图啥的几乎是没有的,时间太少了)
最后,chatgpt真是神器,需要什么样的代码跟他说就行,报了什么错问他就行,提出问题,让他给出模型,,给他数据也能按照你的要求进行相应的处理,出图、分析,很牛逼。