OpenCV是一個(gè)基于(開(kāi)源)發(fā)行的跨平臺(tái)計(jì)算機(jī)視覺(jué)庫(kù),能實(shí)現(xiàn)圖像處理和計(jì)算機(jī)視覺(jué)方面的很多通用算法。車位識(shí)別的圖像處理過(guò)程如圖所示。
在python中設(shè)置完所有內(nèi)容后, 最重要的依賴關(guān)系將是OpenCV庫(kù)。通過(guò)pip將其添加到虛擬環(huán)境中,可以運(yùn)行
pip install opencv-python
。
要檢查所有設(shè)置是否正確,我們可以使用以下cv2.__version__
命令打印環(huán)境中可用的當(dāng)前OpenCV版本。
首先處理旋轉(zhuǎn)矩形
從圖中可以看到,由于視頻拍攝角度的問(wèn)題,車位不是橫平豎直的,并且車位在屏幕上的大小和角度也是不相同的。需要用到旋轉(zhuǎn)矩形的操作,并調(diào)整單個(gè)矩形框使其能夠用于所有車位。
假設(shè)對(duì)圖片上任意點(diǎn)(
x
,
y
x,y
x,y),繞一個(gè)坐標(biāo)點(diǎn)(
r
x
0
,
r
y
0
r_{x0},r_{y0}
rx0?,ry0?)順時(shí)針旋轉(zhuǎn)a角度后的新的坐標(biāo)設(shè)為
(
x
0
,
y
0
)
(x_0, y_0)
(x0?,y0?),則有公式:
x
0
=
(
x
?
r
x
0
)
c
o
s
a
?
(
y
?
r
y
0
)
s
i
n
a
+
r
x
0
x_0=(x-r_{x_0})cosa-(y-r_{y_0})sina+r_{x_0}
x0?=(x?rx0??)cosa?(y?ry0??)sina+rx0??
y
0
=
(
x
?
r
x
0
)
s
i
n
a
+
(
y
?
r
y
0
)
c
o
s
a
+
r
y
0
,
y_0=(x-r_{x_0})sina+(y-r_{y_0})cosa+r_{y_0},
y0?=(x?rx0??)sina+(y?ry0??)cosa+ry0??,
根據(jù)此公式,創(chuàng)建函數(shù)完成矩陣旋轉(zhuǎn)操作。
下面是實(shí)現(xiàn)的代碼:
# 矩形框順時(shí)針旋轉(zhuǎn)
import cv2
import math
# 傳入旋轉(zhuǎn)的參考點(diǎn)坐標(biāo),矩形框左上角坐標(biāo)(x,y),框的寬w和高h(yuǎn),旋轉(zhuǎn)角度a
def angleRota(center_x, center_y, x, y, w, h, a):
# 角度轉(zhuǎn)弧度
a = (math.pi / 180) * a
# 旋轉(zhuǎn)前左上角坐標(biāo)
x1, y1 = x, y
# 右上角坐標(biāo)
x2, y2 = x + w, y
# 右下角坐標(biāo)
x3, y3 = x + w, y + h
# 左下角坐標(biāo)
x4, y4 = x, y + h
# 旋轉(zhuǎn)后的左上角坐標(biāo),像素坐標(biāo)是整數(shù)
px1 = int((x1 - center_x) * math.cos(a) - (y1 - center_y) * math.sin(a) + center_x)
py1 = int((x1 - center_x) * math.sin(a) + (y1 - center_y) * math.cos(a) + center_y)
# 右上角坐標(biāo)
px2 = int((x2 - center_x) * math.cos(a) - (y2 - center_y) * math.sin(a) + center_x)
py2 = int((x2 - center_x) * math.sin(a) + (y2 - center_y) * math.cos(a) + center_y)
# 右下角坐標(biāo)
px3 = int((x3 - center_x) * math.cos(a) - (y3 - center_y) * math.sin(a) + center_x)
py3 = int((x3 - center_x) * math.sin(a) + (y3 - center_y) * math.cos(a) + center_y)
# 左下角坐標(biāo)
px4 = int((x4 - center_x) * math.cos(a) - (y4 - center_y) * math.sin(a) + center_x)
py4 = int((x4 - center_x) * math.sin(a) + (y4 - center_y) * math.cos(a) + center_y)
# 保存每一個(gè)角的坐標(biāo)
pt1 = (px1, py1)
pt2 = (px2, py2)
pt3 = (px3, py3)
pt4 = (px4, py4)
# 存儲(chǔ)每個(gè)角的坐標(biāo)
angle = [pt1, pt2, pt3, pt4]
# 返回調(diào)整后的坐標(biāo)
return angle
# 繪制旋轉(zhuǎn)后的矩形框
def drawLine(img, angle, color, thickness):
# 分別繪制四條邊
cv2.line(img, angle[0], angle[1], color, thickness)
cv2.line(img, angle[1], angle[2], color, thickness)
cv2.line(img, angle[2], angle[3], color, thickness)
cv2.line(img, angle[3], angle[0], color, thickness)
# 返回繪制好旋轉(zhuǎn)矩形的圖像
return img
# 矩形旋轉(zhuǎn)
def recRota(img, center_x, center_y, x1, y1, w, h, rota, draw=True):
'''
img: 原圖像
(center_X, center_y): 旋轉(zhuǎn)參考點(diǎn)的坐標(biāo)
(x1, y1): 矩形框左上角坐標(biāo)
w: 矩形框的寬
h: 矩形框的高
rota: 順時(shí)針的旋轉(zhuǎn)角度,如:30°
'''
color = (255, 255, 0) # 繪制停車線的線條顏色
thickness = 2 # 停車線線條寬度
# (1)計(jì)算旋轉(zhuǎn)一定角度后的四個(gè)角的坐標(biāo)
angle = angleRota(x1, y1, x1, y1, w, h, rota)
# (2)繪制旋轉(zhuǎn)后的矩形
if draw == True:
img = drawLine(img, angle, color, thickness)
# 返回繪制后的圖像,以及矩形框的四個(gè)角的坐標(biāo)
return img, angle
else:
return angle
處理單幀圖像,劃分車位
繪制后的單幀圖像如圖所示
import cv2
video = cv2.VideoCapture('./input.mp4')
ret, cap = video.read()
cv2.imwrite('./first.jpg', cap)
處理視頻,分割出所有車位
在對(duì)單幀圖像處理時(shí),已將所有車位分割出來(lái),并記錄下左上角坐標(biāo)。由于每個(gè)車位框是傾斜的,如果要分割出每個(gè)車位,必須使用切片方法,但切片出來(lái)的圖像是橫平豎直的。因此針對(duì)每一個(gè)車位框,都以該框的左上角為旋轉(zhuǎn)參考,旋轉(zhuǎn)整張幀圖像,將車位框擺正之后再進(jìn)行切片。
下面是實(shí)現(xiàn)的代碼:
# w, h = 90, 160 # 矩形框的寬和高
# w, h = 70, 130
w, h = 90, 160
# 遍歷所有的矩形框坐標(biāo)
for pos in posList:
# 得到旋轉(zhuǎn)后的矩形的四個(gè)角坐標(biāo),傳入原圖,旋轉(zhuǎn)參考點(diǎn)坐標(biāo),矩形框左上角坐標(biāo),框的寬w和高h(yuǎn),逆時(shí)針轉(zhuǎn)4°
angle = recRota(imgDilate, pos[0], pos[1], pos[0], pos[1], w, h, -5, draw=False) # 裁剪的車位不繪制車位圖
# (5)裁剪所有的車位框,由于我們的矩形是傾斜的,先要把矩形轉(zhuǎn)正之后再裁剪
# 變換矩陣,以每個(gè)矩形框的左上坐標(biāo)為參考點(diǎn),順時(shí)針尋轉(zhuǎn)4°,旋轉(zhuǎn)后的圖像大小不變
rota_params = cv2.getRotationMatrix2D(angle[0], angle=-4, scale=1)
# 旋轉(zhuǎn)整張幀圖片,輸入img圖像,變換矩陣,指定輸出圖像大小
rota_img = cv2.warpAffine(imgDilate, rota_params, (img_w, img_h))
# 裁剪擺正了的矩形框,先指定高h(yuǎn),再指定寬w
imgCrop = rota_img[pos[1]:pos[1] + h, pos[0]:pos[0] + w]
# 顯示裁剪出的圖像
cv2.imshow('imgCrop', imgCrop)
處理像素點(diǎn)
處理像素點(diǎn)是車位識(shí)別的難點(diǎn),對(duì)于采集到的24位真彩色位圖圖像文件所需存儲(chǔ)量大,圖像需要占用大量系統(tǒng)資源,不利于圖像的快速處理。
灰度圖只包含亮度信息,不含彩色信息,有利于圖像的處理,故在對(duì)圖像進(jìn)行處理前應(yīng)先將其轉(zhuǎn)化為灰度圖。
由于在拍攝過(guò)程中,受各種主客觀因素的影響,我們獲得的車輛圖像和實(shí)際的需要存在某種程度的差異,如果差異過(guò)大,會(huì)造成圖像分割和識(shí)別的困難,嚴(yán)重時(shí)甚至?xí)狗指詈妥R(shí)別根本無(wú)法進(jìn)行。為此,本文采取一些圖像增強(qiáng)方法來(lái)綜合處理。
(1)二值化方法
圖像的二值化是最簡(jiǎn)單的圖像處理技術(shù),一般都跟具體算法聯(lián)系,本技術(shù)中對(duì)濾波后的灰度圖轉(zhuǎn)換為二值圖,采用自適應(yīng)閾值方法。
(2)中值濾波
中值濾波是一種局部平均平滑技術(shù),在一定條件下,中值濾波可以克服線性濾波器所帶來(lái)的圖像細(xì)節(jié)模糊,而且對(duì)濾除脈沖干擾及顆粒噪聲最為有效。
(3)膨脹
膨脹操作就是將圖像(或圖像的一部分區(qū)域,稱之為A)與核(稱之為B)進(jìn)行卷積。核可以是任何形狀和大小,它擁有一個(gè)單獨(dú)定義出來(lái)的參考點(diǎn),我們稱其為錨點(diǎn)。
代碼實(shí)現(xiàn)如下:
while True:
# 記錄有幾個(gè)空車位
spacePark = 0
# 返回圖像是否讀取成功,以及讀取的幀圖像img
success, img = cap.read()
if img is None:
break
# 為了使裁剪后的單個(gè)車位里面沒(méi)有繪制的邊框,需要在畫車位框之前,把原圖像復(fù)制一份
imgCopy = img.copy()
# 獲得整每幀圖片的寬和高
img_w, img_h = img.shape[:2] # shape是(w,h,c)
# ==1== 轉(zhuǎn)換灰度圖,通過(guò)形態(tài)學(xué)處理來(lái)檢測(cè)車位內(nèi)有沒(méi)有車
imgGray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# ==2== 高斯濾波,卷積核3*3,沿x和y方向的卷積核的標(biāo)準(zhǔn)差為1
imgGray = cv2.GaussianBlur(imgGray, (3, 3), 1)
# ==3== 二值圖,自適應(yīng)閾值方法
imgThresh = cv2.adaptiveThreshold(imgGray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY_INV, 101, 20)
# ==4== 刪除零散的白點(diǎn),
# 如果車位上有車,那么車位上的像素?cái)?shù)量(白點(diǎn))很多,如果沒(méi)有車,車位框內(nèi)基本沒(méi)什么白點(diǎn)
imgMedian = cv2.medianBlur(imgThresh, 5)
# ==5== 擴(kuò)張白色部分,膨脹
kernel = np.ones((3, 3), np.uint8) # 設(shè)置卷積核
imgDilate = cv2.dilate(imgMedian, kernel, iterations=1) # 迭代次數(shù)為1
# 由于這個(gè)視頻比較短,就循環(huán)播放這個(gè)視頻
if cap.get(cv2.CAP_PROP_POS_FRAMES) == cap.get(cv2.CAP_PROP_FRAME_COUNT):
# 如果當(dāng)前幀==總幀數(shù),那就重置當(dāng)前幀為0
cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
三種方法處理后的圖像分別如圖所示。
車位檢測(cè)
我們的檢測(cè)思路是:通過(guò)計(jì)算每個(gè)車位框內(nèi)的像素個(gè)數(shù)來(lái)判斷該車位內(nèi)是否有車。經(jīng)過(guò)上述處理后,我們計(jì)算每個(gè)分割出來(lái)的車位框中的白點(diǎn)個(gè)數(shù),返回灰度值不為0的像素?cái)?shù)量。經(jīng)過(guò)分析,如果白點(diǎn)數(shù)量大于1800,那么就表明車位上有車。
代碼實(shí)現(xiàn)如下:
count = cv2.countNonZero(imgCrop)
# 將計(jì)數(shù)顯示在矩形框上
cv2.putText(imgCopy, str(count), (pos[0] + 5, pos[1] + 20), cv2.FONT_HERSHEY_COMPLEX, 0.8, (0, 255, 255), 2)
# (7)確定車位上是否有車
if count < 3000: # 像素?cái)?shù)量小于3000輛就是沒(méi)有車
color = (0, 255, 0) # 沒(méi)有車的話車位線就是綠色
spacePark += 1 # 每檢測(cè)到一個(gè)空車位,數(shù)量就加一
else:
color = (0, 0, 255) # 有車時(shí)車位線就是紅色
最后我們的檢測(cè)結(jié)果如圖所示。
文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-753137.html
現(xiàn)在,我們就構(gòu)建好了一個(gè)停車場(chǎng)車位識(shí)別的系統(tǒng)!文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-753137.html
到了這里,關(guān)于基于OpenCV構(gòu)建停車場(chǎng)車位識(shí)別項(xiàng)目的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!