2011-11-18 20:54:01 liujia2100 阅读数 13454
  • ThreeJS视频教程

    对整threeJS体系进行全面剖析。整理出全面的教学大纲,涵盖内容面非常广。 此教学版本为threeJS107版本。 关于版本不建议大家使用低于90的版本学习。 以下是课程目录 1-ThreeJS概览(基本图形简介,什么是点线面如何绘制点线面,什么是材质,什么是几何体,什么是相机,什么是渲染器,什么是场景) 2-相机和渲染器(详解相机类型,渲染器如何使用,针对不同场景怎么用,怎么调效果,怎么渲染,怎么输出画布,如何解决透明问题等等) 3-创建平面几何(常见的几何体如何使用,如何使用简单的几何体绘制出自定义自己想要的几何体,关于几何体的性能剖析,如何解决性能,几何体的渲染原理) 4-高级图形算法常见库(求直线的斜率  计算线段与圆的交点 计算线段的长度 判断折线是否在多边形内 等等) 5-sprite精灵(怎么让一个图标永远朝向屏幕,精灵的属性,精灵材质原理等,广告提示框必用) 6-骨骼游戏动画(什么是模型动画,常见游戏案例,如何让人头进行各种攻击动作) 7-3d模型加载(常见模型格式,如何渲染不同格式,不同格式的特点,什么格式性能优越,模型渲染异常,贴图不显示等问题详解) 8-高阶动态纹理(你所不知道的纹理用法,我说你不知道,你肯定不知道) 9-漫游轨迹以及其动画路径(怎么绘制贝塞尔曲线,如何使用曲线上的路径,跟随路径移动的原理,相机如何运动,物体如何运动) 10-着色器(什么是着色器。初识着色器基础,着色器材质怎么用,怎么使用着色器库) 11-常见渲染以及透明度问题 12-对象拾取以及拖拽(3d世界里面如何拖拽物体,拖拽的原理,mousemove mouseon等的事件效果) 13-世界坐标以及组的问题(什么是相对坐标,什么是世界坐标,什么是当前坐标,怎么转化父子坐标系,组的优化,为什么用组,组的优势) 14-指定对象旋转中心(什么是物体的几何体中心,如何改变中心,如何绕轴转动) 15-层级对象渲染(多个场景一键切换,切换的优势,针对大项目的用法) 16-拓展了解系列(不定期不断更新案例,各种酷炫效果bloom,halo等,以及各种3d图表,粒子案例等,不断构建你的3d实践能力)

    187 人正在学习 去看看 李元继

一天从网上下了20个vc的hough代码,没有一个代码是成功的。令人郁闷,我参考matlab代码写出了hough检测单个直线的程序

Hough变换:

本程序是我花费时间最长的程序;参考matlab算法;首先求出原图上的每一个像素在变换域上的对应曲线,即原图上每一点(i,j)对应变换域曲线(p,k);p=(int)(i*cos(pi*k/180)+j*sin(pi*k/180))(0<k<180);

然后遍历(i,j),累加变换域重复出现的位置,即曲线的交点

然后遍历累加器,寻找交点最大的数据,找出对应的原图坐标,即为所求直线

主要代码

for(i=0;i<img.height;i++) //转化成灰度

{

for(j=0,n=0;n<img.width*3,j<img.width;n+=3,j++)

{ //gray 变量存储rgb转灰度的数据

gray= ((float)(img.image[lineBytes*i+n+2])+(float)(*(img.image+lineBytes*i+n+1))+(float)(*(img.image+lineBytes*i+n)))/3; //lineBytes 原图每行实际字节数

grayPic[i*img.width+j]=(byte)gray;//转换成的灰度图像放在grayPic中

}

}

int logNum; //边缘检测

memset(lpDIBBits,(byte)0,sizeof(byte)*img.height*img.width);

for(i=3;i<img.height-2;i++)

for(j=3;j<img.width-2;j++)

{

//logNum 变量 记录每次运算的值

logNum=16*grayPic[i*img.width+j]-grayPic[(i-2)*img.width+j]-grayPic[(i-1)*img.width+j-1]-2*grayPic[(i-1)*img.width+j]-grayPic[(i-1)*img.width+j+1]-grayPic[i*img.width+j-2]-2*grayPic[i*img.width+j-1]-2*grayPic[i*img.width+j+1]-grayPic[i*img.width+j+2]-grayPic[(i+1)*img.width+j-1]-2*grayPic[(i+1)*img.width+j]-grayPic[(i+1)*img.width+j+1]-grayPic[(i+2)*img.width+j];//log算子

if(logNum > 0)

lpDIBBits[i*img.width+j]=255;//边缘检测后的数据存放在lpDIBBits中

else

lpDIBBits[i*img.width+j]=0;

}

for(i=1;i<img.height;i++) //img.height原图高度

for(j=1;j<img.width;j++) //img.width 原图宽度

{

if(lpDIBBits[i*img.width+j]==255) //对边缘检测后的数据(存在lpDIBBits中)进行hough变化

{

for(k=1;k<ma;k++) //ma=180

{

p=(int)(i*cos(pi*k/180)+j*sin(pi*k/180));//p hough变换中距离参数

p=(int)(p/2+mp/2); //p值优化防止为负

npp[k][p]=npp[k][p]++; //npp对变换域中对应重复出现的点累加

}

}

}

kmax=0; //最长直线的角度

pmax=0; //最长直线的距离

n=0; //这一部分为寻找最长直线

for(i=1;i<ma;i++) //ma=180

for(j=1;j<mp;j++) //mp为原图对角线距离

{

if(npp[i][j]>yuzhi) //找出最长直线 yuzhi为中间变量用于比较

{

yuzhi=npp[i][j];

kmax=i; //记录最长直线的角度

pmax=j; //记录最长直线的距离

}

}

memset(temp,(byte)255,sizeof(byte)*img.width*img.height);//原图中坐标符合kmax和pmax的值

for(i=1;i<img.height;i++) //的集合即是最长的直线

for(j=1;j<img.width;j++)

{

if(lpDIBBits[i*img.width+j]==255)

{

p=(int)(i*cos(pi*kmax/180)+j*sin(pi*kmax/180));//pi=3.1415926

p=(int)(p/2+mp/2); //mp为原图对角线距离

if(p==pmax)

*(temp+i*img.width+j)=0; //存储图像数据 放在temp数组中

}

}


右边是原图,里面有圆和直线,左边是经过hough变换检测出的直线(版权所有 欢迎交流)

2015-10-28 14:17:20 baimafujinji 阅读数 11370
  • ThreeJS视频教程

    对整threeJS体系进行全面剖析。整理出全面的教学大纲,涵盖内容面非常广。 此教学版本为threeJS107版本。 关于版本不建议大家使用低于90的版本学习。 以下是课程目录 1-ThreeJS概览(基本图形简介,什么是点线面如何绘制点线面,什么是材质,什么是几何体,什么是相机,什么是渲染器,什么是场景) 2-相机和渲染器(详解相机类型,渲染器如何使用,针对不同场景怎么用,怎么调效果,怎么渲染,怎么输出画布,如何解决透明问题等等) 3-创建平面几何(常见的几何体如何使用,如何使用简单的几何体绘制出自定义自己想要的几何体,关于几何体的性能剖析,如何解决性能,几何体的渲染原理) 4-高级图形算法常见库(求直线的斜率  计算线段与圆的交点 计算线段的长度 判断折线是否在多边形内 等等) 5-sprite精灵(怎么让一个图标永远朝向屏幕,精灵的属性,精灵材质原理等,广告提示框必用) 6-骨骼游戏动画(什么是模型动画,常见游戏案例,如何让人头进行各种攻击动作) 7-3d模型加载(常见模型格式,如何渲染不同格式,不同格式的特点,什么格式性能优越,模型渲染异常,贴图不显示等问题详解) 8-高阶动态纹理(你所不知道的纹理用法,我说你不知道,你肯定不知道) 9-漫游轨迹以及其动画路径(怎么绘制贝塞尔曲线,如何使用曲线上的路径,跟随路径移动的原理,相机如何运动,物体如何运动) 10-着色器(什么是着色器。初识着色器基础,着色器材质怎么用,怎么使用着色器库) 11-常见渲染以及透明度问题 12-对象拾取以及拖拽(3d世界里面如何拖拽物体,拖拽的原理,mousemove mouseon等的事件效果) 13-世界坐标以及组的问题(什么是相对坐标,什么是世界坐标,什么是当前坐标,怎么转化父子坐标系,组的优化,为什么用组,组的优势) 14-指定对象旋转中心(什么是物体的几何体中心,如何改变中心,如何绕轴转动) 15-层级对象渲染(多个场景一键切换,切换的优势,针对大项目的用法) 16-拓展了解系列(不定期不断更新案例,各种酷炫效果bloom,halo等,以及各种3d图表,粒子案例等,不断构建你的3d实践能力)

    187 人正在学习 去看看 李元继

全文目录请见

图像处理中的数学原理详解(Part1 总纲)

http://blog.csdn.net/baimafujinji/article/details/48467225


1.3.3 方向导数与梯度






我的“图像处理中的数学原理”专栏中之系列文章由“清华大学出版社”结集出版。欢迎关注我的新书《图像处理中的数学修炼》(Applied Mathematics in Digital Image Processing)——详细介绍图像处理中的数学原理,为你打开一道通往图像世界的数学之门,详细内容及目录请见 http://blog.csdn.net/baimafujinji/article/details/48467225



2018-05-26 18:09:38 znzxc 阅读数 323
  • ThreeJS视频教程

    对整threeJS体系进行全面剖析。整理出全面的教学大纲,涵盖内容面非常广。 此教学版本为threeJS107版本。 关于版本不建议大家使用低于90的版本学习。 以下是课程目录 1-ThreeJS概览(基本图形简介,什么是点线面如何绘制点线面,什么是材质,什么是几何体,什么是相机,什么是渲染器,什么是场景) 2-相机和渲染器(详解相机类型,渲染器如何使用,针对不同场景怎么用,怎么调效果,怎么渲染,怎么输出画布,如何解决透明问题等等) 3-创建平面几何(常见的几何体如何使用,如何使用简单的几何体绘制出自定义自己想要的几何体,关于几何体的性能剖析,如何解决性能,几何体的渲染原理) 4-高级图形算法常见库(求直线的斜率  计算线段与圆的交点 计算线段的长度 判断折线是否在多边形内 等等) 5-sprite精灵(怎么让一个图标永远朝向屏幕,精灵的属性,精灵材质原理等,广告提示框必用) 6-骨骼游戏动画(什么是模型动画,常见游戏案例,如何让人头进行各种攻击动作) 7-3d模型加载(常见模型格式,如何渲染不同格式,不同格式的特点,什么格式性能优越,模型渲染异常,贴图不显示等问题详解) 8-高阶动态纹理(你所不知道的纹理用法,我说你不知道,你肯定不知道) 9-漫游轨迹以及其动画路径(怎么绘制贝塞尔曲线,如何使用曲线上的路径,跟随路径移动的原理,相机如何运动,物体如何运动) 10-着色器(什么是着色器。初识着色器基础,着色器材质怎么用,怎么使用着色器库) 11-常见渲染以及透明度问题 12-对象拾取以及拖拽(3d世界里面如何拖拽物体,拖拽的原理,mousemove mouseon等的事件效果) 13-世界坐标以及组的问题(什么是相对坐标,什么是世界坐标,什么是当前坐标,怎么转化父子坐标系,组的优化,为什么用组,组的优势) 14-指定对象旋转中心(什么是物体的几何体中心,如何改变中心,如何绕轴转动) 15-层级对象渲染(多个场景一键切换,切换的优势,针对大项目的用法) 16-拓展了解系列(不定期不断更新案例,各种酷炫效果bloom,halo等,以及各种3d图表,粒子案例等,不断构建你的3d实践能力)

    187 人正在学习 去看看 李元继

先处理图像,滤去杂质,进行霍夫线变换,得到线段的序列,选择一根最长的线,这里简单处理,选择纵向最长的,进行角度的计算。

#include <iostream>
#include <opencv2/opencv.hpp>
#include <queue>
#include <math.h>

using namespace std;
using namespace cv;
int main()
{
	IplImage * g1 = cvLoadImage("倾斜的尺子.bmp", 1);//原图
	IplImage * g2 = cvCreateImage(cvSize(g1->width, g1->height), IPL_DEPTH_8U, 1);//存储灰度化后的图像
	IplImage * g3 = cvCreateImage(cvSize(g1->width, g1->height), IPL_DEPTH_8U, 1);//存储二值化、腐蚀、膨胀后的图像
	CvMemStorage* m_storage = cvCreateMemStorage(0);

	int length = 0;
	int maxTemp = 0;
	int maxLength = 0;
	double slope = 0;//斜率
	int tubeAngle = 0;//角度

	IplImage *dst = cvCloneImage(g1);//霍夫变换得到的直线画在dst上
	cvShowImage("原图", g1);
	cvCvtColor(g1, g2, CV_BGR2GRAY);//  转化为灰度图像
	cvAdaptiveThreshold(g2, g3, 255, CV_ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY, 31);//自适应二值化
 	Mat gg3 = cv::cvarrToMat(g3);
	erode(gg3, gg3, Mat(2, 2, CV_8U), Point(-1, -1), 2);   //腐蚀
	dilate(gg3, gg3, Mat(2, 2, CV_8U), Point(-1, -1), 2);  //膨胀    腐蚀膨胀为了滤出噪声点而凸显目标

	cvShowImage("处理后的图像", g3);
	CvSeq* lines_p = cvHoughLines2(g3, m_storage, CV_HOUGH_PROBABILISTIC, 1, CV_PI / 180, 30, 30, 10);

	cvZero(dst);

	CvPoint* line_p;
	for (int i = 0; i < lines_p->total; i++)
	{
		line_p = (CvPoint*)cvGetSeqElem(lines_p, i);
		cvLine(dst, line_p[0], line_p[1], CV_RGB(255, 255, 255), 2, CV_AA, 0);
		length = abs(line_p[0].y - line_p[1].y);
		if (i == 0)
		{
			maxLength = length;
		}
		else
		{
			if (length > maxLength)
			{
				maxTemp = i;//得到纵向最长的直线对应的序号
				maxLength = length;
			}
		}
	}

	cvShowImage("霍夫线变换得到的线", dst);
	line_p = (CvPoint*)cvGetSeqElem(lines_p, maxTemp);
	if (line_p != NULL)
	{
		if (line_p[0].x != line_p[1].x)
		{
			slope = abs((double)maxLength / (double)(line_p[0].x - line_p[1].x));
			tubeAngle = std::atan(slope) * 180 / 3.14159; 
		}
		else
			tubeAngle = 90;		
	}	
	cout << tubeAngle << endl;  
	waitKey();
	return 0;
}

结果如下:

 39度。

原图g1:

处理后的图像g3:

线变换得到的线段dst:

2019-06-16 23:01:31 u012601647 阅读数 109
  • ThreeJS视频教程

    对整threeJS体系进行全面剖析。整理出全面的教学大纲,涵盖内容面非常广。 此教学版本为threeJS107版本。 关于版本不建议大家使用低于90的版本学习。 以下是课程目录 1-ThreeJS概览(基本图形简介,什么是点线面如何绘制点线面,什么是材质,什么是几何体,什么是相机,什么是渲染器,什么是场景) 2-相机和渲染器(详解相机类型,渲染器如何使用,针对不同场景怎么用,怎么调效果,怎么渲染,怎么输出画布,如何解决透明问题等等) 3-创建平面几何(常见的几何体如何使用,如何使用简单的几何体绘制出自定义自己想要的几何体,关于几何体的性能剖析,如何解决性能,几何体的渲染原理) 4-高级图形算法常见库(求直线的斜率  计算线段与圆的交点 计算线段的长度 判断折线是否在多边形内 等等) 5-sprite精灵(怎么让一个图标永远朝向屏幕,精灵的属性,精灵材质原理等,广告提示框必用) 6-骨骼游戏动画(什么是模型动画,常见游戏案例,如何让人头进行各种攻击动作) 7-3d模型加载(常见模型格式,如何渲染不同格式,不同格式的特点,什么格式性能优越,模型渲染异常,贴图不显示等问题详解) 8-高阶动态纹理(你所不知道的纹理用法,我说你不知道,你肯定不知道) 9-漫游轨迹以及其动画路径(怎么绘制贝塞尔曲线,如何使用曲线上的路径,跟随路径移动的原理,相机如何运动,物体如何运动) 10-着色器(什么是着色器。初识着色器基础,着色器材质怎么用,怎么使用着色器库) 11-常见渲染以及透明度问题 12-对象拾取以及拖拽(3d世界里面如何拖拽物体,拖拽的原理,mousemove mouseon等的事件效果) 13-世界坐标以及组的问题(什么是相对坐标,什么是世界坐标,什么是当前坐标,怎么转化父子坐标系,组的优化,为什么用组,组的优势) 14-指定对象旋转中心(什么是物体的几何体中心,如何改变中心,如何绕轴转动) 15-层级对象渲染(多个场景一键切换,切换的优势,针对大项目的用法) 16-拓展了解系列(不定期不断更新案例,各种酷炫效果bloom,halo等,以及各种3d图表,粒子案例等,不断构建你的3d实践能力)

    187 人正在学习 去看看 李元继

在图像处理中,经常会遇到对直线或者圆的检测,之后再进行后续处理,下面简单介绍一下opencv中的霍夫检测

对棋盘的直线检测

import cv2 as cv
import numpy as np


def line_detect(bw_image, image):
    # rho 线段以像素为单位的距离精度,double类型的,推荐用1.0
    # theta 检测直线的角度,一般为np.pi/180
    # threshold 直线上点数阈值,超过才会判断为直线,这里设为20
    # minLineLength 直线长度的阈值,超过才会判断为直线,这里设为20
    # minLineGap 不同线段之间的距离阈值,超过则默认为是一条直线,这里设为20
    lines = cv.HoughLinesP(bw_image, 1.0, np.pi/180, 20, minLineLength=20, maxLineGap=20)  # 霍夫变换检测直线
    for line in lines:
        line = line[0]
        x_start, y_start, x_end, y_end = line
        cv.line(image, (x_start, y_start), (x_end, y_end), (0, 255, 0), 3)
    cv.imshow('', image)
    cv.waitKey(0)
    cv.destroyAllWindows()

if '__main__' == __name__:
    image_path = './chess.jpg'
    image = cv.imread(image_path)
    gray_image = cv.cvtColor(cv.imread(image_path), cv.COLOR_RGB2GRAY)
    ret, bw_image = cv.threshold(gray_image, 200, 255, cv.THRESH_BINARY_INV)
    line_detect(bw_image, image)

 

霍夫圆检测

import cv2 as cv
import numpy as np

def circle_detect(bw_image, image):
    # method   Hough 变换方式  HOUGH_GRADIENT
    # dp 直累加器图像的分辨率  一般为1
    # minDist 两个不同圆的最小距离
    # param1 用于处理边缘检测的梯度值方法
    # param2 累加器的阀值,阈值越小,检测到的圈子越多。
    # min_radius 最小圆半径
    # maxRadius 最大圆半径
    cv.imshow('', image)
    cv.waitKey(0)
    circles = cv.HoughCircles(bw_image, cv.HOUGH_GRADIENT, 1, 100, param1=100, param2=30, minRadius=20, maxRadius=500)
    for circle in circles[0]:
        x_center, y_center, radius = circle
        cv.circle(image, (x_center, y_center), radius, (0, 255, 0), 3)
    cv.imshow('', image)
    cv.waitKey(0)
    cv.destroyAllWindows()





if '__main__' == __name__:
    image_path = './timg.jpg'
    image = cv.imread(image_path)
    gray_image = cv.cvtColor(cv.imread(image_path), cv.COLOR_RGB2GRAY)
    ret, bw_image = cv.threshold(gray_image, 20, 255, cv.THRESH_BINARY)
    circle_detect(bw_image, image)

2019-07-09 16:52:50 qq_42258099 阅读数 78
  • ThreeJS视频教程

    对整threeJS体系进行全面剖析。整理出全面的教学大纲,涵盖内容面非常广。 此教学版本为threeJS107版本。 关于版本不建议大家使用低于90的版本学习。 以下是课程目录 1-ThreeJS概览(基本图形简介,什么是点线面如何绘制点线面,什么是材质,什么是几何体,什么是相机,什么是渲染器,什么是场景) 2-相机和渲染器(详解相机类型,渲染器如何使用,针对不同场景怎么用,怎么调效果,怎么渲染,怎么输出画布,如何解决透明问题等等) 3-创建平面几何(常见的几何体如何使用,如何使用简单的几何体绘制出自定义自己想要的几何体,关于几何体的性能剖析,如何解决性能,几何体的渲染原理) 4-高级图形算法常见库(求直线的斜率  计算线段与圆的交点 计算线段的长度 判断折线是否在多边形内 等等) 5-sprite精灵(怎么让一个图标永远朝向屏幕,精灵的属性,精灵材质原理等,广告提示框必用) 6-骨骼游戏动画(什么是模型动画,常见游戏案例,如何让人头进行各种攻击动作) 7-3d模型加载(常见模型格式,如何渲染不同格式,不同格式的特点,什么格式性能优越,模型渲染异常,贴图不显示等问题详解) 8-高阶动态纹理(你所不知道的纹理用法,我说你不知道,你肯定不知道) 9-漫游轨迹以及其动画路径(怎么绘制贝塞尔曲线,如何使用曲线上的路径,跟随路径移动的原理,相机如何运动,物体如何运动) 10-着色器(什么是着色器。初识着色器基础,着色器材质怎么用,怎么使用着色器库) 11-常见渲染以及透明度问题 12-对象拾取以及拖拽(3d世界里面如何拖拽物体,拖拽的原理,mousemove mouseon等的事件效果) 13-世界坐标以及组的问题(什么是相对坐标,什么是世界坐标,什么是当前坐标,怎么转化父子坐标系,组的优化,为什么用组,组的优势) 14-指定对象旋转中心(什么是物体的几何体中心,如何改变中心,如何绕轴转动) 15-层级对象渲染(多个场景一键切换,切换的优势,针对大项目的用法) 16-拓展了解系列(不定期不断更新案例,各种酷炫效果bloom,halo等,以及各种3d图表,粒子案例等,不断构建你的3d实践能力)

    187 人正在学习 去看看 李元继

车道线检测

····这里是车道线检测第三篇,也是最后一篇,睿智小编,在线码字。
····车道线检测,从易到难:单直线车道检测–>单弯道车道检测–>多直线车道检测–>多弯道车道检测
····我更希望用例子去说明,多按图说话。本篇分三个内容:

···· 1.讲解Udacity的CarND-LaneLines-P1-master项目
···· 2.讲解Udacity的CarND-Advanced-Lane-Lines-master项目
···· 3.讲解我在这基础上改进的multi-lane-lines-detection项目

Multi-Lane-lines-detection

···基于前两篇文章的思路进行改进,得到实时多车道检测算法。
详细代码见 https://github.com/wisdom-bob/multi-lane-lines-detection
在这里插入图片描述
····如图所示,为项目结果图,边缘拟合度高,基于hough变换,实时性高,效果稳定,这里直接设定最高检测三车道,满足变道功能需求,偏航预警需求,车道跟随需求。
····主要思路:对输入图像通过灰度化、高斯滤波、canny边缘检测;再框选出感兴趣区域,对感兴趣区域内的二值化点集进行hough变换,得到目标直线集;基于车道宽度和车道线斜率筛选数据,再对直线集进行分类、合并得到目标车道线,设计滚动平滑器,输出平滑变化车道线。

思路讲解

····我们通过一帧图片来说明处理过程。
在这里插入图片描述
····如图[image]所示为图像原图,通过灰度化处理、高斯滤波,基于Canny算子对图像进行边缘检测二值化,关于canny的相关理论可以见车道线检测(一),基于ROI得到我们需要的目标检测区域,到这里就完成了车道线的图像预处理。

#canny
gray = grayscale(image)
blur_gray = gaussian_blur(gray,7)
edges = canny(blur_gray, 65, 170)
masked_edges = region_of_interest(edges,[interest])

#  img, rho, theta, threshold, min_line_len, max_line_gap
lines = hough_lines(masked_edges, 1, np.pi/180, 15, 1, 130)
result = weighted_img(image, lines, 0.9, 0.9)

····针对[roi]图像,进行hough变换,得到结果如[lines]所示,针对这些直线根据角度值进行筛选,合并和剔除,得到最终车道线,如图[line],完成多车道检测,不过从图像检测结果也可以看出算法并没有检测出右侧车道,虽然二值化已经得到明显的右侧道结果,但由于点之间间隔较大且均匀,而被忽略掉了。

重要函数解释

基于车道线检测(一)、(二),主要进行改变的地方就是增加了平滑器,改进了hough_lines函数,实现了多车道检测功能,下面针对改进的这两个函数,hough_linesSmooth进行讲解说明。

hough_lines

执行Hough变换,基于输出结果lines,进行角度筛选,分类合并,输出结果(当然中间还要经过Smooth平滑器)。
在这里插入图片描述
我们明确针对三车道检测,根据车道宽度3.7m,可以算出四条车道线的角度范围,如上图所示,我们可以发现|k|>0.2,基于角度条件筛选车道线;转化车道线特征为斜截式(y=530为底线),对车道线进行分类,把其中截距和斜率近似的车道线归为一类;基于分类结果,剔除匹配目标太少的目标,得到当帧的稳定检测结果,传入Smooth进行数据记录;Smooth,针对记录数据,输出平滑结果,作为当帧输出结果。

	def hough_lines(img, rho, theta, threshold, min_line_len, max_line_gap):
    # 基于houghLinesP的结果进行特征处理,转化直线特征成斜截式,基于相似度函数对直线进行分类,
    # 剔除数量过少的目标,把车道线均值传入平滑器
    
    # a为声明全局Smooth类,用法下文介绍
    global a
    
    # 预处理后的图像传入houghlinesP,完成hough变换
    lines = cv2.HoughLinesP(img, rho, theta, threshold, np.array([]), minLineLength=min_line_len, maxLineGap=max_line_gap)
    line_img = np.zeros([img.shape[0], img.shape[1], 3], dtype=np.uint8)
    lines2 = []
    try:
	    # 遍历检测结果,根据k取值,筛选车道线结果
        for line in lines:
            for x1,y1,x2,y2 in line:
                if abs(y1-y2) < 10:#10
                    continue
                k = float(y2-y1)/(x2-x1)
                if abs(1/k)>5:
                    continue
                # 输出结果统一,转化为斜截式
                if y1 > y2:
                    extend = int(x2 + (height-y2)/k)
                    lines2.append([x2-x1, y2, k, extend])
                elif y1 < y2:
                    extend = int(x1 + (height-y1)/k)
                    lines2.append([x2-x1, y1, k, extend])
        lines3 = [[lines2[0]]]
        
		# 基于相似性函数,对车道线进行分类,把同一条车道线数据分成一类,这里需要调参mindif
        for j in range(len(lines2)):
            mindif=10
            tempid=0
            side=lines2[j]
            for ID in range(len(lines3)):
                temp = SimularMea1(lines3[ID][0][2],lines3[ID][0][3],side[2],side[3])# 相似度计算函数
                if temp < mindif:
                    mindif = temp
                    tempid = ID    
            if mindif > 7:
                lines3.append([side])
            else:
                lines3[tempid].append(side)
        
        # 基于分类结果,取平均值,得到lines4,并把结果传输给smooth,这里对+=操作重载操作
        lines4=[]
        for side in lines3:
            if len(side)<3:
                continue
            side=np.array(side)
            h2 = side[:, 1].min()
            side[:,0] /= side[:,0].min()
            k1 = np.average(side[:,2], weights=side[:,0])
            x1 = np.average(side[:,3], weights=side[:,0])
            lines4.append([int(x1), height, int(x1-(height-h2)/k1), int(h2)])
        a += np.array(lines4)
    except:
        pass
    # 从smooth中取出平滑结果,作为当帧输出。
    lines5 = a.data.mean(axis=0)
    draw_lines(line_img, [lines5], thickness=5)
    return line_img

下面对平滑器进行讲解说明

# 滚动平滑器,通过滚动平滑,不断更新记录的车道线,每条车道线保留window_size个数据,输出为平均值,
# 使车道线显示平滑更新,结果更稳定,鲁棒性更高。
class Smooth:
    def __init__(self, windowsize=10):
        # 初始化,window_size代表同一根车道线最多保留多少帧结果,实际输出为平均值!
        # index记录帧数,lenth记录有效车道数,threshold需要调参,time记录每条车道线的检测数,VP记录灭点像素坐标
        # data,保留window_size帧数据,最多10条车道线,每条车道线有4个参数
        self.window_size = windowsize
        self.data = np.zeros((self.window_size, 10, 4), dtype=np.float32)
        self.index = 0
        self.lenth = 0
        self.threshold = 300
        self.time = np.array([0,0,0,0,0,0,0,0,0,0],dtype=np.int8)
        self.VP = None
    
    def __iadd__(self, x):# 对接重载的+=操作
        if self.index == 0:
		# 初始化
            self.lenth = len(x)
            self.data[:,0:self.lenth] = x 
            self.time[0:self.lenth] = 2
        index = self.index % self.window_size
        
        # 当帧车道线与跟踪数据对比,判断是否属于,是则加入data中作为更新值,否则丢弃数据或者作为新跟踪目标;当有灭点时,
        # 判断是否经过灭点,去掉不经过的车道线;实时更新time值,>0为有效目标,否则无效;每3帧更新一次灭点和跟踪数目。
        for data in x:
            mindif = self.threshold
            tempid = 0
            for ID in range(self.lenth):
                Data = self.data[index][ID]
                temp = abs(data[0]-Data[0])# 另外构建SimularMea2代入
                if temp < mindif:
                    mindif = temp
                    tempid = ID
            if self.VP is not None:
                if disP2L(self.VP,data)>15:# 灭点到线距离,大于阈值15,则认为不经过灭点,需要调参。
                    continue                
            # 若未检测到,则增设为新的跟踪车道线,否则作为某条车道线在平滑器中记录的当帧更新值
            if mindif == self.threshold:
                self.data[:,self.lenth] = data
                self.time[self.lenth] = 3
                self.lenth += 1
            else:
                self.data[index][tempid] = data
                self.time[tempid] += 2
        self.time[self.time>0] -=1
		
		# 每3帧,计算一次灭点,剔除无效车道线。
        if self.index % 3 == 0:
            targetid=np.nonzero(self.time)
            templen=len(targetid[0])
            
            if templen != self.lenth:
                temp=self.data[:,targetid,:].mean(axis=0)
                self.data=np.zeros((self.window_size, 10, 4), dtype=np.float32)
                self.data[:,0:templen,:] = temp
                
                temp=self.time[targetid]
                self.time = np.array([0,0,0,0,0,0,0,0,0,0],dtype=np.int8)
                self.time[0:templen]=temp
            
                self.lenth=templen

            temp=self.data.mean(axis=0)
            if self.lenth >2:
                self.VP=CatchVanishP(temp,self.lenth)        
 
        self.index += 1
        return self

总结和拓展

····至此,完成基于Hough变换的改进直线车道多车道检测,只在cpu上跑可达到50帧,满足实时性要求,算是非常好的结果,基于滚动平滑器,车道线检测结果非常稳定,只是偶尔会有错帧,效果好了许多,完整代码见github。
····从优化角度去考虑,还有以下可改进地方:hough变换效果有待提高,偶尔良好的二值化结果却无法实现变换,是个糟糕的事情;相似度函数可以进一步改进,提高匹配度,进而有更好的结果,偶尔的错帧偏移都是由于匹配不够好,分类效果不够好导致的。
····快去试试吧~~

没有更多推荐了,返回首页