欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

2020华为杯E题--基于辅助车道线的大雾能见度估计与预测(附代码)

程序员文章站 2022-07-15 23:31:28
...

一、E题赛题

2020研究生数学建模赛题链接:https://download.csdn.net/download/qq_35759272/13028941
第三题问题:

二、赛题分析

2.1数据分析

题目给的数据分两种:机场杆子视频,高速公路视频截图。数据的区别在于:机场视频有固定距离的杆子,并且是平视,可以作为参考物,可以估计出拍摄距离(摄像机与杆子的距离);而高速公路没有固定的参考物,由于路面与摄像机成一定角度,不是平视,所以路面也不可以作为固定参考物。

2.2方案选择

对于机场视频可以采用——基于暗通道优先算法的能见度估计模型(链接:https://yuanwenjie.blog.csdn.net/article/details/110116319)。
第三题题目明确使用高速公路视频截图数据,所以采用——基于辅助车道线的大雾能见度估计与预测模型

三、基于辅助车道线的大雾能见度估计与预测模型

3.1能见度公式推导

根据 09 年出版的《公路交通标志和表现设置规范》的规定,国内道路中间车道标线的标准为:对于二级及以上等级的高速公路中的白色车道线,长度标准为 6 米,直线上相邻两标线的间隔为 9 米;对于小于二级的公路的白色车道线,长度标准为 2 米,直线上相邻两标线的间隔为 4 米。如此,只需选定图像中某一个车道线的起点和终点[18],然后分别计算起点的透射率1t 和终点的透射率2t ,结合车道线的长度M 就可以计算出图像的大气消光系数 ,具体的方法是:
(1)根据车道线求大气消光系数
在车道线起点处,根据公式有:
2020华为杯E题--基于辅助车道线的大雾能见度估计与预测(附代码)
在车道线终点处,根据公式:2020华为杯E题--基于辅助车道线的大雾能见度估计与预测(附代码)
根据上面公式求得大气消光系数:2020华为杯E题--基于辅助车道线的大雾能见度估计与预测(附代码)
(2)求能见度
从工程实践应用的角度,根据国际照明委员会(CIE)给出的定义[45]: 在对比度阈值为 0.05 时, 人眼所能观测到物体的最
大距离成为气象能见度距离(Meteorological Visibility Distance, MVD)。在目标物相对于周围环境的对比度C0=1时,就可以推导出能见度的一般公式:
2020华为杯E题--基于辅助车道线的大雾能见度估计与预测(附代码)

3.2 模型流程图

2020华为杯E题--基于辅助车道线的大雾能见度估计与预测(附代码)

3.3 解题过程

(1)车道线检测
lane_detection

from moviepy.editor import VideoFileClip
import matplotlib.pyplot as plt
import matplotlib.image as mplimg
import numpy as np
import cv2

blur_ksize = 5  # Gaussian blur kernel size
canny_lthreshold = 50  # Canny edge detection low threshold
canny_hthreshold = 150  # Canny edge detection high threshold

# Hough transform parameters
rho = 1
theta = np.pi / 180
threshold = 15
min_line_length = 40
max_line_gap = 20

def roi_mask(img, vertices):
  mask = np.zeros_like(img)

  #defining a 3 channel or 1 channel color to fill the mask with depending on the input image
  if len(img.shape) > 2:
    channel_count = img.shape[2]  # i.e. 3 or 4 depending on your image
    mask_color = (255,) * channel_count
  else:
    mask_color = 255

  cv2.fillPoly(mask, vertices, mask_color)
  masked_img = cv2.bitwise_and(img, mask)
  return masked_img

def draw_roi(img, vertices):
  cv2.polylines(img, vertices, True, [255, 0, 0], thickness=2)

def draw_lines(img, lines, color=[255, 0, 0], thickness=2):
  for line in lines:
    for x1, y1, x2, y2 in line:
      cv2.line(img, (x1, y1), (x2, y2), color, thickness)

def hough_lines(img, rho, theta, threshold, min_line_len, max_line_gap):
  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)
  # draw_lines(line_img, lines)
  draw_lanes(line_img, lines)
  return line_img

def draw_lanes(img, lines, color=[255, 0, 0], thickness=8):
  left_lines, right_lines = [], []
  for line in lines:
    for x1, y1, x2, y2 in line:
      k = (y2 - y1) / (x2 - x1)
      if k < 0:
        left_lines.append(line)
      else:
        right_lines.append(line)
  
  if (len(left_lines) <= 0 or len(right_lines) <= 0):
    return img
  
  clean_lines(left_lines, 0.1)
  clean_lines(right_lines, 0.1)
  left_points = [(x1, y1) for line in left_lines for x1,y1,x2,y2 in line]
  left_points = left_points + [(x2, y2) for line in left_lines for x1,y1,x2,y2 in line]
  right_points = [(x1, y1) for line in right_lines for x1,y1,x2,y2 in line]
  right_points = right_points + [(x2, y2) for line in right_lines for x1,y1,x2,y2 in line]
  
  left_vtx = calc_lane_vertices(left_points, 325, img.shape[0])
  right_vtx = calc_lane_vertices(right_points, 325, img.shape[0])
  
  cv2.line(img, left_vtx[0], left_vtx[1], color, thickness)
  cv2.line(img, right_vtx[0], right_vtx[1], color, thickness)
  
def clean_lines(lines, threshold):
  slope = [(y2 - y1) / (x2 - x1) for line in lines for x1, y1, x2, y2 in line]
  while len(lines) > 0:
    mean = np.mean(slope)
    diff = [abs(s - mean) for s in slope]
    idx = np.argmax(diff)
    if diff[idx] > threshold:
      slope.pop(idx)
      lines.pop(idx)
    else:
      break
  
  
def calc_lane_vertices(point_list, ymin, ymax):
  x = [p[0] for p in point_list]
  y = [p[1] for p in point_list]
  fit = np.polyfit(y, x, 1)
  fit_fn = np.poly1d(fit)
  
  xmin = int(fit_fn(ymin))
  xmax = int(fit_fn(ymax))
  
  return [(xmin, ymin), (xmax, ymax)]

def process_an_image(img):
  roi_vtx = np.array([[(0, img.shape[0]), (460, 325), (520, 325), (img.shape[1], img.shape[0])]])

  gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
  blur_gray = cv2.GaussianBlur(gray, (blur_ksize, blur_ksize), 0, 0)
  edges = cv2.Canny(blur_gray, canny_lthreshold, canny_hthreshold)
  roi_edges = roi_mask(edges, roi_vtx)
  line_img = hough_lines(roi_edges, rho, theta, threshold, min_line_length, max_line_gap)
  res_img = cv2.addWeighted(img, 0.8, line_img, 1, 0)

  '''
  plt.figure()
  plt.imshow(img)
  plt.savefig('images/lane_original.png', bbox_inches='tight')
  plt.figure()
  plt.imshow(gray, cmap='gray')
  plt.savefig('images/gray.png', bbox_inches='tight')
  plt.figure()
  plt.imshow(blur_gray, cmap='gray')
  plt.savefig('images/blur_gray.png', bbox_inches='tight')
  plt.figure()
  plt.imshow(edges, cmap='gray')
  plt.savefig('images/edges.png', bbox_inches='tight')
  plt.figure()
  plt.imshow(roi_edges, cmap='gray')
  plt.savefig('images/roi_edges.png', bbox_inches='tight')
  plt.figure()
  plt.imshow(line_img, cmap='gray')
  plt.savefig('images/line_img.png', bbox_inches='tight')
  plt.figure()
  plt.imshow(res_img)
  plt.savefig('images/res_img.png', bbox_inches='tight')
  plt.show()
  '''

  return res_img
 
# img = mplimg.imread("lane.jpg") 
# process_an_image(img)

output = 'video_2_sol.mp4'
clip = VideoFileClip("video_2.mp4")
out_clip = clip.fl_image(process_an_image)
out_clip.write_videofile(output, audio=False)

plot.py

from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import pylab

def center_spines(ax=None, centerx=0, centery=0):
  """Centers the axis spines at <centerx, centery> on the axis "ax", and
  places arrows at the end of the axis spines."""
  if ax is None:
    ax = plt.gca()
  
  # Set the axis's spines to be centered at the given point
  # (Setting all 4 spines so that the tick marks go in both directions)
  ax.spines['left'].set_position(('data', centerx))
  ax.spines['bottom'].set_position(('data', centery))
  ax.spines['right'].set_position(('data', centerx - 1))
  ax.spines['top'].set_position(('data', centery - 1))

  # Hide the line (but not ticks) for "extra" spines
  for side in ['right', 'top']:
      ax.spines[side].set_color('none')

  # On both the x and y axes...
  for axis, center in zip([ax.xaxis, ax.yaxis], [centerx, centery]):
    # Hide the ticklabels at <centerx, centery>
    formatter = CenteredFormatter()
    formatter.center = center
    axis.set_major_formatter(formatter)

  # Add offset ticklabels at <centerx, centery> using annotation
  # (Should probably make these update when the plot is redrawn...)
  xlabel, ylabel = map(formatter.format_data, [centerx, centery])
  if xlabel == ylabel:
    ax.annotate('%s' % xlabel, (centerx, centery),
            xytext=(-4, -4), textcoords='offset points',
            ha='right', va='top')
  else:
    ax.annotate('(%s, %s)' % (xlabel, ylabel), (centerx, centery),
            xytext=(-4, -4), textcoords='offset points',
            ha='right', va='top')


class CenteredFormatter(mpl.ticker.ScalarFormatter):
  """Acts exactly like the default Scalar Formatter, but yields an empty
  label for ticks at "center"."""
  center = 0
  def __call__(self, value, pos=None):
    if value == self.center:
      return ''
    else:
      return mpl.ticker.ScalarFormatter.__call__(self, value, pos)



#####
# Gaussian Filter
#####
fig = plt.figure('Gaussian Filter')

ax = fig.add_subplot(1, 2, 1)
data = np.array([[1, 4, 7, 4, 1],
                 [4, 20, 33, 30, 4],
                 [7, 33, 55, 33, 7],
                 [4, 20, 33, 20, 4],
                 [1, 4, 7, 4, 1]])
ax.text(-0.1, 0.5, r'$\frac{1}{341}$', fontsize=30) # np.sum(data) == 341
ny, nx = data.shape
tbl = plt.table(cellText=data, loc=(0, 0), cellLoc='center')
tbl.set_fontsize(20)
tc = tbl.properties()['child_artists']
for cell in tc:
  cell.set_height(0.8/ny)
  cell.set_width(0.8/nx)

# hide spines and ticks
ax.spines['left'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.set_xticks([])
ax.set_yticks([])


ax = fig.add_subplot(1, 2, 2, projection='3d')
X = np.arange(-5, 5, 0.1)
Y = np.arange(-5, 5, 0.1)
X, Y = np.meshgrid(X, Y)
Z = pylab.bivariate_normal(X, Y)
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.jet, linewidth=0)
ax.set_xticklabels([])  # hide axis labels
ax.set_yticklabels([])
ax.set_zticklabels([])
# fig.colorbar(surf, shrink=0.5, aspect=5)

#####
# Parameter space and Hough space
#####
mpl.rcParams['font.size'] = 18
mpl.rcParams['lines.linewidth'] = 3.0

fig = plt.figure('Hough Space')

x = np.arange(-0.5, 2.5, 0.01)
y = -x + 2
ax = fig.add_subplot(1, 3, 1)
plt.plot(x, y)
ax.text(0.5, 2, r'y=mx+b')
x = np.arange(0, 1, 0.01)
y = x
plt.plot(x, y, '--')
ax.text(0.3, 0.05, r'$\theta$', fontsize=25)
ax.text(0.4, 0.6, r'$\rho$', fontsize=25)
plt.xlabel('x')
plt.ylabel('y')
center_spines()
ax.set_xticklabels([])
ax.set_yticklabels([])

ax = fig.add_subplot(1, 3, 2)
ax.set_xlim([-2, 1])
ax.set_ylim([-0.25, 1])
plt.plot([-1], [0.5], 'o')
x = np.arange(-1, 0, 0.01)
y = [0.5] * len(x)
plt.plot(x, y, 'k--')
y = np.arange(0, 0.5, 0.01)
x = [-1] * len(y)
plt.plot(x, y, 'k--')
plt.xlabel('m')
plt.ylabel('b')
center_spines()
ax.set_xticklabels([])
ax.set_yticklabels([])

ax = fig.add_subplot(1, 3, 3)
ax.set_xlim([-1, 2])
ax.set_ylim([-0.25, 1])
plt.plot([1.2], [0.3], 'o')
x = np.arange(0, 1.2, 0.01)
y = [0.3] * len(x)
plt.plot(x, y, 'k--')
y = np.arange(0, 0.3, 0.01)
x = [1.2] * len(y)
plt.plot(x, y, 'k--')
plt.xlabel(r'$\theta$')
plt.ylabel(r'$\rho$')
center_spines()
ax.set_xticklabels([])
ax.set_yticklabels([])



plt.show()


(2)车道线端点判断
首先,对图像进行 Sobel 边缘检测,得到结果图如图 2-5(a)所示;
然后,对第一步中的结果图采用自下到上自右到左的方法逐行进行伸缩窗处理:

  1. 以图像边缘点为中心的矩形窗大小为 1*2,判断矩形窗的各个边长是否与图中的边缘
    像素点有焦点,若没有焦点则仅仅记录当前窗口的尺寸和位置,如果有交点则继续进
    行步骤 2;
  2. 将以该像素为中心的矩形窗变为原来的n倍,如果没有交点继续记录当前窗口的尺寸
    和位置,同时将窗口中所有的白色像素值置为 255,若有交点则继续进行步骤 3;
  3. 判断矩形窗的尺寸是否大于设定的阈值 N ,大于则停止变大,并转到步骤 1,否则转
    到步骤 2,直到图像中所有的白色像素点处理完毕。
    为了提高上述算法中车道线检测的正确率,这里只针对图像下方二分之一的部分进行处理。 得到矩形窗之后, 统计矩形窗中白色像素点的个数, 判断其是否大于像素个数的阈值 P ,
    最后标出车道线位置,如图 2-5(b)所示
    2020华为杯E题--基于辅助车道线的大雾能见度估计与预测(附代码)
    (3)能见度计算
    求大气消光系数:2020华为杯E题--基于辅助车道线的大雾能见度估计与预测(附代码)
    根据前面的公式求能见度
    2020华为杯E题--基于辅助车道线的大雾能见度估计与预测(附代码)

第二题解题——基于AlexNet深度网络的能见度估计模型
解题文章链接:2020研究生数学建模E题–AlexNet深度网络解法(大雾能见度估计与预测)(含代码)
第三题解题——基于辅助车道线的大雾能见度估计模型
(1)对于机场视频可以采用——基于暗通道优先算法的能见度估计模型
文章链接:2020华为杯E题–基于暗通道优先算法的能见度估计模型(附代码)
第四题解题——基于灰色预测的大雾能见度预测模型
解题文章链接:2020华为杯E题——基于灰色预测的大雾能见度预测模型(附代码)

本博客参考文献:
【1】《雾霾天气条件下能见度的检测与恢复算法研究》