从车道线检测入门OpenCV

Posted *大祺

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从车道线检测入门OpenCV相关的知识,希望对你有一定的参考价值。

本文为纯小白之作(So大佬读者们可以移步了),读者可结合视频,

相信大家“一定”(当否定的概率小到渺茫时我认为其为绝对事件bushi)看不到文末,

所以看到开头的小伙伴可以点个赞不?       

                                                                                                                             ——快裂开的小白

目录

#01图片的读取、展示和保存

彩色图像

灰度图像

代码

#02Canny边缘检测

边缘认定标准

代码

#03.roi_mask

fillPolly函数

numpy.array函数:

bitwise_and函数

代码

#04霍夫变换

二值图像

cv2.line()

cv2.HoughLinesP()

代码

#05离群值过滤

show me the code!

#06最小二乘拟合

polyfit()和polyval()

np.ravel()

代码片段

#07画直线

#08视频流读写

一、基础函数

#1.cap = cv2.VideoCapture()

#2.ret,frame = cap.read()

#3.cv2.resize()

#4.cv2.cvtcolor()

#5.cv2.inRange()

二、滑动条

cv2.createTrackbar()

cv2.getTrackbarPos()

cv2.setTrackbarPos()


#01图片的读取、展示和保存

彩色图像

图像中的每个像素值都分成B(蓝)、G(绿)、R(红)三个基色分量,每个基色分量直接决定其基色的强度,这样产生的色彩称为真彩色。例如图像深度为24,用B:G:R=8:8:8来表示色彩,则R、G、B各占用8位来表示各自基色分量的强度,每个基色分量的强度等级为2^8=256种。图像可容纳2^24=16M种色彩(24位色)。24位色被称为真彩色,它可以达到人眼分辨的极限,发色数是1677万多色,也就是2的24次方。

灰度图像

每个像素只有一个采样颜色的图像。这类图像通常显示为从最暗黑色到最亮的白色的灰度 ,像素0~255,越大越亮。

####waitkey()函数

cv2.waitkey(delay),delay 的单位为ms毫秒,当 delay 取大于0的值时,程序在给定的 delay 时间内等待用户按键触发,如果用户没有按下键,则继续等待下一个delay时间(循环),直到用户按键触发,退出程序。若设置delay为0,则表示必须点击窗口界面的×才能关闭程序。 若设置delay的值小于0,等待键盘按键,任何一个按键都会关闭程序,默认参数为小于0(我电脑上打印出来是-1)。 ———————————————— 版权声明:本文为CSDN博主「楊建业」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:opencv中cv2.waitkey()参数详解_csdn_bajie-CSDN博客_cv2.waitkey(0)

代码

'''_read_and_display.py

import cv2
img=cv2.imread('img.jpg',cv2.IMREAD_GRAYSCALE)
print(type(img))
print(img.shape)
'''
cv2.imshow('image',img)
k = cv2.waitKey(0)print(k)
'''
cv2.imwrite('img_gray.jpg',img)
print('yes')

常数用大写表示:cv2.IMREAD_GRAYSCALE == 0

(一开始imwrite函数不报错但却不能生成图片,然后康康学长说我的.py文件是在venv虚拟环境里的,所以跟视频里不一样,然后我点“项目”就突然好了。。。我不知道发生了啥。)

#02Canny边缘检测

边缘认定标准

 

cv2.Canny()函数,第一个参数是输入图像,第二、三个参数是minVal和maxVal

处在上、下阈值之间的为弱边缘,大于上阈值的为强边缘(我们认为它肯定是边缘),弱边缘中与强边缘相连的才视为真正的边缘:

如上图将c看作边缘,b为噪声。

 

 

选取四个梯度方向(实际上相当于八个方向,因为有正负)

代码

'''_edge_detect.py

import cv2
img = cv2.imread('img.jpg',cv2.IMREAD_GRAYSCALE)
edge_img=cv2.Canny(img,70,120)
cv2.imshow('edges',edge_img)
cv2.waitKey(0)

#03.roi_mask

·ROI ( region of interest , 感兴趣的区域 )

·数组切片

·布尔运算(与运算)

np.zeros_like(a) : 构建一个与a同维度的数组,并初始化为0.

fillPolly函数

fillPolly(img,ppt,npt,1,Scalar(255,255,255),lineType)

函数参数:

1.多边形将被画到img上

2.多边形的顶点集为ppt(注意点的顺序)

3.绘制的多边形顶点数目为npt

4.要绘制的多边形数量为1

5.多边形的颜色定义为Scarlar(255,255,255),即RGB的值为白色

注意:点构成的边界,要加中括号

颜色原理:

按位与:因为掩膜图片中ROI区域的白色部分,即255(二进制是11111111,故与此部分相与仍是原数,

非ROI区域黑色,即00000000,与全0相与那么就会变成0,即不感兴趣的部分就会变成黑色

numpy.array函数:

一个常见的错误包括用多个数值参数调用array而不是提供一个由数值组成的列表作为一个参数。

>>> a = array(1,2,3,4)    # WRONG
​
>>> a = array([1,2,3,4])  # RIGHT 

数组将序列包含序列转化成二维的数组,序列包含序列包含序列转化成三维数组等等。

>>> b = array( [ (1.5,2,3), (4,5,6) ] )
>>> b
array([[ 1.5,  2. ,  3. ],
       [ 4. ,  5. ,  6. ]]) 

数组类型可以在创建时显示指定

>>> c = array( [ [1,2], [3,4] ], dtype=complex )
>>> c
array([[ 1.+0.j,  2.+0.j],
       [ 3.+0.j,  4.+0.j]]) 

还有一篇讲得很详细:

numpy.array函数详解Android 小萌新的博客-CSDN博客np.array函数

bitwise_and函数

函数原型: bitwise_and(src1, src2, dst=None, mask=None)

参数说明: ·src1、src2:为输入图像或标量,标量可以为单个数值或一个四元组 · dst:可选输出变量,如果需要使用非None则要先定义,且其大小与输 入变量相同 ·mask:图像掩膜,可选参数,为8位单通道的灰度图像,用于指定要更改的输出图像数组的元素,即输出图像像素只有mask对应位置元素不为0的部分才输出,否则该位置像素的所有通道分量都设置为0

代码

'''roi_mask.py

import cv2
import numpy as np
​
edge_img =cv2.imread('img.jpg',cv2.IMREAD_GRAYSCALE)
mask = np.zeros_like(edge_img)
mask2 = cv2.fillPoly(mask,np.array([[[0,368],[280,210],[360,210],[640,368]]]),color=255)
masked_edge_img = cv2.bitwise_and(edge_img, mask2)
​
cv2.imshow('newmask',masked_edge_img)
cv2.waitKey(0)

 

 

#04霍夫变换

霍夫变换(其一:直角坐标-->极坐标 找曲线交点

二值图像

二值图像(也可表示为 黑白、B&W、单色图像 )是每个像素只有两个可能值(0或225)的数字图像。但是也可以用来表示每个像素只有一个采样值的任何图像,例如灰度图像等。二值图像只能展示边缘信息。

cv2.line()

用于“画直线”

cv2.line(img, pt1, pt2, color[, thickness[, lineType[, shift]]]) → img

·img,背景图 ·pt1,直线起点坐标 ·pt2,直线终点坐标 ·color,当前绘画的颜色。如在BGR模式下,传递(255,0,0)表示蓝色画笔。灰度图下,只需要传递亮度值即可。 ·thickness,画笔的粗细,线宽。若是-1表示画封闭图像,如填充的圆。默认值是1. ·lineType,线条的类型, 如8-connected类型、anti-aliased线条(反锯齿),默认情况下是8-connected样式ide,cv2.LINE_AA表示反锯齿线条,在曲线的时候视觉效果更佳。

注:pt1, pt2 必须要为元组(tuple)形式,不能为列表

cv2.HoughLinesP()

lines = cv2.HoughLinesP(image, rho, theta, threshold, minLineLength, maxLineGap)

● image 是输入图像,即源图像,必须为 8 位的单通道二值图像。对于其他类型的图像,在进行霍夫变换之前,需要将其修改为这个指定的格式。

● rho 为以像素为单位的距离 r 的精度。一般情况下,使用的精度是 1。(值越大,考虑越多的线.)

● theta 是角度 θ 的精度。一般情况下,使用的精度是 np.pi/180,表示搜索所有可能的角度。

● threshold 是阈值。该值越小,判定出的直线越多;值越大,判定出的直线就越少。

识别直线时,要判定有多少个点位于该直线上。在判定直线是否存在时,对直线所穿过的点的 数量进行评估,如果直线所穿过的点的数量小于阈值,则认为这些点恰好(偶然)在算法上构 成直线,但是在源图像中该直线并不存在;如果大于阈值,则认为直线存在

● minLineLength 用来控制「接受直线的最小长度」的值,默认值为 0。

● maxLineGap 用来控制接受共线线段之间的最小间隔,即在一条线中两点的最大间隔。

如果两点间的间隔超过了参数 maxLineGap 的值,就认为这两点不在一条线上。默认值为 0。

● 返回值:x1,y1,x2,y2.

[ np.array ( [ [x1, y1, x2, y2] ]),

np.array ( [ [x1, y1, x2, y2] ]),

...,

np.array ( [ [x1, y1, x2, y2] ]) ]

———————————————— 版权声明:本文为CSDN博主「江南蜡笔小新」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:[OpenCV] HoughLines和HoughLinesP的区别与不同效果展示_江南蜡笔小新-CSDN博客_cv2.houghlines

代码

’‘’

import cv2
import numpy as np
def calculate_slope(line):
    '''    功能:计算线段line的斜率
    :param line: np.arry([[x1,y1,x2,y2]])
    :return:
    '''
    x1,y1,x2,y2=line[0]
    return (y1-y2)/(x1-x2)
def reject_abnormal_lines(lines,threshold):
    '''    剔除斜率离群的线段
    :param lines: 线段集合,[np.array([[x1, y1, x2, y2]]),...]
    :param threshold: 允许最小误差
    :return:斜率在误差内一致的线段
    '''
    slopes = [calculate_slope(line)for line in lines]
    while len(lines) > 0:
        mean = np.mean(slopes)
        diff = [abs(s - mean)for s in slopes]
        idx = np.argmax(diff)
        if diff[idx] > threshold:
            slopes.pop(idx)
            lines.pop(idx)
            else:
                break
                return lines
            edge_img=cv2.imread('masked_edge.jpg',cv2.IMREAD_GRAYSCALE)
            lines = cv2.HoughLinesP(edge_img,1,np.pi/180,15, minLineLength=40,maxLineGap=20)
            left_lines = [line for line in lines if calculate_slope(line)>0]
            right_lines = [line for line in lines if calculate_slope(line)<0]
    print(len(lines))
    print(len(left_lines))
    print(len(right_lines))
    reject_abnormal_lines(left_lines,0.07)
    reject_abnormal_lines(right_lines,0.07)
    print(len(left_lines))
    print(len(right_lines))

#05离群值过滤

show me the code!

'''

import cv2
import numpy as np
def calculate_slope(line):    
    '''    功能:计算线段line的斜率
    :param line: np.arry([[x1,y1,x2,y2]])
    :return:
    '''
    x1,y1,x2,y2=line[0]
    return (y1-y2)/(x1-x2)
def reject_abnormal_lines(lines,threshold):
    '''    剔除斜率离群的线段
    :param lines: 线段集合,[np.array([[x1, y1, x2, y2]]),...]
    :param threshold: 允许最小误差
    :return:斜率在误差内一致的线段
    '''
    slopes = [calculate_slope(line)for line in lines]
    while len(lines) > 0:
        mean = np.mean(slopes)  #求平均斜率
        diff = [abs(s - mean)for s in slopes]
        idx = np.argmax(diff)   #最大差的斜率元素下标
        if diff[idx] > threshold:
            slopes.pop(idx)
            lines.pop(idx)
        else:
            break
    return lines
edge_img=cv2.imread('masked_edge.jpg',cv2.IMREAD_GRAYSCALE)
lines = cv2.HoughLinesP(edge_img,1,np.pi/180,15, minLineLength=40,maxLineGap=20)
#注意此处斜率跟我们平时的相反
left_lines = [line for line in lines if calculate_slope(line)<0]  
right_lines = [line for line in lines if calculate_slope(line)>0]
print(len(lines))
print(len(left_lines))
print(len(right_lines))
reject_abnormal_lines(left_lines,0.07)
reject_abnormal_lines(right_lines,0.07)
print(len(left_lines))
print(len(right_lines))

#06最小二乘拟合

polyfit()和polyval()

polyval是求值函数

polyfit是曲线拟合函数,主要用于多项式曲线拟合

p = polyfit(x, y, m)

其中 x, y 为已知数据点向量的横纵坐标,m为拟合多项式的次数,结果返回m次拟合多项式系数,从高到低次存放在向量p中。

y_0 = polyval(p, x_0)

可求得多项式在x_0处的值为y_0

如:输入

p = [4 2 1]

x = [5 6 7]

polyval(p, x)

可以得到多项式p(x)= 4 * x^2+2*x+1在x=[5 6 7]的值

np.ravel()

将多维数组降为一维。

代码片段

‘’‘

def least_squares_fit(lines):
"""
	将lines中的线段拟合成一条线段
	:param lines: 线段集合, [np.array([[x_1, y_1, x_2, y_2]]),np.array([[x_1, y_1, x_2, y_2]]),...,np.array([[x_1, y_1, x_2, y_2]])]
    :return: 线段上的两点,np.array([[xmin, ymin], [xmax, ymax]])
"""    
	# 1. 取出所有坐标点
    x_coords = np.ravel([[line[0][0], line[0][2]] for line in lines])
    y_coords = np.ravel([[line[0][1], line[0][3]] for line in lines])
    # 2. 进行直线拟合.得到多项式系数
    poly = np.polyfit(x_coords, y_coords, deg=1)
    # 3. 根据多项式系数,计算两个直线上的点,用于唯一确定这条直线
    point_min = (np.min(x_coords), np.polyval(poly, np.min(x_coords)))
    point_max = (np.max(x_coords), np.polyval(poly, np.max(x_coords)))
    return np.array([point_min, point_max], dtype=np.int)
print("left lane")
print(least_squares_fit(left_lines))
print("right lane")
print(least_squares_fit(right_lines))
 

#07画直线

plus:(画直线问题之种种)函数见cv2.line()

1.因为左上角是原点,所以k>0是从左上往右下走的,这个k和我们平时的是反的

2.关于直线颜色,cv2.imread()读进来的是BGR格式的,不是我们最常见的RGB格式,即(255,0,0)是蓝色不是红色。

#08视频流读写

通过不断循环读取视频的每一帧

一、基础函数

#1.cap = cv2.VideoCapture()

VideoCapture()中参数是0,表示打开笔记本的内置摄像头,参数是视频文件路径则打开视频,

#2.ret,frame = cap.read()

cap.read()按帧读取视频,ret,frame是获cap.read()方法的两个返回值。其中ret是布尔值,如果读取帧是正确的则返回True,如果文件读取到结尾,它的返回值就为False。frame就是每一帧的图像,是个三维矩阵。

#3.cv2.resize()

cv2.resize(src, dsize[, dst[, fx[, fy[, interpolation]]]]) -> dst 参数说明:

src :需要改变尺寸的图像 dsize:目标图像大小 dst:目标图像 fx:w方向上的缩放比例 fy:h方向上的缩放比例 interpolation - 插值方法。共有5种(此处略)。

有三点需要注意:

1.dsize的形状是(w,h),而opencv读取出来的图像的形状是(h,w) 2.当参数dsize不为0时,dst的大小为dsize;否则,由src的大小以及缩放比例fx和fy来决定;可以看出dsize和(fx,fy)两者不能同时为0 3.因为dsize是没有默认值的,所以必须指定,也即我们使用fx和fy来控制大小的时候必须设置dsize=(0,0)

#4.cv2.cvtcolor()

hsv = cv2.cvtColor(rgb_image, cv2.COLOR_BGR2HSV)

#5.cv2.inRange()

mask = cv2.inRange(hsv, lower_red, upper_red)

参数说明:

hsv:原图

lower_red:图像中低于 lower_red的值,图像值变为0

upper_red:图像中高于upper_red的值,图像值变为0

二、滑动条

cv2.createTrackbar()

功能:绑定滑动条和窗口,定义滚动条的数值。

cv2.createTrackbar(“scale”, “display”, 0, 100, self.opencv_calibration_node.on_scale)

*参数意义:*

  • 第一个参数:滑动条的名字,

  • 第二个参数:滑动条被放置的窗口的名字,

  • 第三个参数:滑动条默认值,

  • 第四个参数:滑动条的最大值,

  • 第五个参数:回调函数,每次滑动都会调用回调函数。

cv2.getTrackbarPos()

功能:得到滑动条的数值。

cv2.getTrackbarPos("hue min", "TrackBars")

参数:

·第一个参数是滑动条名字,

·第二个是所在窗口,

·返回值是滑动条的数值

cv2.setTrackbarPos()

功能:设置滑动条的默认值。

cv2.setTrackbarPos(‘Alpha’, ‘image’, 100)

参数:

  • 第一个参数是滑动条名字,

  • 第二个是s所在窗口,

  • 第三个参数是滑动条默认值,

附:

 

以上是关于从车道线检测入门OpenCV的主要内容,如果未能解决你的问题,请参考以下文章

opencv入门项目——车道线检测

python+openCV (入门级)车道线检测 学习笔记

基于Python+OpenCV车道线检测(直道和弯道)

基于Python+OpenCV车道线检测(直道和弯道)

无人驾驶的图像识别之车道线检测

车道线检测算法-Ultra-Fast-Lane-Detection