使用 darkSCNN 和 Caffe 进行车道线检测

使用 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 核心代码解析)
    • [六、附加内容:从源码编译 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 通过深度学习的方式学习车道线的特征。其基本流程如下:

  1. 特征提取:使用卷积层从输入图像中提取多层次的特征
  2. 像素级分类:对每个像素进行分类,判断它是否属于车道线以及属于哪条车道线

三、效果展示

四、详细操作步骤

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 框架进行车道线检测,包括环境搭建、模型下载、代码编译和实际运行的全过程。通过这个示例,您可以了解到:

  1. 该模型的基本应用流程
  2. Caffe 框架的基本使用方法
  3. 计算机视觉任务中的常见预处理和后处理技术
  4. 如何将深度学习模型应用到视频处理中

注意:本文提供的代码和步骤在 Ubuntu 18.04 环境下测试通过,其他环境可能需要适当调整。

相关推荐
ai产品老杨5 小时前
以技术共享点燃全球能源变革新引擎的智慧能源开源了
javascript·人工智能·开源·音视频·能源
taxunjishu6 小时前
基于 CC-Link IE FB 转 DeviceNet 技术的三菱 PLC 与发那科机器人在汽车涂装线的精准喷涂联动
网络·人工智能·物联网·机器人·自动化·汽车·区块链
siliconstorm.ai6 小时前
开源与闭源的再对决:从Grok到中国力量,AI生态走向何方?
大数据·图像处理·人工智能·语言模型·ai作画·云计算·机器翻译
ws2019076 小时前
奔赴MOBILITY China 2026深圳新能源汽车技术展,共鉴行业高光时刻
大数据·人工智能·科技·汽车
TextIn智能文档云平台8 小时前
AI文档产品与传统OCR软件的根本区别是什么?
人工智能·ocr
FIT2CLOUD飞致云8 小时前
新增MCP工具管理,AI对话节点新增工具设置,支持对接企业微信机器人,MaxKB v2.1.0版本发布
人工智能·开源
l12345sy8 小时前
Day19_【机器学习—线性回归 (2)—损失函数、梯度下降法】
人工智能·机器学习·线性回归·梯度下降法·损失函数
深兰科技8 小时前
深兰科技AI问诊助手走访打浦桥街道社区卫生服务中心
人工智能·windows·github·postman·visual studio·深兰科技·ai问诊
道一238 小时前
Keras/TensorFlow 中 `predict()` 函数详细说明
人工智能·tensorflow·keras