From 58497783e39655fd354991923fb6136428bab372 Mon Sep 17 00:00:00 2001 From: dacun922 <3109383112@qq.com> Date: Tue, 2 Dec 2025 10:17:43 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E6=8F=90=E4=BA=A4main.py=E5=88=B0nn?= =?UTF-8?q?=E4=BB=93=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 14 +++++ main.py | 181 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+) create mode 100644 .gitignore create mode 100644 main.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..bd7435748d --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +# 视频文件(排除sample.hevc) +../../sample.hevc +*.hevc +*.mp4 +*.avi + +# 备份/临时文件 +*.save +*.old +*.launch + +# 模型目录(若models里有超大文件,先排除;如需提交则用Git LFS) +models/ + diff --git a/main.py b/main.py new file mode 100644 index 0000000000..c87e2fecb2 --- /dev/null +++ b/main.py @@ -0,0 +1,181 @@ +import sys +import os +sys.path.append('/home/dacun/nn/src') + +import numpy as np +import cv2 +from tensorflow.keras.models import load_model +from common.transformations.camera import transform_img, eon_intrinsics +from common.transformations.model import medmodel_intrinsics +from common.tools.lib.parser import parser + +# 关闭TensorFlow所有冗余警告 +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +# -------------------------- 核心工具函数 -------------------------- +def frames_to_tensor(frames): + if len(frames) == 0: + return np.array([]) + H = (frames.shape[1] * 2) // 3 + W = frames.shape[2] + tensor = np.zeros((frames.shape[0], 6, H//2, W//2), dtype=np.float32) + tensor[:, 0] = frames[:, 0:H:2, 0::2] + tensor[:, 1] = frames[:, 1:H:2, 0::2] + tensor[:, 2] = frames[:, 0:H:2, 1::2] + tensor[:, 3] = frames[:, 1:H:2, 1::2] + tensor[:, 4] = frames[:, H:H+H//4].reshape((-1, H//2, W//2)) + tensor[:, 5] = frames[:, H+H//4:H+H//2].reshape((-1, H//2, W//2)) + return tensor / 128.0 - 1.0 + +def preprocess_frames(imgs): + if not imgs: + return np.array([]) + processed = np.zeros((len(imgs), 384, 512), dtype=np.uint8) + for i, img in enumerate(imgs): + try: + processed[i] = transform_img(img, from_intr=eon_intrinsics, to_intr=medmodel_intrinsics, yuv=True, output_size=(512, 256)) + except: + processed[i] = np.zeros((384, 512), dtype=np.uint8) + return frames_to_tensor(processed) + +# -------------------------- 主函数(无参数、全英文、车道线优化) -------------------------- +def main(): + # 1. 初始化显示窗口(800x600,固定尺寸) + win_name = "Lane Line Prediction (Blue=Left | Red=Right | Green=Path)" + cv2.namedWindow(win_name, cv2.WINDOW_NORMAL) + cv2.resizeWindow(win_name, 800, 600) + + # 2. 读取视频(写死路径,无需传参) + video_path = "/home/dacun/nn/sample.hevc" + cap = cv2.VideoCapture(video_path) + if not cap.isOpened(): + empty_frame = np.ones((600, 800, 3), dtype=np.uint8) * 255 + cv2.putText(empty_frame, "Cannot open video", (150, 300), cv2.FONT_HERSHEY_SIMPLEX, 1.5, (0, 0, 255), 3) + cv2.imshow(win_name, empty_frame) + cv2.waitKey(0) + cv2.destroyAllWindows() + return + + # 读取前10帧(用于推理,保留原始帧和模型输入帧) + raw_display_frames = [] # 用于显示的800x600帧 + model_input_imgs = [] # 用于模型的512x384 YUV帧 + for _ in range(10): + ret, frame = cap.read() + if not ret: + break + # 缩放为显示尺寸(800x600) + display_frame = cv2.resize(frame, (800, 600)) + raw_display_frames.append(display_frame) + # 转换为模型需要的YUV格式并缩放 + yuv_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2YUV_I420) + model_frame = cv2.resize(yuv_frame, (512, 384), cv2.INTER_AREA) + model_input_imgs.append(model_frame) + cap.release() + + # 校验帧数是否足够 + if len(raw_display_frames) < 2: + empty_frame = np.ones((600, 800, 3), dtype=np.uint8) * 255 + cv2.putText(empty_frame, "Insufficient video frames", (100, 300), cv2.FONT_HERSHEY_SIMPLEX, 1.2, (0, 0, 255), 3) + cv2.imshow(win_name, empty_frame) + cv2.waitKey(0) + cv2.destroyAllWindows() + return + + # 3. 加载模型(显示英文提示,无乱码) + load_frame = np.ones((600, 800, 3), dtype=np.uint8) * 255 + cv2.putText(load_frame, "Loading model...", (200, 300), cv2.FONT_HERSHEY_SIMPLEX, 1.5, (0, 255, 0), 3) + cv2.imshow(win_name, load_frame) + cv2.waitKey(200) # 刷新显示 + + # 模型路径(写死,无需修改) + model_path = "/home/dacun/桌面/openpilot-modeld-main/models/supercombo.h5" + try: + supercombo_model = load_model(model_path, compile=False) + except Exception as e: + empty_frame = np.ones((600, 800, 3), dtype=np.uint8) * 255 + cv2.putText(empty_frame, "Model load failed", (180, 300), cv2.FONT_HERSHEY_SIMPLEX, 1.5, (0, 0, 255), 3) + cv2.imshow(win_name, empty_frame) + cv2.waitKey(0) + cv2.destroyAllWindows() + return + + # 4. 预处理帧(显示英文提示) + preprocess_frame = np.ones((600, 800, 3), dtype=np.uint8) * 255 + cv2.putText(preprocess_frame, "Preprocessing frames...", (150, 300), cv2.FONT_HERSHEY_SIMPLEX, 1.2, (0, 255, 0), 3) + cv2.imshow(win_name, preprocess_frame) + cv2.waitKey(200) + + frame_tensors = preprocess_frames(model_input_imgs) + if frame_tensors.size == 0: + empty_frame = np.ones((600, 800, 3), dtype=np.uint8) * 255 + cv2.putText(empty_frame, "Preprocessing failed", (180, 300), cv2.FONT_HERSHEY_SIMPLEX, 1.5, (0, 0, 255), 3) + cv2.imshow(win_name, empty_frame) + cv2.waitKey(0) + cv2.destroyAllWindows() + return + + # 5. 模型状态初始化 + model_state = np.zeros((1, 512)) + model_desire = np.zeros((1, 8)) + + # 6. 逐帧推理+绘制(核心优化:车道线右移+放大圆点) + print("✅ Start inference and display (Press Q to exit)") + for i in range(len(frame_tensors) - 1): + # 确保帧存在,避免索引越界 + if i >= len(raw_display_frames): + current_frame = np.ones((600, 800, 3), dtype=np.uint8) * 255 + else: + current_frame = raw_display_frames[i].copy() # 复制原始帧,避免修改 + + try: + # 模型推理(连续两帧作为输入) + input_data = [np.vstack(frame_tensors[i:i+2])[None], model_desire, model_state] + model_output = supercombo_model.predict(input_data, verbose=0) + parsed_result = parser(model_output) + model_state = model_output[-1] + + # -------------------------- 车道线绘制优化 -------------------------- + # 提取模型输出的车道线/路径x坐标 + left_lane_x = parsed_result["lll"][0] + right_lane_x = parsed_result["rll"][0] + path_x = parsed_result["path"][0] + + # 窗口尺寸 + win_h, win_w = 600, 800 + # y坐标映射(0-191 → 0-599) + y_points = np.linspace(0, win_h - 1, 192).astype(int) + # x坐标映射(0-512 → 0-799)+ 右移100像素(解决偏左问题)+ 放大圆点到8px + left_x_mapped = (left_lane_x / 512 * win_w + 100).astype(int) + right_x_mapped = (right_lane_x / 512 * win_w + 100).astype(int) + path_x_mapped = (path_x / 512 * win_w + 100).astype(int) + + # 绘制左车道线(蓝色,8px实心圆) + for x, y in zip(left_x_mapped, y_points): + if 0 <= x < win_w and 0 <= y < win_h: + cv2.circle(current_frame, (x, y), 8, (255, 0, 0), -1) + # 绘制右车道线(红色,8px实心圆) + for x, y in zip(right_x_mapped, y_points): + if 0 <= x < win_w and 0 <= y < win_h: + cv2.circle(current_frame, (x, y), 8, (0, 0, 255), -1) + # 绘制预测路径(绿色,6px实心圆) + for x, y in zip(path_x_mapped, y_points): + if 0 <= x < win_w and 0 <= y < win_h: + cv2.circle(current_frame, (x, y), 6, (0, 255, 0), -1) + + except Exception as e: + # 推理失败时仅打印错误,仍显示原始帧 + print(f"⚠️ Frame {i+1} inference error: {str(e)[:30]}") + + # 强制显示当前帧 + cv2.imshow(win_name, current_frame) + # 按Q退出 + if cv2.waitKey(100) & 0xFF == ord('q'): + print("🛑 Exit by user (Q pressed)") + break + + # 7. 程序收尾 + cv2.destroyAllWindows() + print("🎉 All frames processed successfully!") + +if __name__ == "__main__": + main() From 7b3504ebe24f0c0f2ecc4f3be1cca974ed60ae13 Mon Sep 17 00:00:00 2001 From: dacun922 <3109383112@qq.com> Date: Sun, 7 Dec 2025 03:30:53 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=E9=87=8D=E6=96=B0=E6=8F=90=E4=BA=A4?= =?UTF-8?q?=EF=BC=9Amain.py=E8=BD=A6=E9=81=93=E7=BA=BF=E9=A2=84=E6=B5=8B?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.py | 1 + 1 file changed, 1 insertion(+) diff --git a/main.py b/main.py index c87e2fecb2..6ca99dddd0 100644 --- a/main.py +++ b/main.py @@ -179,3 +179,4 @@ def main(): if __name__ == "__main__": main() +# 重新提交PR:车道线预测核心代码 From c28e40f0b7a3072ace325dc7ae0e1db8289e48bd Mon Sep 17 00:00:00 2001 From: dacun922 <3109383112@qq.com> Date: Sun, 7 Dec 2025 03:33:34 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E6=9B=B4=E6=96=B0main.py=EF=BC=9A=E6=8E=A8?= =?UTF-8?q?=E9=80=81=E5=88=B0nn=E4=BB=93=E5=BA=93main=E5=88=86=E6=94=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000000..789b652c18 --- /dev/null +++ b/README.md @@ -0,0 +1,126 @@ +# Lane Line Prediction with OpenPilot Supercombo Model +基于 OpenPilot 的 supercombo 预训练模型实现视频车道线实时预测,通过 OpenCV 可视化车道线(左车道-蓝、右车道-红、预测路径-绿),适配 Ubuntu 虚拟机环境。 + +## 📌 项目功能 +- 读取 HEVC 格式视频文件,提取帧并预处理为模型输入格式 +- 使用 supercombo 模型推理车道线坐标 +- 基于 OpenCV 实时可视化车道线(放大圆点,解决偏左问题) +- 适配 Ubuntu 虚拟机环境,无 Matplotlib 渲染依赖 +- 完善的异常处理(文件不存在、帧数不足、模型加载失败等) + +## 🛠️ 环境要求 +| 依赖项 | 版本要求 | +|--------|----------| +| Python | 3.8+ (测试环境:3.10) | +| TensorFlow | 2.x | +| OpenCV-Python | 4.5.5.62+ | +| NumPy | 1.21+ | +| Ubuntu | 20.04/22.04 (虚拟机/物理机) | + +## 🚀 快速开始 + +### 1. 克隆仓库(可选) +```bash +git clone <你的GitHub仓库地址> +cd <仓库名称> +``` + +### 2. 创建并激活虚拟环境 +```bash +# 创建虚拟环境 +python3 -m venv venv + +# 激活虚拟环境 +source venv/bin/activate +``` + +### 3. 安装依赖 +```bash +# 安装核心依赖 +pip install tensorflow opencv-python==4.5.5.62 numpy + +# 安装虚拟机显示依赖(可选,解决OpenCV窗口问题) +sudo apt update && sudo apt install -y libgtk-3-dev libgl1-mesa-glx +``` + +### 4. 准备模型和视频文件 +- 将 `supercombo.h5` 模型文件放入路径:`/home/dacun/桌面/openpilot-modeld-main/models/` +- 将 HEVC 格式视频文件(如 `sample.hevc`)放入路径:`/home/dacun/nn/` + +### 5. 运行程序 +```bash +# 进入脚本目录 +cd /home/dacun/nn/src/openpilot_model/ + +# 直接运行(无需传参,路径已写死适配环境) +python3 main.py +``` + +## 📝 使用说明 +1. 程序启动后会依次显示: + - `Loading model...`:模型加载中 + - `Preprocessing frames...`:视频帧预处理中 + - 视频画面 + 车道线可视化(蓝=左车道、红=右车道、绿=预测路径) +2. 按 `Q` 键可退出程序(需先点击视频窗口激活) +3. 终端会输出推理进度和异常信息(如某帧推理失败) + +## 🔍 核心逻辑 +### 1. 视频预处理 +- 将视频帧转换为 YUV420 格式,缩放至 512x384(模型输入尺寸) +- 对帧进行归一化和张量转换,适配 supercombo 模型输入要求 + +### 2. 模型推理 +- 使用连续 2 帧作为模型输入,推理得到车道线 x 坐标(共 192 个点) +- 模型输出坐标映射到视频显示尺寸(800x600),并右移 100 像素解决偏左问题 + +### 3. 可视化 +- 放大车道线圆点(8px),确保肉眼可见 +- 仅依赖 OpenCV 绘制,避免 Matplotlib 虚拟机渲染问题 +- 异常帧保底显示原始视频画面,不中断程序 + +## 📸 效果展示 +> 请替换为你的实际运行截图(虚拟机截屏方法见下文) +![Lane Line Prediction Demo](demo.png) +- 左侧为原始视频帧,右侧为叠加车道线后的效果 +- 蓝色圆点:左车道线;红色圆点:右车道线;绿色圆点:预测行驶路径 + +## ❌ 常见问题解决 +### 1. 窗口显示问号/乱码 +- 原因:Ubuntu 虚拟机缺少中文字体 +- 解决:程序已替换为全英文提示,无需额外配置 + +### 2. 车道线偏左 +- 原因:模型输出坐标范围与视频显示尺寸不匹配 +- 解决:程序已默认将 x 坐标右移 100 像素,可修改 `+100` 调整偏移量 + +### 3. OpenCV 窗口黑屏/无内容 +- 步骤1:启用虚拟机 3D 加速(设置 → 显示 → 勾选 3D 加速) +- 步骤2:重装 OpenCV 依赖: + ```bash + pip uninstall -y opencv-python + pip install opencv-python==4.5.5.62 + ``` +- 步骤3:验证视频文件完整性:`ffplay /home/dacun/nn/sample.hevc` + +### 4. 模型加载失败 +- 检查模型路径:`/home/dacun/桌面/openpilot-modeld-main/models/supercombo.h5` +- 确保模型文件完整(未损坏、未截断) + +## 🖥️ 虚拟机截屏方法 +```bash +# Ubuntu 虚拟机内部截屏 +# 全屏截图 +Print Screen + +# 区域截图 +Shift + Print Screen + +# 保存到剪贴板(当前窗口) +Alt + Print Screen +``` + +## 📄 许可证 +本项目仅用于学习交流,supercombo 模型版权归 OpenPilot 官方所有。 + +## 📧 联系作者 +如有问题,可提交 Issue 或联系:<你的邮箱/联系方式> \ No newline at end of file From aa61f699ccaff87e1a79d4c254f4b99f47121f4b Mon Sep 17 00:00:00 2001 From: dacun922 <3109383112@qq.com> Date: Mon, 8 Dec 2025 09:18:21 +0800 Subject: [PATCH 4/5] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E8=BD=A6?= =?UTF-8?q?=E9=81=93=E7=BA=BF=E7=BB=98=E5=88=B6-v1.1=EF=BC=88=E5=9C=86?= =?UTF-8?q?=E7=82=B9=E5=A4=A7=E5=B0=8F+=E7=89=88=E6=9C=AC=E6=A0=87?= =?UTF-8?q?=E8=AF=86+=E5=8A=A0=E8=BD=BD=E6=8F=90=E7=A4=BA=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.py | 58 ++++++++++++++++++++------------------------------------- 1 file changed, 20 insertions(+), 38 deletions(-) diff --git a/main.py b/main.py index 6ca99dddd0..02836ec07e 100644 --- a/main.py +++ b/main.py @@ -38,14 +38,14 @@ def preprocess_frames(imgs): processed[i] = np.zeros((384, 512), dtype=np.uint8) return frames_to_tensor(processed) -# -------------------------- 主函数(无参数、全英文、车道线优化) -------------------------- +# -------------------------- 主函数(v1.1优化版) -------------------------- def main(): - # 1. 初始化显示窗口(800x600,固定尺寸) - win_name = "Lane Line Prediction (Blue=Left | Red=Right | Green=Path)" + # 1. 初始化显示窗口(新增v1.1版本标识) + win_name = "Lane Line Prediction v1.1 (Blue=Left | Red=Right | Green=Path)" cv2.namedWindow(win_name, cv2.WINDOW_NORMAL) cv2.resizeWindow(win_name, 800, 600) - # 2. 读取视频(写死路径,无需传参) + # 2. 读取视频(固定路径) video_path = "/home/dacun/nn/sample.hevc" cap = cv2.VideoCapture(video_path) if not cap.isOpened(): @@ -56,17 +56,17 @@ def main(): cv2.destroyAllWindows() return - # 读取前10帧(用于推理,保留原始帧和模型输入帧) - raw_display_frames = [] # 用于显示的800x600帧 - model_input_imgs = [] # 用于模型的512x384 YUV帧 + # 读取前10帧(分离显示帧和模型输入帧) + raw_display_frames = [] + model_input_imgs = [] for _ in range(10): ret, frame = cap.read() if not ret: break - # 缩放为显示尺寸(800x600) + # 缩放为显示尺寸 display_frame = cv2.resize(frame, (800, 600)) raw_display_frames.append(display_frame) - # 转换为模型需要的YUV格式并缩放 + # 转换为模型所需YUV格式 yuv_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2YUV_I420) model_frame = cv2.resize(yuv_frame, (512, 384), cv2.INTER_AREA) model_input_imgs.append(model_frame) @@ -81,16 +81,16 @@ def main(): cv2.destroyAllWindows() return - # 3. 加载模型(显示英文提示,无乱码) + # 3. 加载模型(新增成功提示) load_frame = np.ones((600, 800, 3), dtype=np.uint8) * 255 cv2.putText(load_frame, "Loading model...", (200, 300), cv2.FONT_HERSHEY_SIMPLEX, 1.5, (0, 255, 0), 3) cv2.imshow(win_name, load_frame) - cv2.waitKey(200) # 刷新显示 + cv2.waitKey(200) - # 模型路径(写死,无需修改) model_path = "/home/dacun/桌面/openpilot-modeld-main/models/supercombo.h5" try: supercombo_model = load_model(model_path, compile=False) + print("✅ Model loaded successfully (supercombo.h5)") # 新增提示 except Exception as e: empty_frame = np.ones((600, 800, 3), dtype=np.uint8) * 255 cv2.putText(empty_frame, "Model load failed", (180, 300), cv2.FONT_HERSHEY_SIMPLEX, 1.5, (0, 0, 255), 3) @@ -99,7 +99,7 @@ def main(): cv2.destroyAllWindows() return - # 4. 预处理帧(显示英文提示) + # 4. 预处理帧 preprocess_frame = np.ones((600, 800, 3), dtype=np.uint8) * 255 cv2.putText(preprocess_frame, "Preprocessing frames...", (150, 300), cv2.FONT_HERSHEY_SIMPLEX, 1.2, (0, 255, 0), 3) cv2.imshow(win_name, preprocess_frame) @@ -118,57 +118,40 @@ def main(): model_state = np.zeros((1, 512)) model_desire = np.zeros((1, 8)) - # 6. 逐帧推理+绘制(核心优化:车道线右移+放大圆点) + # 6. 逐帧推理+绘制(优化圆点大小:8→9/6→7) print("✅ Start inference and display (Press Q to exit)") for i in range(len(frame_tensors) - 1): - # 确保帧存在,避免索引越界 - if i >= len(raw_display_frames): - current_frame = np.ones((600, 800, 3), dtype=np.uint8) * 255 - else: - current_frame = raw_display_frames[i].copy() # 复制原始帧,避免修改 - + current_frame = raw_display_frames[i].copy() if i < len(raw_display_frames) else np.ones((600, 800, 3), dtype=np.uint8) * 255 try: - # 模型推理(连续两帧作为输入) input_data = [np.vstack(frame_tensors[i:i+2])[None], model_desire, model_state] model_output = supercombo_model.predict(input_data, verbose=0) parsed_result = parser(model_output) model_state = model_output[-1] - # -------------------------- 车道线绘制优化 -------------------------- - # 提取模型输出的车道线/路径x坐标 + # 车道线坐标映射+右移+圆点优化 left_lane_x = parsed_result["lll"][0] right_lane_x = parsed_result["rll"][0] path_x = parsed_result["path"][0] - - # 窗口尺寸 win_h, win_w = 600, 800 - # y坐标映射(0-191 → 0-599) y_points = np.linspace(0, win_h - 1, 192).astype(int) - # x坐标映射(0-512 → 0-799)+ 右移100像素(解决偏左问题)+ 放大圆点到8px left_x_mapped = (left_lane_x / 512 * win_w + 100).astype(int) right_x_mapped = (right_lane_x / 512 * win_w + 100).astype(int) path_x_mapped = (path_x / 512 * win_w + 100).astype(int) - # 绘制左车道线(蓝色,8px实心圆) + # 左车道(蓝,9px)、右车道(红,9px)、路径(绿,7px) for x, y in zip(left_x_mapped, y_points): if 0 <= x < win_w and 0 <= y < win_h: - cv2.circle(current_frame, (x, y), 8, (255, 0, 0), -1) - # 绘制右车道线(红色,8px实心圆) + cv2.circle(current_frame, (x, y), 9, (255, 0, 0), -1) for x, y in zip(right_x_mapped, y_points): if 0 <= x < win_w and 0 <= y < win_h: - cv2.circle(current_frame, (x, y), 8, (0, 0, 255), -1) - # 绘制预测路径(绿色,6px实心圆) + cv2.circle(current_frame, (x, y), 9, (0, 0, 255), -1) for x, y in zip(path_x_mapped, y_points): if 0 <= x < win_w and 0 <= y < win_h: - cv2.circle(current_frame, (x, y), 6, (0, 255, 0), -1) - + cv2.circle(current_frame, (x, y), 7, (0, 255, 0), -1) except Exception as e: - # 推理失败时仅打印错误,仍显示原始帧 print(f"⚠️ Frame {i+1} inference error: {str(e)[:30]}") - # 强制显示当前帧 cv2.imshow(win_name, current_frame) - # 按Q退出 if cv2.waitKey(100) & 0xFF == ord('q'): print("🛑 Exit by user (Q pressed)") break @@ -179,4 +162,3 @@ def main(): if __name__ == "__main__": main() -# 重新提交PR:车道线预测核心代码 From 2093573ae62bfb8f9d993516255f2af50fba1be6 Mon Sep 17 00:00:00 2001 From: dacun922 <3109383112@qq.com> Date: Mon, 15 Dec 2025 08:40:21 +0800 Subject: [PATCH 5/5] =?UTF-8?q?feat:=20=E5=B0=86=E7=BB=9D=E5=AF=B9?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=E6=94=B9=E4=B8=BA=E7=9B=B8=E5=AF=B9=E8=B7=AF?= =?UTF-8?q?=E5=BE=84,=E6=B7=BB=E5=8A=A0=E4=B8=8B=E8=BD=BD=E9=93=BE?= =?UTF-8?q?=E6=8E=A5=E5=92=8C=E8=AF=B4=E6=98=8E=E6=96=87=E6=A1=A3README.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 ++- main.py | 64 +++++++++++++++++++++++++++++++++++-------------------- 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index c198e51470..83b4947c43 100644 --- a/README.md +++ b/README.md @@ -45,4 +45,5 @@ mkdocs serve * 已有相关 [无人车](https://openhutb.github.io/doc/used_by/) 、[无人机](https://openhutb.github.io/air_doc/third/used_by/) 、[具身人](https://openhutb.github.io/doc/pedestrian/humanoid/) 的实现 * [神经网络原理](https://github.com/OpenHUTB/neuro) - +通过网盘分享的文件:supercombo.h5 +链接: https://pan.baidu.com/s/1yYc4aPBLnXRuWXEb0hyKMA?pwd=1234 提取码: 1234 \ No newline at end of file diff --git a/main.py b/main.py index 02836ec07e..5d447efb56 100644 --- a/main.py +++ b/main.py @@ -1,6 +1,7 @@ import sys import os -sys.path.append('/home/dacun/nn/src') +# 替换绝对路径为相对路径:基于main.py所在目录向上找src文件夹(适配任意部署环境) +sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..')) import numpy as np import cv2 @@ -38,15 +39,15 @@ def preprocess_frames(imgs): processed[i] = np.zeros((384, 512), dtype=np.uint8) return frames_to_tensor(processed) -# -------------------------- 主函数(v1.1优化版) -------------------------- +# -------------------------- 主函数(无参数、全英文、车道线优化) -------------------------- def main(): - # 1. 初始化显示窗口(新增v1.1版本标识) - win_name = "Lane Line Prediction v1.1 (Blue=Left | Red=Right | Green=Path)" + # 1. 初始化显示窗口(800x600,固定尺寸) + win_name = "Lane Line Prediction (Blue=Left | Red=Right | Green=Path)" cv2.namedWindow(win_name, cv2.WINDOW_NORMAL) cv2.resizeWindow(win_name, 800, 600) - # 2. 读取视频(固定路径) - video_path = "/home/dacun/nn/sample.hevc" + # 2. 读取视频(修改为相对路径:main.py所在文件夹下的sample.hevc) + video_path = "./sample.hevc" # 仅改这里:绝对路径→相对路径 cap = cv2.VideoCapture(video_path) if not cap.isOpened(): empty_frame = np.ones((600, 800, 3), dtype=np.uint8) * 255 @@ -56,17 +57,17 @@ def main(): cv2.destroyAllWindows() return - # 读取前10帧(分离显示帧和模型输入帧) - raw_display_frames = [] - model_input_imgs = [] + # 读取前10帧(用于推理,保留原始帧和模型输入帧) + raw_display_frames = [] # 用于显示的800x600帧 + model_input_imgs = [] # 用于模型的512x384 YUV帧 for _ in range(10): ret, frame = cap.read() if not ret: break - # 缩放为显示尺寸 + # 缩放为显示尺寸(800x600) display_frame = cv2.resize(frame, (800, 600)) raw_display_frames.append(display_frame) - # 转换为模型所需YUV格式 + # 转换为模型需要的YUV格式并缩放 yuv_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2YUV_I420) model_frame = cv2.resize(yuv_frame, (512, 384), cv2.INTER_AREA) model_input_imgs.append(model_frame) @@ -81,16 +82,16 @@ def main(): cv2.destroyAllWindows() return - # 3. 加载模型(新增成功提示) + # 3. 加载模型(显示英文提示,无乱码) load_frame = np.ones((600, 800, 3), dtype=np.uint8) * 255 cv2.putText(load_frame, "Loading model...", (200, 300), cv2.FONT_HERSHEY_SIMPLEX, 1.5, (0, 255, 0), 3) cv2.imshow(win_name, load_frame) - cv2.waitKey(200) + cv2.waitKey(200) # 刷新显示 - model_path = "/home/dacun/桌面/openpilot-modeld-main/models/supercombo.h5" + # 模型路径(修改为相对路径:main.py所在文件夹下的models/supercombo.h5) + model_path = "./models/supercombo.h5" # 仅改这里:绝对路径→相对路径 try: supercombo_model = load_model(model_path, compile=False) - print("✅ Model loaded successfully (supercombo.h5)") # 新增提示 except Exception as e: empty_frame = np.ones((600, 800, 3), dtype=np.uint8) * 255 cv2.putText(empty_frame, "Model load failed", (180, 300), cv2.FONT_HERSHEY_SIMPLEX, 1.5, (0, 0, 255), 3) @@ -99,7 +100,7 @@ def main(): cv2.destroyAllWindows() return - # 4. 预处理帧 + # 4. 预处理帧(显示英文提示) preprocess_frame = np.ones((600, 800, 3), dtype=np.uint8) * 255 cv2.putText(preprocess_frame, "Preprocessing frames...", (150, 300), cv2.FONT_HERSHEY_SIMPLEX, 1.2, (0, 255, 0), 3) cv2.imshow(win_name, preprocess_frame) @@ -118,40 +119,57 @@ def main(): model_state = np.zeros((1, 512)) model_desire = np.zeros((1, 8)) - # 6. 逐帧推理+绘制(优化圆点大小:8→9/6→7) + # 6. 逐帧推理+绘制(核心优化:车道线右移+放大圆点) print("✅ Start inference and display (Press Q to exit)") for i in range(len(frame_tensors) - 1): - current_frame = raw_display_frames[i].copy() if i < len(raw_display_frames) else np.ones((600, 800, 3), dtype=np.uint8) * 255 + # 确保帧存在,避免索引越界 + if i >= len(raw_display_frames): + current_frame = np.ones((600, 800, 3), dtype=np.uint8) * 255 + else: + current_frame = raw_display_frames[i].copy() # 复制原始帧,避免修改 + try: + # 模型推理(连续两帧作为输入) input_data = [np.vstack(frame_tensors[i:i+2])[None], model_desire, model_state] model_output = supercombo_model.predict(input_data, verbose=0) parsed_result = parser(model_output) model_state = model_output[-1] - # 车道线坐标映射+右移+圆点优化 + # -------------------------- 车道线绘制优化 -------------------------- + # 提取模型输出的车道线/路径x坐标 left_lane_x = parsed_result["lll"][0] right_lane_x = parsed_result["rll"][0] path_x = parsed_result["path"][0] + + # 窗口尺寸 win_h, win_w = 600, 800 + # y坐标映射(0-191 → 0-599) y_points = np.linspace(0, win_h - 1, 192).astype(int) + # x坐标映射(0-512 → 0-799)+ 右移100像素(解决偏左问题)+ 放大圆点到8px left_x_mapped = (left_lane_x / 512 * win_w + 100).astype(int) right_x_mapped = (right_lane_x / 512 * win_w + 100).astype(int) path_x_mapped = (path_x / 512 * win_w + 100).astype(int) - # 左车道(蓝,9px)、右车道(红,9px)、路径(绿,7px) + # 绘制左车道线(蓝色,8px实心圆) for x, y in zip(left_x_mapped, y_points): if 0 <= x < win_w and 0 <= y < win_h: - cv2.circle(current_frame, (x, y), 9, (255, 0, 0), -1) + cv2.circle(current_frame, (x, y), 8, (255, 0, 0), -1) + # 绘制右车道线(红色,8px实心圆) for x, y in zip(right_x_mapped, y_points): if 0 <= x < win_w and 0 <= y < win_h: - cv2.circle(current_frame, (x, y), 9, (0, 0, 255), -1) + cv2.circle(current_frame, (x, y), 8, (0, 0, 255), -1) + # 绘制预测路径(绿色,6px实心圆) for x, y in zip(path_x_mapped, y_points): if 0 <= x < win_w and 0 <= y < win_h: - cv2.circle(current_frame, (x, y), 7, (0, 255, 0), -1) + cv2.circle(current_frame, (x, y), 6, (0, 255, 0), -1) + except Exception as e: + # 推理失败时仅打印错误,仍显示原始帧 print(f"⚠️ Frame {i+1} inference error: {str(e)[:30]}") + # 强制显示当前帧 cv2.imshow(win_name, current_frame) + # 按Q退出 if cv2.waitKey(100) & 0xFF == ord('q'): print("🛑 Exit by user (Q pressed)") break