世上无难事,只要肯放弃!
这次本来打算做一个停车场视频,把空车位框起来的,但是由于我的pytorch始终没有办法构建好自己的数据集,虽然有大佬给的keras训练好的模型,而恰好我使用的是python3.8,安装keras有点麻烦。并且我很懒,所以这次试手只完成了一半。
好了,话不多说,开始。
先从视频里截一张图
经典开局两件套
import cv2
import numpy as np
import torch
def imshow(img):
cv2.imshow(str(img),img)
cv2.waitKey(0)
cv2.destroyAllWindows()
由于截图左上角部分是不需要的,所以先获取我们想要的部分
'''
获取停车场想要的部分
'''
img = img_gray.copy()
(y,x) = img.shape[:2]
# 用jupyter 手动调整的参数
mask = [(0.05*x,0.7*y),(0.34*x,0.5*y),(0.58*x,0.12*y),(0.91*x,0.12*y),(0.91*x,0.88*y),(0.05*x,0.88*y)]
I = np.zeros_like(img)
mask = np.array([mask],dtype = np.int)
# cv2.fillPoly 可以用任意点围成多边形并且指定围成图形的值
# mask 是一个三维矩阵 相当于只有一个元素 该元素有n个点 每个点有2个坐标x,y 可以用jupyter type一下看看类型
# 所以mask = np.array 时 list mask要多加一个[]
cv2.fillPoly(I,mask,255)
# cv2.bitwise_and() 对二者进行与操作
# 要求二者同类型 同数据类型 所以用zeros_like 生成I
img = cv2.bitwise_and(img, I)
imshow(img)
- 接下来是直线检测
'''
直线检测
'''
# 先要进行边缘检测
img = cv2.Canny(img,160,220)
# 直线检测
#输入的图像需要是边缘检测后的结果
#minLineLengh(线的最短长度,比这个短的都被忽略)和MaxLineCap(两条直线之间的最大间隔,小于此值,认为是一条直线)
#rho距离精度,theta角度精度,threshod超过设定阈值才被检测出线段
# 返回值是个三维数组 (n,1,4) n条直线,1,4个值x1,y1,x2,y2
lines = cv2.HoughLinesP(img, rho=0.1, theta=np.pi/10, threshold=15, minLineLength=9, maxLineGap=4)
简单讲一下 hough直线检测,
hough直线检测是把y=kx+b 通过x=rcos()转换成极坐标,
这样得到r和theta的一点,对应y=kx+b一条直线。
当极坐标的点重合的越多,说明共y=kx+b这条线上的线段越多。
当重合的数量达到一定阈值,就认为存在一条直线。
什么,你说你不知道canny边缘检测?
canny我的理解简单来说就是选择局部最大梯度的点
然后比较大小,小于第一个参数的直接判断非边缘
大于第二个参数的判断为边缘
大于第一个,小于第二个则看它旁边的点是否为边缘
- 筛选直线
# 筛选直线
img_1 = img_rgb.copy()
line_n =[]
for line in lines:
x1,y1,x2,y2 = line[0]
if abs(x2-x1) <55 and abs(x2-x1)>25:
if abs(y2-y1)<1:
line_n.append((x1,y1,x2,y2))
# cv2.line(图像,起点,终点,颜色,粗)
cv2.line(img_1,(x1,y1),(x2,y2),(0,255,0),2)
imshow(img_1)
import operator
# operator.itemgetter是一个函数,获取对应维的数
# 按起点对列表排序
line = sorted(line_n, key=operator.itemgetter(0, 1))
- 分块
'''
分块
'''
park = {}
# 用range是为了方便访问下一个线 len-1 是避免i+1越界
num = 0
for i in range(len(line) - 1):
# distance 是两天直线起点的距离
distance = abs(line[i][0] - line[i + 1][0])
if num not in park.keys():
park[num] = []
if distance < 20:
park[num].append(line[i])
else:
park[num].append(line[i])
num += 1
''''
for j in range(len(park)):
img_2 = img_1.copy()
for i in park[j]:
cv2.line(img_2,(i[0],i[1]),(i[2],i[3]),(0,0,255),2)
imshow(img_2)
'''
用了循环,红色的是同一个块更加直观
- 把每一个块框出来
'''
把块框出来
'''
# 因为每块停车位底部是同一条直线 所以选max_y为底
max_y=0
rec = []
# 字典的遍历默认是访问键
for key in park:
min_y = img_gray.shape[0]
long = 0
sum = 0
# 找出最上方的的直线到max_y作为高,最长线的长度为宽,x均值为起点
# 因为找到的线都偏短,x的均值会偏右 所以给x设置一个偏移量
shift = 10
# 最长的线一般偏短,所以加一点补充
long_sup = 3
for line in park[key]:
distance = abs(line[2]-line[0])
if distance > long:
long = distance
if line[1]>max_y:
max_y = line[1]
if line[1]<min_y:
min_y = line[1]
sum += line[0]
avg = int(sum/len(park[key]))-shift
long += long_sup
# 其实左上和右下两个点就能表示矩形了
rec.append([[avg,max_y],[avg,min_y],[avg+long,min_y],[avg+long,max_y]])
for i in rec:
i[0][1]=max_y
i[3][1]=max_y
同样每块单独框出来
- 微调
# 对每组车位左上角的y进行细调
adjust =np.array( [-6,1,0,-4,3,-4,4,-3,0,0,-3,-5])
j=0
for i in rec:
i[1][1]+=adjust[j]
i[2][1]+=adjust[j]
j+=1
'''
for i in rec:
img_3 = img_rgb.copy()
# rectangle 里 坐标是元组
cv2.rectangle(img_3,tuple(i[1]),tuple(i[3]),(255,0,0),2)
imshow(img_3)
'''
框选车位
'''
# 将每一个单独的车位画出来
'''
# 每一个车位的高度差gap
gap = 15.5
car_park = []
item = 0
for i in rec:
height = abs(i[1][1] - i[0][1])
width = i[3][0] - i[0][0]
num = int(height / gap)
# num+ 1个空位 有一个空位不完整
for j in range(num + 1):
# 记录车位的左上和右下坐标
if item < 1 or item > 10:
car_park.append([[i[1][0], i[1][1] + int(j * gap)], [i[2][0], i[2][1] + int(j * gap + gap)]])
else:
car_park.append([[i[1][0], i[1][1] + int(j * gap)], [i[1][0] + int(width / 2), i[2][1] + int(j * gap + gap)]])
car_park.append([[i[1][0] + int(width / 2), i[1][1] + int(j * gap)], [i[2][0], i[2][1] + int(j * gap + gap)]])
item += 1
img_4 = img_rgb.copy()
for i in car_park:
cv2.rectangle(img_4, tuple(i[0]), tuple(i[1]), (255, 0, 0), 2)
imshow(img_4)
到此位置都很简单,难点在后面?
(其实我觉得也不难,但是就是报一些搜不到原因的错)
后面就是深度学习训练模型,把框的车位放入模型进行预测。然后从视频里读取帧进行框选。目前还没能成功,以后有机会再弄吧。下一期再见!