OpenCV视频学习笔记(14)-项目实战-停车场车位识别

十四、项目实战-停车场车位识别

输入数据是视频;
步骤:
(1)统计多少停车位被占据了;
(2)统计还有多少停车位空闲着;
(3)哪个停车位被占了,哪个没被占标识出来;
实现步骤:
(1)边缘检测,检测每一个车位的位置(坐标点),同时还要过滤掉干扰项;
(2)把每个位置裁剪出来,做建模操作,判别位置有没有被占据,也就是进行分类任务;
Parking.py
import matplotlib.pyplot as plt
import cv2
import os, glob
import numpy as np

class Parking:

def show_images(self, images, cmap=None):
    cols = 2
    rows = (len(images)+1)//cols
    
    plt.figure(figsize=(15, 12))
    for i, image in enumerate(images):
        plt.subplot(rows, cols, i+1)
        cmap = 'gray' if len(image.shape)==2 else cmap
        plt.imshow(image, cmap=cmap)
        plt.xticks([])
        plt.yticks([])
    plt.tight_layout(pad=0, h_pad=0, w_pad=0)
    plt.show()

def cv_show(self,name,img):
    cv2.imshow(name, img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
def select_rgb_white_yellow(self,image): 
    #过滤掉背景
    lower = np.uint8([120, 120, 120])
    upper = np.uint8([255, 255, 255])
    # lower_red和高于upper_red的部分分别变成0,lower_red~upper_red之间的值变成255,相当于过滤背景
    white_mask = cv2.inRange(image, lower, upper)
    self.cv_show('white_mask',white_mask)
    
    masked = cv2.bitwise_and(image, image, mask = white_mask)
    self.cv_show('masked',masked)
    return masked
def convert_gray_scale(self,image):
    return cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
def detect_edges(self,image, low_threshold=50, high_threshold=200):
    return cv2.Canny(image, low_threshold, high_threshold)

def filter_region(self,image, vertices):
    """
            剔除掉不需要的地方
    """
    mask = np.zeros_like(image)
    if len(mask.shape)==2:
        cv2.fillPoly(mask, vertices, 255)
        self.cv_show('mask', mask)    
    return cv2.bitwise_and(image, mask)#保留mask为255的地方,其他被过滤掉

def select_region(self,image):
“”"
手动选择区域
“”"
# first, define the polygon by vertices
rows, cols = image.shape[:2]
pt_1 = [cols0.05, rows0.90]
pt_2 = [cols0.05, rows0.70]
pt_3 = [cols0.30, rows0.55]
pt_4 = [cols0.6, rows0.15]
pt_5 = [cols0.90, rows0.15]
pt_6 = [cols0.90, rows0.90]

    vertices = np.array([[pt_1, pt_2, pt_3, pt_4, pt_5, pt_6]], dtype=np.int32) 
    point_img = image.copy()       
    point_img = cv2.cvtColor(point_img, cv2.COLOR_GRAY2RGB)#画出区域的点之前要先把灰度图转化回来
    for point in vertices[0]:
        cv2.circle(point_img, (point[0],point[1]), 10, (0,0,255), 4)
    self.cv_show('point_img',point_img)
    

    
    return self.filter_region(image, vertices)

def hough_lines(self,image):#检测线段在哪,用霍夫变换
    #输入的图像需要是边缘检测后的结果
    #minLineLengh(线的最短长度,比这个短的都被忽略)和MaxLineCap(两条直线之间的最大间隔,小于此值,认为是一条直线)
    #rho距离精度,theta角度精度,threshod超过设定阈值才被检测出线段
    return cv2.HoughLinesP(image, rho=0.1, theta=np.pi/10, threshold=15, minLineLength=9, maxLineGap=4)
    
def draw_lines(self,image, lines, color=[255, 0, 0], thickness=2, make_copy=True):#画线
    # 过滤霍夫变换检测到直线
    if make_copy:
        image = np.copy(image) #复制图像
    cleaned = []
    for line in lines:
        for x1,y1,x2,y2 in line:
            if abs(y2-y1) <=1 and abs(x2-x1) >=25 and abs(x2-x1) <= 55:#y2-y1判断倾斜程度,倾斜的话不是停车位的线,x2-x1如果太大的话,那有可能是整个停车场的边界
                cleaned.append((x1,y1,x2,y2))
                cv2.line(image, (x1, y1), (x2, y2), color, thickness)
    print(" No lines detected: ", len(cleaned))#做了线的过滤后,打印剩下的线
    return image
def identify_blocks(self,image, lines, make_copy=True):
    if make_copy:
        new_image = np.copy(image)
    #Step 1: 过滤部分直线
    cleaned = []
    for line in lines:
        for x1,y1,x2,y2 in line:
            if abs(y2-y1) <=1 and abs(x2-x1) >=25 and abs(x2-x1) <= 55:
                cleaned.append((x1,y1,x2,y2))
    
    #Step 2: 对直线按照x1进行排序
    import operator
    list1 = sorted(cleaned, key=operator.itemgetter(0, 1))
    
    #Step 3: 找到多个列,相当于每列是一排车
    clusters = {}
    dIndex = 0
    clus_dist = 10

    for i in range(len(list1) - 1):
        distance = abs(list1[i+1][0] - list1[i][0])
        if distance <= clus_dist:
            if not dIndex in clusters.keys(): clusters[dIndex] = []#每一个cluster表示每一列
            clusters[dIndex].append(list1[i])
            clusters[dIndex].append(list1[i + 1]) 

        else:
            dIndex += 1
    
    #Step 4: 得到坐标
    rects = {}
    i = 0
    for key in clusters:
        all_list = clusters[key]#对于每一个列,找到停车位的每一条线
        cleaned = list(set(all_list))
        if len(cleaned) > 5:
            cleaned = sorted(cleaned, key=lambda tup: tup[1])
            avg_y1 = cleaned[0][1]
            avg_y2 = cleaned[-1][1]
            avg_x1 = 0
            avg_x2 = 0
            for tup in cleaned:#算x比较麻烦,所以求平均值
                avg_x1 += tup[0]
                avg_x2 += tup[2]
            avg_x1 = avg_x1/len(cleaned)
            avg_x2 = avg_x2/len(cleaned)
            rects[i] = (avg_x1, avg_y1, avg_x2, avg_y2)
            i += 1
    
    print("Num Parking Lanes: ", len(rects))
    #Step 5: 把列矩形画出来
    buff = 7
    for key in rects:
        tup_topLeft = (int(rects[key][0] - buff), int(rects[key][1]))
        tup_botRight = (int(rects[key][2] + buff), int(rects[key][3]))
        cv2.rectangle(new_image, tup_topLeft,tup_botRight,(0,255,0),3)
    return new_image, rects

def draw_parking(self,image, rects, make_copy = True, color=[255, 0, 0], thickness=2, save = True): 
    if make_copy:
        new_image = np.copy(image)
    gap = 15.5#固定好车位间的距离 
    spot_dict = {} # 字典:一个车位对应一个位置
    tot_spots = 0
    #微调
    adj_y1 = {0: 20, 1:-10, 2:0, 3:-11, 4:28, 5:5, 6:-15, 7:-15, 8:-10, 9:-30, 10:9, 11:-32}
    adj_y2 = {0: 30, 1: 50, 2:15, 3:10, 4:-15, 5:15, 6:15, 7:-20, 8:15, 9:15, 10:0, 11:30}
    
    adj_x1 = {0: -8, 1:-15, 2:-15, 3:-15, 4:-15, 5:-15, 6:-15, 7:-15, 8:-10, 9:-10, 10:-10, 11:0}
    adj_x2 = {0: 0, 1: 15, 2:15, 3:15, 4:15, 5:15, 6:15, 7:15, 8:10, 9:10, 10:10, 11:0}
    for key in rects:
        tup = rects[key]
        x1 = int(tup[0]+ adj_x1[key])
        x2 = int(tup[2]+ adj_x2[key])
        y1 = int(tup[1] + adj_y1[key])
        y2 = int(tup[3] + adj_y2[key])
        cv2.rectangle(new_image, (x1, y1),(x2,y2),(0,255,0),2)
        num_splits = int(abs(y2-y1)//gap)
        for i in range(0, num_splits+1):
            y = int(y1 + i*gap)
            cv2.line(new_image, (x1, y), (x2, y), color, thickness)
        if key > 0 and key < len(rects) -1 :        
            #中间还要切分一下竖直线
            x = int((x1 + x2)/2)
            cv2.line(new_image, (x, y1), (x, y2), color, thickness)
        # 计算数量
        if key == 0 or key == (len(rects) -1):
            tot_spots += num_splits +1
        else:
            tot_spots += 2*(num_splits +1)
            
        # 字典对应好
        if key == 0 or key == (len(rects) -1):
            for i in range(0, num_splits+1):
                cur_len = len(spot_dict)
                y = int(y1 + i*gap)
                spot_dict[(x1, y, x2, y+gap)] = cur_len +1        
        else:
            for i in range(0, num_splits+1):
                cur_len = len(spot_dict)
                y = int(y1 + i*gap)
                x = int((x1 + x2)/2)得到画竖直线的中间位置的坐标
                spot_dict[(x1, y, x, y+gap)] = cur_len +1
                spot_dict[(x, y, x2, y+gap)] = cur_len +2   
    
    print("total parking spaces: ", tot_spots, cur_len)
    if save:#保存一下划分完的图像
        filename = 'with_parking.jpg'
        cv2.imwrite(filename, new_image)
    return new_image, spot_dict

def assign_spots_map(self,image, spot_dict, make_copy = True, color=[255, 0, 0], thickness=2):
    if make_copy:
        new_image = np.copy(image)
    for spot in spot_dict.keys():
        (x1, y1, x2, y2) = spot
        cv2.rectangle(new_image, (int(x1),int(y1)), (int(x2),int(y2)), color, thickness)
    return new_image

def save_images_for_cnn(self,image, spot_dict, folder_name ='cnn_data'):
   #自己把有车没车的车位的数据裁减好,指定好,用于后面训练模型
 for spot in spot_dict.keys():
        (x1, y1, x2, y2) = spot
        (x1, y1, x2, y2) = (int(x1), int(y1), int(x2), int(y2))
        #裁剪
        spot_img = image[y1:y2, x1:x2]
        spot_img = cv2.resize(spot_img, (0,0), fx=2.0, fy=2.0) 
        spot_id = spot_dict[spot]
        
        filename = 'spot' + str(spot_id) +'.jpg'
        print(spot_img.shape, filename, (x1,x2,y1,y2))
        
        cv2.imwrite(os.path.join(folder_name, filename), spot_img)
def make_prediction(self,image,model,class_dictionary):
    #预处理
    img = image/255.

    #转换成4D tensor
    image = np.expand_dims(img, axis=0)

    # 用训练好的模型进行训练
#train.py 用VGG16网络模型
    class_predicted = model.predict(image)
    inID = np.argmax(class_predicted[0])
    label = class_dictionary[inID]
    return label
def predict_on_image(self,image, spot_dict , model,class_dictionary,make_copy=True, color = [0, 255, 0], alpha=0.5):#测试过程
    if make_copy:
        new_image = np.copy(image)
        overlay = np.copy(image)
    self.cv_show('new_image',new_image)
    cnt_empty = 0
    all_spots = 0
    for spot in spot_dict.keys():
        all_spots += 1
        (x1, y1, x2, y2) = spot
        (x1, y1, x2, y2) = (int(x1), int(y1), int(x2), int(y2))
        spot_img = image[y1:y2, x1:x2]
        spot_img = cv2.resize(spot_img, (48, 48)) #测试网络时要与训练时保持一致
        
        label = self.make_prediction(spot_img,model,class_dictionary)
        if label == 'empty':
            cv2.rectangle(overlay, (int(x1),int(y1)), (int(x2),int(y2)), color, -1)
            cnt_empty += 1
            
    cv2.addWeighted(overlay, alpha, new_image, 1 - alpha, 0, new_image)
            
    cv2.putText(new_image, "Available: %d spots" %cnt_empty, (30, 95),
    cv2.FONT_HERSHEY_SIMPLEX,
    0.7, (255, 255, 255), 2)
    
    cv2.putText(new_image, "Total: %d spots" %all_spots, (30, 125),
    cv2.FONT_HERSHEY_SIMPLEX,
    0.7, (255, 255, 255), 2)
    save = False
    
    if save:
        filename = 'with_marking.jpg'
        cv2.imwrite(filename, new_image)
    self.cv_show('new_image',new_image)
    
    return new_image

def predict_on_video(self,video_name,final_spot_dict, model,class_dictionary,ret=True):  #视频按帧处理,结果是一个视频流,在不断地变化 
    cap = cv2.VideoCapture(video_name)
    count = 0
    while ret:
        ret, image = cap.read()
        count += 1
        if count == 5:
            count = 0
            
            new_image = np.copy(image)
            overlay = np.copy(image)
            cnt_empty = 0
            all_spots = 0
            color = [0, 255, 0] 
            alpha=0.5
            for spot in final_spot_dict.keys():
                all_spots += 1
                (x1, y1, x2, y2) = spot
                (x1, y1, x2, y2) = (int(x1), int(y1), int(x2), int(y2))
                spot_img = image[y1:y2, x1:x2]
                spot_img = cv2.resize(spot_img, (48,48)) 

                label = self.make_prediction(spot_img,model,class_dictionary)
                if label == 'empty':
                    cv2.rectangle(overlay, (int(x1),int(y1)), (int(x2),int(y2)), color, -1)
                    cnt_empty += 1

            cv2.addWeighted(overlay, alpha, new_image, 1 - alpha, 0, new_image)

            cv2.putText(new_image, "Available: %d spots" %cnt_empty, (30, 95),
            cv2.FONT_HERSHEY_SIMPLEX,
            0.7, (255, 255, 255), 2)

            cv2.putText(new_image, "Total: %d spots" %all_spots, (30, 125),
            cv2.FONT_HERSHEY_SIMPLEX,
            0.7, (255, 255, 255), 2)
            cv2.imshow('frame', new_image)
            if cv2.waitKey(10) & 0xFF == ord('q'):
                break

    cv2.destroyAllWindows()
    cap.release()

Park_test.py
from future import division
import matplotlib.pyplot as plt
import cv2
import os, glob
import numpy as np
from PIL import Image
from keras.applications.imagenet_utils import preprocess_input
from keras.models import load_model
from keras.preprocessing import image
from Parking import Parking
import pickle
cwd = os.getcwd()

def img_process(test_images,park):
white_yellow_images = list(map(park.select_rgb_white_yellow, test_images))
park.show_images(white_yellow_images)

gray_images = list(map(park.convert_gray_scale, white_yellow_images))
park.show_images(gray_images)#灰度处理

edge_images = list(map(lambda image: park.detect_edges(image), gray_images))
park.show_images(edge_images)#边缘检测

roi_images = list(map(park.select_region, edge_images))
park.show_images(roi_images)

list_of_lines = list(map(park.hough_lines, roi_images))#通过霍夫变换找停车位边上的直线,这一步得到所有的线
line_images = []
for image, lines in zip(test_images, list_of_lines):#画线
    line_images.append(park (image, lines)) 
park.show_images(line_images)

如果中有车的干扰;
rect_images = []#接下来找停车位的位置,画出矩形区域
rect_coords = []
for image, lines in zip(test_images, list_of_lines):
    new_image, rects = park.identify_blocks(image, lines)#画矩形
    rect_images.append(new_image)
    rect_coords.append(rects)
    
park.show_images(rect_images)

delineated = []
spot_pos = []
for image, rects in zip(test_images, rect_coords):#通过坐标不断地切分每一个停车位
    new_image, spot_dict = park.draw_parking(image, rects)
    delineated.append(new_image)
    spot_pos.append(spot_dict)
    
park.show_images(delineated) 
final_spot_dict = spot_pos[1]
print(len(final_spot_dict))


with open('spot_dict.pickle', 'wb') as handle:#把计算出来的坐标位置保存下来
    pickle.dump(final_spot_dict, handle, protocol=pickle.HIGHEST_PROTOCOL)

park.save_images_for_cnn(test_images[0],final_spot_dict)

return final_spot_dict

def keras_model(weights_path): #可以把car1.h5直接读取进来
model = load_model(weights_path)
return model
def img_test(test_images,final_spot_dict,model,class_dictionary):
#对一个图像进行检测 ,需要传入测试图像、指定好的字典(每个区域对应的编号)、训练好的模型、模型中预测为0时为空,1时被占据
for i in range (len(test_images)):#对每一张图像都执行预测操作
predicted_images = park.predict_on_image(test_images[i],final_spot_dict,model,class_dictionary)
def video_test(video_name,final_spot_dict,model,class_dictionary):
name = video_name
cap = cv2.VideoCapture(name)
park.predict_on_video(name,final_spot_dict,model,class_dictionary,ret=True)

if name == ‘main’:
test_images = [plt.imread(path) for path in glob.glob(‘test_images/*.jpg’)]#有两张图像
weights_path = ‘car1.h5’#网络模型的训练结果,训练好的模型,也可以用train.py进行模型的训练,其中用的是keras做的,因为比较简单
video_name = ‘parking_video.mp4’#输入的视频
class_dictionary = {}
class_dictionary[0] = ‘empty’#空车位
class_dictionary[1] = ‘occupied’#被占据的车位
park = Parking()#调用Parking.py,实例化
park.show_images(test_images)
final_spot_dict = img_process(test_images,park)
model = keras_model(weights_path)
img_test(test_images,final_spot_dict,model,class_dictionary)
video_test(video_name,final_spot_dict,model,class_dictionary)

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值