车道检测源码分析系列(三)

下面看一个出自国外某教授的基于OpenCVSharp的车辆视觉框架,虽然包含车道检测和车辆识别,但源程序仅100行左右,非常精巧。

算法描述

检测部分的步骤很经典,是比较通用的步骤:

  1. 将图像上半部分无关内容截掉,设定ROI
  2. 转为灰度图
  3. Canny算子提取边缘
  4. Hough检测直线
  5. 通过直线角度位置信息筛选出车道线,标记到图上

效果如下图:

源程序

有博客将其中车道检测部分转为了OpenCV2,以下的源码转自该博客。原文链接

另外,国外有基于此程序进行扩展的,后续会介绍。

OpenCVSharp版本

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;

using OpenCvSharp;

namespace LaneDetection
{
    class Program
    {
        [STAThread]
        static void Main()
        {
            CvCapture cap = CvCapture.FromFile("test1.mp4");
            CvWindow w = new CvWindow("Lane Detection");
            CvWindow canny = new CvWindow("Canny");
            IplImage src, gray, dstCanny, halfFrame, smallImg;
            CvMemStorage storage = new CvMemStorage();
            CvSeq lines;

            while (CvWindow.WaitKey(10) < 0)
            {
                src = cap.QueryFrame();
                halfFrame = new IplImage(new CvSize(src.Size.Width / 2, src.Size.Height / 2), BitDepth.U8, 3);
                Cv.PyrDown(src, halfFrame, CvFilter.Gaussian5x5);

                gray = new IplImage(src.Size, BitDepth.U8, 1);
                dstCanny = new IplImage(src.Size, BitDepth.U8, 1);
                storage.Clear();

                // Crop off top half of image since we‘re only interested in the lower portion of the video
                int halfWidth = src.Width / 2;
                int halfHeight = src.Height / 2;
                int startX = halfWidth - (halfWidth / 2);
                src.SetROI(new CvRect(0, halfHeight - 0, src.Width - 1, src.Height - 1));

                gray.SetROI(src.GetROI());
                dstCanny.SetROI(src.GetROI());

                src.CvtColor(gray, ColorConversion.BgrToGray);
                Cv.Smooth(gray, gray, SmoothType.Gaussian, 5, 5);
                Cv.Canny(gray, dstCanny, 50, 200, ApertureSize.Size3);
                canny.Image = dstCanny;
                storage.Clear();
                lines = dstCanny.HoughLines2(storage, HoughLinesMethod.Probabilistic, 1, Math.PI / 180, 50, 50, 100);

                for (int i = 0; i < lines.Total; i++)
                {
                    CvLineSegmentPoint elem = lines.GetSeqElem<CvLineSegmentPoint>(i).Value;

                    int dx = elem.P2.X - elem.P1.X;
                    int dy = elem.P2.Y - elem.P1.Y;
                    double angle = Math.Atan2(dy, dx) * 180 / Math.PI;

                    if (Math.Abs(angle) <= 10)
                        continue;

                    if (elem.P1.Y > elem.P2.Y + 50  || elem.P1.Y < elem.P2.Y -50 )
                    {
                        src.Line(elem.P1, elem.P2, CvColor.Red, 2, LineType.AntiAlias, 0);
                    }
                }
                src.ResetROI();
                storage.Clear();
                w.Image = src;
            }
        }
    }
}

OpenCV2版本

#include "stdafx.h"
#include <highgui.h>
#include <cv.h>
#include <math.h>

using namespace cv;
using namespace std;

#define INF 99999999
CvCapture* g_capture = NULL;

int g_slider_pos = 0 ;
int frame_count = 0;
CvSeq* lines;

int main(int argc,char* argv[])
{
    cvNamedWindow( "show");
    g_capture=cvCreateFileCapture( "D:\\road.avi");
    IplImage* frame;
    while(1)
    {
        CvMemStorage* storage = cvCreateMemStorage();
        frame=cvQueryFrame(g_capture);

        //set the ROI of the original image
        int x = 0,y = frame->height/2;
        int width = frame->width,height = frame->height/2;

        if(!frame)
            break; 

        cvSetImageROI(frame,cvRect(x,y,width,height));
        IplImage* gray = cvCreateImage(cvGetSize(frame),8,1);
        cvCvtColor(frame,gray,CV_BGR2GRAY);

        cvCanny(gray,gray,50,100);
        cvShowImage("canny",gray);
        cvSmooth(gray,gray,CV_GAUSSIAN,3,1,0);

        //Hough
        lines = cvHoughLines2(gray,storage,CV_HOUGH_PROBABILISTIC,1,CV_PI/180,50,90,50);

        //select approprivate lines acoording to the slope
        for (int i = 0;i < lines->total;i ++)
        {
            double k =INF;
            CvPoint* line = (CvPoint*)cvGetSeqElem(lines,i);
            int dx = line[1].x - line[0].x;
            int dy = line[1].x - line[0].y;
            double angle = atan2(dy,dx) * 180 /CV_PI;
            if (abs(angle) <= 10)
                continue;
            if (line[0].y > line[1].y + 50 || line[0].y < line[1].y - 50)
            {
                cvLine(frame,line[0],line[1],CV_RGB(255,0,0),2,CV_AA);
            }
        }
        cvResetImageROI(frame);
        cvShowImage( "show",frame);
        char c = cvWaitKey(33);
        if(c==27)
            break;
    }
    cvReleaseCapture(&g_capture);
    cvDestroyWindow( "show");
    return 0;
}

参考资料

时间: 2024-11-25 22:40:40

车道检测源码分析系列(三)的相关文章

车道检测源码分析系列(二)

本节分析一个国人开发的简单的车道检测程序(不涉及跟踪) simple_lane_tracking 源码地址 作者主页 概述 采用opencv2编写 C++ 算法核心步骤 提取车道标记特征,封装在laneExtraction类中 车道建模(两条边,单车道),封装在laneModeling类中 对于断断续续的斑点车道标记(虚线)使用RANSAC匹配直线,对每幅图像,检测结果可能是感兴趣的左右车道都检测到或者全都没检测到 主程序框架 track.cpp 主程序依次读入源文件夹下的图片,进行处理后输出到

车道检测源码分析系列(一)

车道线检测是辅助驾驶和自动驾驶的重要研究内容,相关的算法研究已经延续了20多年,本系列文章旨在帮助初学者在阅读文献之余快速上手构建自己的项目.OpenCV极大地方便了机器视觉应用的编写,为了能在刚起步时就看得远些,我们需要站在OpenCV巨人的肩膀上. 先看一个比较简单的程序,Github地址 框架 class LaneDetect封装核心算法 void makeFromVid(string path) 根据视频文件目录读入,对视频中的图像帧进行处理 主函数 调用makeFromVid 核心算法

jQuery源码分析系列(31) : Ajax deferred实现 - Aaron.

AJAX的底层实现都是浏览器提供的,所以任何基于api上面的框架或者库,都只是说对于功能的灵活与兼容维护性做出最优的扩展 ajax请求的流程: 1.通过 new XMLHttpRequest 或其它的形式(指IE)生成ajax的对象xhr. 2.通过xhr.open(type, url, async, username, password)的形式建立一个连接. 3.通过setRequestHeader设定xhr的请求头部(request header). 4.通过send(data)请求服务器端

Cordova Android源码分析系列一(项目总览和CordovaActivity分析)

PhoneGap/Cordova是一个专业的移动应用开发框架,是一个全面的WEB APP开发的框架,提供了以WEB形式来访问终端设备的API的功能.这对于采用WEB APP进行开发者来说是个福音,这可以避免了原生开发的某些功能.Cordova 只是个原生外壳,app的内核是一个完整的webapp,需要调用的原生功能将以原生插件的形式实现,以暴露js接口的方式调用. Cordova Android项目是Cordova Android原生部分的Java代码实现,提供了Android原生代码和上层We

jQuery源码分析系列(33) : AJAX中的前置过滤器和请求分发器

jQuery1.5以后,AJAX模块提供了三个新的方法用于管理.扩展AJAX请求,分别是: 1.前置过滤器 jQuery. ajaxPrefilter 2.请求分发器 jQuery. ajaxTransport, 3.类型转换器 ajaxConvert 源码结构: jQuery.extend({ /** * 前置过滤器 * @type {[type]} */ ajaxPrefilter: addToPrefiltersOrTransports(prefilters), /** * 请求分发器 *

jQuery源码分析系列(38) : 队列操作

Queue队列,如同data数据缓存与Deferred异步模型一样,都是jQuery库的内部实现的基础设施 Queue队列是animate动画依赖的基础设施,整个jQuery中队列仅供给动画使用 Queue队列 队列是一种特殊的线性表,只允许在表的前端(队头)进行删除操作(出队),在表的后端(队尾)进行插入操作(入队).队列的特点是先进先出(FIFO-first in first out),即最先插入的元素最先被删除. 为什么要引入队列? 我们知道代码的执行流有异步与同步之分,例如 var a

jQuery-1.9.1源码分析系列完毕目录整理

jQuery 1.9.1源码分析已经完毕.目录如下 jQuery-1.9.1源码分析系列(一)整体架构 jQuery-1.9.1源码分析系列(一)整体架构续 jQuery-1.9.1源码分析系列(二)jQuery选择器 jQuery-1.9.1源码分析系列(二)jQuery选择器续1 jQuery-1.9.1源码分析系列(二)jQuery选择器续2——筛选 jQuery-1.9.1源码分析系列(三) Sizzle选择器引擎——词法解析 jQuery-1.9.1源码分析系列(三) Sizzle选择

Dubbo源码分析系列-服务的发布

RPC简化类图 RPC模块核心接口和抽象实现 默认实现Dubbo协议的接口和抽象实现 服务发布过程 调用过程 上图是服务提供者暴露服务的主过程: 首先ServiceConfig类拿到对外提供服务的实际类ref(如:HelloWorldImpl),然后通过ProxyFactory类的getInvoker方法使用ref生成一个AbstractProxyInvoker实例,到这一步就完成具体服务到Invoker的转化.接下来就是Invoker转换到Exporter的过程. Dubbo处理服务暴露的关键

Docker源码分析(三):Docker Daemon启动

1 前言 Docker诞生以来,便引领了轻量级虚拟化容器领域的技术热潮.在这一潮流下,Google.IBM.Redhat等业界翘楚纷纷加入Docker阵营.虽然目前Docker仍然主要基于Linux平台,但是Microsoft却多次宣布对Docker的支持,从先前宣布的Azure支持Docker与Kubernetes,到如今宣布的下一代Windows Server原生态支持Docker.Microsoft的这一系列举措多少喻示着向Linux世界的妥协,当然这也不得不让世人对Docker的巨大影响