使用 darkSCNN 和 Caffe 进行车道线检测
-
- 一、引言:车道线检测的重要性
- [二、darkSCNN_caffe 原理解析](#二、darkSCNN_caffe 原理解析)
-
- [2.1 什么是 darkSCNN?](#2.1 什么是 darkSCNN?)
- [2.2 模型工作原理](#2.2 模型工作原理)
- 三、效果展示
- 四、详细操作步骤
-
- [4.1 环境准备:创建 Docker 容器](#4.1 环境准备:创建 Docker 容器)
- [4.2 安装必要的依赖包](#4.2 安装必要的依赖包)
- [4.3 下载预训练模型](#4.3 下载预训练模型)
- [4.4 获取测试视频](#4.4 获取测试视频)
- [4.5 编译推理程序](#4.5 编译推理程序)
- [4.6 运行车道线检测](#4.6 运行车道线检测)
- [4.7 生成结果 GIF(便于展示)](#4.7 生成结果 GIF(便于展示))
- 五、代码详解
-
- [5.1 `caffe_inference.cpp` 核心代码解析](#5.1
caffe_inference.cpp
核心代码解析)
- [5.1 `caffe_inference.cpp` 核心代码解析](#5.1
- [六、附加内容:从源码编译 Caffe](#六、附加内容:从源码编译 Caffe)
-
- [6.1 安装编译依赖](#6.1 安装编译依赖)
- [6.2 下载 Caffe 源码](#6.2 下载 Caffe 源码)
- [6.3 配置和编译](#6.3 配置和编译)
- 七、总结
一、引言:车道线检测的重要性
车道线检测是自动驾驶和高级驾驶辅助系统(ADAS)中的关键技术之一。它能够帮助车辆识别道路上的车道标记,从而实现车道保持、偏离预警等功能。本文介绍了如何使用 darkSCNN 模型和 Caffe 框架对视频中的车道线进行检测。
二、darkSCNN_caffe 原理解析
2.1 什么是 darkSCNN?
darkSCNN 是一种基于卷积神经网络(CNN)的车道线检测模型
2.2 模型工作原理
darkSCNN 通过深度学习的方式学习车道线的特征。其基本流程如下:
- 特征提取:使用卷积层从输入图像中提取多层次的特征
- 像素级分类:对每个像素进行分类,判断它是否属于车道线以及属于哪条车道线
三、效果展示

四、详细操作步骤
4.1 环境准备:创建 Docker 容器
我们使用 Docker 容器来确保环境一致性,避免依赖问题。
bash
# 创建项目目录
mkdir darkSCNN_caffe_DEMO
cd darkSCNN_caffe_DEMO/
# 清理可能存在的旧容器
docker stop darkSCNN_caffe
docker rm darkSCNN_caffe
# 创建新容器,设置文件映射和网络
docker run -it --privileged --net=host \
-v $PWD:/home -w /home --name darkSCNN_caffe ubuntu:18.04 /bin/bash
4.2 安装必要的依赖包
在容器内执行以下命令安装所需软件:
bash
# 更新软件包列表
apt update
# 安装核心依赖
apt install caffe-cpu -y
apt install wget -y
apt install unzip -y
apt install python3-pip -y
apt install locales -y
# 安装视频处理相关工具
apt install ffmpeg -y
# 安装计算机视觉库
apt install libopencv-dev -y
# 安装数学运算和深度学习依赖
apt install libopenblas-dev -y
apt install libcaffe-cpu-dev -y
apt install libboost1.62-all-dev -y
# 安装日志和序列化库
apt install libgflags-dev -y
apt install libgoogle-glog-dev -y
apt install libprotobuf-dev -y
# 安装视频下载工具
pip3 install you-get
# 设置 locale(解决可能出现的字符编码问题)
locale-gen
locale-gen en_US.UTF-8
export LC_CTYPE=en_US.UTF-8
4.3 下载预训练模型
bash
# 下载 darkSCNN 的预训练模型
wget https://apollo-pkg-beta.cdn.bcebos.com/perception_model/darkSCNN_caffe.zip
# 解压模型文件
unzip darkSCNN_caffe.zip
4.4 获取测试视频
bash
# 从网络下载测试视频(这里以B站视频为例)
you-get https://www.bilibili.com/video/BV19J411t7Uy/ -O test
# 从视频中提取一帧作为测试图像
ffmpeg -i test.mp4 -ss 00:00:01 -vframes 1 test.jpg
4.5 编译推理程序
bash
g++ -std=c++11 -DCPU_ONLY -o caffe_inference caffe_inference.cpp \
-lcaffe -lglog -lboost_system -lopenblas `pkg-config --libs opencv`
4.6 运行车道线检测
bash
# 设置日志级别(减少不必要的输出)
export GLOG_minloglevel=7
export GLOG_alsologtostderr=1
# 运行推理程序,处理视频
./caffe_inference test.mp4 darkSCNN_caffe.mp4
4.7 生成结果 GIF(便于展示)
bash
# 将输出的视频转换为GIF格式,便于在网页上展示
ffmpeg -i darkSCNN_caffe.mp4 -vf "fps=5,scale=640:-1:flags=lanczos,split [s0][s1];[s0] palettegen=stats_mode=diff [p];[s1][p] paletteuse=dither=none:diff_mode=rectangle" darkSCNN_caffe.gif
五、代码详解
5.1 caffe_inference.cpp
核心代码解析
c
#include <cblas.h>
#include <opencv2/opencv.hpp>
#include <caffe/caffe.hpp>
#include <algorithm>
#include <iosfwd>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <fstream>
#include <iostream>
#include <sstream>
#include <cmath>
using namespace caffe;
using std::string;
int main(int argc, char** argv) {
// 设置CPU模式
Caffe::set_mode(Caffe::CPU);
// 模型文件路径
string model_file = "darkSCNN_caffe/deploy.prototxt";
string trained_file = "darkSCNN_caffe/deploy.caffemodel";
// 加载网络
Net<float> net(model_file, TEST);
net.CopyTrainedLayersFrom(trained_file);
// 获取输入blob
Blob<float>* input_blob = net.input_blobs()[0];
input_blob->Reshape(1, 3, 480, 640); // 根据实际情况调整形状
float* input_data_ptr = input_blob->mutable_cpu_data();
// 打开视频文件
cv::VideoCapture cap(argv[1]);
if (!cap.isOpened()) {
std::cerr << "Error: Could not open video file." << std::endl;
return -1;
}
// 创建视频写入器,用于保存处理结果
cv::VideoWriter writer;
int out_width = 640;
int out_height = 480*3; // 高度为3倍,用于垂直拼接三种视图
writer.open(argv[2], cv::VideoWriter::fourcc('M', 'J', 'P', 'G'), 5, cv::Size(out_width, out_height));
if (!writer.isOpened()) {
std::cerr << "Error: Could not create video writer." << std::endl;
return -1;
}
cv::Mat frame;
int frame_count = 0;
int process_count=0;
// 主循环:处理视频每一帧
while (true) {
cap >> frame; // 读取一帧
if (frame.empty()) break;
frame_count++;
if(frame_count%25!=0) continue; // 每25帧处理一帧,提高处理速度
process_count+=1;
if (process_count>25) break; // 最多处理25帧,避免处理时间过长
std::cout << "Processing frame " << process_count << std::endl;
// 调整图像尺寸为640x480
cv::Mat resize_image;
cv::resize(frame, resize_image, cv::Size(640, 480));
// 将图像数据类型转换为float32
cv::Mat float_image;
resize_image.convertTo(float_image, CV_32FC3, 1.0);
// 注意:如果输入图像实际上是BGR格式,则可以跳过此步骤
cv::cvtColor(float_image, float_image, cv::COLOR_RGB2BGR);
// 分离通道以便逐个处理
std::vector<cv::Mat> channels;
cv::split(float_image, channels);
// 均值减法(均值顺序为BGR)
cv::Scalar mean(95.0, 99.0, 96.0); // BGR均值
channels[0] -= mean[0]; // B通道
channels[1] -= mean[1]; // G通道
channels[2] -= mean[2]; // R通道
// 为每个通道创建新的形状 [1, height, width]
for (auto& channel : channels) {
channel = channel.reshape(1, 1); // 1个通道,1行,列数自动计算
}
// 合并通道 [channels, height, width]
cv::Mat chw_image;
cv::vconcat(channels, chw_image);
memcpy(input_data_ptr,chw_image.data,1*3*480*640*sizeof(float));
// 前向传播
net.Forward();
// 获取输出
Blob<float>* output_blob = net.output_blobs()[1];
const float* output_data = output_blob->cpu_data();
int output0_Size=1*13*480*640;
int num_lanes_=13;
int lane_output_height_=480;
int lane_output_width_=640;
float confidence_threshold_lane_=0.95;
// 创建一个向量来存储每个车道的掩码
std::vector<cv::Mat> masks;
// 从Blob中提取每个车道的置信度图
for (int i = 0; i < num_lanes_; ++i) {
cv::Mat tmp(lane_output_height_, lane_output_width_, CV_32FC1);
// 计算当前车道数据在Blob中的起始位置
const float* channel_data = output_data +i * lane_output_height_ * lane_output_width_;
// 将数据复制到OpenCV矩阵中
memcpy(tmp.data, channel_data,lane_output_width_ * lane_output_height_ * sizeof(float));
masks.push_back(tmp);
}
// 初始化像素计数器和彩色掩码图像
std::vector<int> cnt_pixels(num_lanes_, 0);
cv::Mat mask_color(lane_output_height_, lane_output_width_, CV_32FC1);
mask_color.setTo(cv::Scalar(0)); // 初始化为0(背景)
// 遍历所有车道类别
for (int c = 0; c < num_lanes_; ++c) {
// 获取当前车道的置信度图
cv::Mat& lane_mask = masks[c];
// 遍历每个像素
for (int h = 0; h < lane_mask.rows; ++h) {
for (int w = 0; w < lane_mask.cols; ++w) {
// 如果置信度超过阈值
if (lane_mask.at<float>(h, w) >= confidence_threshold_lane_) {
// 在彩色掩码中设置对应的类别值
mask_color.at<float>(h, w) = static_cast<float>(c);
// 增加该类的像素计数
cnt_pixels[c]++;
}
}
}
}
// 将浮点型掩码转换为8位图像以便显示和保存
cv::Mat mask_color_8u;
mask_color.convertTo(mask_color_8u, CV_8U, 255.0 / (num_lanes_ - 1));
// 应用颜色映射使不同类别显示为不同颜色
cv::Mat colored_output;
cv::applyColorMap(mask_color_8u, colored_output, cv::COLORMAP_JET);
cv::Mat overlay;
double alpha = 0.2; // 透明度
cv::addWeighted(resize_image, 1 - alpha, colored_output, alpha, 0.0, overlay);
// 垂直拼接三种视图:原始图像、分割结果和叠加结果
cv::Mat result0;
cv::vconcat(resize_image, colored_output, result0);
cv::Mat result1;
cv::vconcat(result0, overlay, result1);
// 写入结果帧到输出视频
writer.write(result1);
}
// 释放资源
cap.release();
writer.release();
std::cout << "Video processing completed. Processed " << process_count << " frames." << std::endl;
return 0;
}
六、附加内容:从源码编译 Caffe
6.1 安装编译依赖
bash
# 安装基础编译工具和库
apt install libopenblas-dev -y
apt install git -y
apt install cmake -y
apt install protobuf-compiler -y
# 安装数据库支持(可选)
apt install libhdf5-dev -y
apt install liblmdb-dev -y
apt install libleveldb-dev -y
apt install libsnappy-dev -y
# 安装数学库
apt install libatlas-base-dev -y
6.2 下载 Caffe 源码
bash
# 克隆 Caffe 官方仓库
git clone https://github.com/BVLC/caffe.git
cd caffe
# 切换到稳定版本(1.0版本)
git checkout 1.0
6.3 配置和编译
bash
# 创建配置文件
cat > Makefile.config << 'EOF'
CPU_ONLY := 1
USE_OPENCV := 0
USE_LEVELDB := 0
USE_LMDB := 0
BLAS := atlas
PYTHON_INCLUDE := /usr/include/python2.7 /usr/lib/python2.7/dist-packages/numpy/core/include
PYTHON_LIB := /usr/lib
INCLUDE_DIRS := $(PYTHON_INCLUDE) /usr/local/include /usr/include/hdf5/serial
LIBRARY_DIRS := $(PYTHON_LIB) /usr/local/lib /usr/lib /usr/lib/x86_64-linux-gnu/hdf5/serial
BUILD_DIR := build
DISTRIBUTE_DIR := distribute
Q ?= @
EOF
# 开始编译
make
七、总结
本文介绍了如何使用 darkSCNN 模型和 Caffe 框架进行车道线检测,包括环境搭建、模型下载、代码编译和实际运行的全过程。通过这个示例,您可以了解到:
- 该模型的基本应用流程
- Caffe 框架的基本使用方法
- 计算机视觉任务中的常见预处理和后处理技术
- 如何将深度学习模型应用到视频处理中
注意:本文提供的代码和步骤在 Ubuntu 18.04 环境下测试通过,其他环境可能需要适当调整。