首先实现一下单通道摄像头后台
下一篇再实现多通道
主要功能:
透视变换框选感兴趣的区域,然后检测检测画面中移动的物体,并将坐标通过udp发送出去
讲一下大致思路
1.用到的库文件:
opencv-python
numpy
2.首先获取摄像头画面
import cv2
import numpy as np
cap=cv2.VideoCapture(0)
cv2.namedWindow('frame',0)
cv2.resizeWindow('frame',640,480)
while True:
ret,frame=cap.read()
if not ret:
break
cv2.imshow('frame',frame)
c=cv2.waitKey(30)
if c==27:
break
cap.release()
cv2.destroyAllWindows()
3.透视变化划出我们感兴趣的区域
#透视变换
#src 为摄像头画面
#x0,y0,...,x3,y3为我们感兴趣区域的四个顶点 分别为左上,右上,左下,右下
def framePerspective(src,x0,y0,x1,y1,x2,y2,x3,y3):
pts1=np.float32([[x0,y0],[x1,y1],[x2,y2],[x3,y3]])
pts2=np.float32([[0,0],[640,0],[0,480],[640,480]])
M=cv2.getPerspectiveTransform(pts1,pts2)
dst=cv2.warpPerspective(src,M,(640,480))
dst=cv2.flip(dst,1)
return dst
4.剔除背景检测移动物体
kernel=cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(5,5))
fgbg=cv2.createBackgroundSubtractorMOG2()
#剔除背景检测移动物体
def bgRemoveObjDetect(src,kernel,fgbg):
fgmask=fgbg.apply(src)
mask=cv2.morphologyEx(fgmask,cv2.MORPH_OPEN,kernel)
contours,_=cv2.findContours(mask.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
for c in contours:
if 1000<cv2.contourArea(c)<400000:
x,y,w,h=cv2.boundingRect(c)
cv2.rectangle(src,(x,y),(x+w,y+h),(0,0,255),10)
return src
5.鼠标点击事件,主要用来点击获取感兴趣的区域的四个顶点坐标
clickNum=0
x0,y0,x1,y1,x2,y2,x3,y3=0,0,0,0,0,0,0,0
cv2.setMouseCallback('frame', leftMouseClickEvent)
#鼠标点击事件回调
def leftMouseClickEvent(event,x,y,flags,param):
global clickNum,x0,y0,x1,y1,x2,y2,x3,y3
if event==cv2.EVENT_LBUTTONDOWN:
if clickNum==0:
x0,y0,x1,y1,x2,y2,x3,y3=0,0,0,0,0,0,0,0
elif clickNum==1:
x0, y0 = x, y
elif clickNum==2:
x1, y1 = x, y
elif clickNum==3:
x2,y2=x,y
elif clickNum == 4:
x3, y3 = x, y
clickNum=0
return
clickNum += 1
6.完整代码,在工程同级目录下,新建一个Config.xml, 用来存储四个点位信息, 然后把完整代码复制过去,插上一个usb摄像头,然后点击四个位置画出感兴趣区域后,即可实现动态物体检测,鼠标再点击一次则为重置画面
<Config>
<Channel0>
<pos0>555,240</pos0>
<pos1>1479,249</pos1>
<pos2>246,792</pos2>
<pos3>1308,783</pos3>
</Channel0>
</Config>
import cv2
import numpy as np
import socket
import xml.etree.cElementTree as ET
import os
import sys
import tkinter
def traverseXml(element):
if len(element)>0:
for child in element:
print(child.tag,'----',child.attrib)
traverseXml(child)
#鼠标点击事件回调
def leftMouseClickEvent(event,x,y,flags,param):
global clickNum,x0,y0,x1,y1,x2,y2,x3,y3
if event==cv2.EVENT_LBUTTONDOWN:
if clickNum==0:
x0,y0,x1,y1,x2,y2,x3,y3=0,0,0,0,0,0,0,0
elif clickNum==1:
x0, y0 = x, y
elif clickNum==2:
x1, y1 = x, y
elif clickNum==3:
x2,y2=x,y
elif clickNum == 4:
x3, y3 = x, y
clickNum=0
updateXml()
return
clickNum += 1
#获取摄像头画面
def getCamFrame(cap0):
_,frame0=cap0.read()
return frame0
#透视变换
def framePerspective(frame,x0,y0,x1,y1,x2,y2,x3,y3):
pts1=np.float32([[x0,y0],[x1,y1],[x2,y2],[x3,y3]])
pts2=np.float32([[0,0],[640,0],[0,480],[640,480]])
M=cv2.getPerspectiveTransform(pts1,pts2)
dst=cv2.warpPerspective(frame,M,(640,480))
dst=cv2.flip(dst,1)
cv2.imshow('frame',dst)
return dst
#背景差检测移动物体并通过udp发送
def frameDifferencing():
global kernel,fgbg,dst,server,ip_port
fgmask=fgbg.apply(dst)
mask=cv2.morphologyEx(fgmask,cv2.MORPH_OPEN,kernel)
# cv2.imshow('mask',mask)
contours,_=cv2.findContours(mask.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
posMsg='v'
for c in contours:
if 1000<cv2.contourArea(c)<400000:
x,y,w,h=cv2.boundingRect(c)
cv2.rectangle(dst,(x,y),(x+w,y+h),(0,0,255),10)
posMsg+=str((x+(w/2))/640)+','+str((y+(h/2))/480)+'@'
cv2.imshow('frame',dst)
if '@' in posMsg:
server.sendto(posMsg.encode('utf-8'),('127.0.0.1', 11111))
#读取xml文件
def readXml():
global x0, y0, x1, y1, x2, y2, x3, y3
xmlFilePath = os.path.abspath('Config.xml')
try:
tree = ET.parse(xmlFilePath)
print("tree type:", type(tree))
# 获得根节点
root = tree.getroot()
except Exception as e:
print("parse Config.xml fail!")
for child in root:
if child.tag=='Channel0':
for child1 in child:
# print(child1.text)
if child1.tag=='pos0':
posArr=child1.text.split(',')
x0,y0=posArr[0],posArr[1]
print("x0:"+x0+" y0:"+y0)
elif child1.tag=='pos1':
posArr=child1.text.split(',')
x1,y1=posArr[0],posArr[1]
print("x1:"+x1+" y1:"+y1)
elif child1.tag == 'pos2':
posArr = child1.text.split(',')
x2, y2 = posArr[0], posArr[1]
print("x2:" + x2 + " y2:" + y2)
elif child1.tag == 'pos3':
posArr = child1.text.split(',')
x3, y3 = posArr[0], posArr[1]
print("x1:" + x3 + " y1:" + y3)
#写入xml文件
def updateXml():
global x0, y0, x1, y1, x2, y2, x3, y3,clickNum
xmlFilePath = os.path.abspath('Config.xml')
try:
tree = ET.parse(xmlFilePath)
print("tree type:", type(tree))
# 获得根节点
root = tree.getroot()
except Exception as e:
print("parse Config.xml fail!")
for child in root:
if child.tag == 'Channel0':
for child1 in child:
# print(child1.text)
if child1.tag == 'pos0':
txt=str(x0)+','+str(y0)
child1.text=txt
elif child1.tag == 'pos1':
txt = str(x1) + ',' + str(y1)
child1.text = txt
elif child1.tag == 'pos2':
txt = str(x2) + ',' + str(y2)
child1.text = txt
elif child1.tag == 'pos3':
txt = str(x3) + ',' + str(y3)
child1.text = txt
tree.write(xmlFilePath)
if __name__=='__main__':
clickNum=0
x0,y0,x1,y1,x2,y2,x3,y3=0,0,0,0,0,0,0,0
readXml()
ip_port = ('127.0.0.1', 12345)
server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# server.bind(ip_port)
cap0 = cv2.VideoCapture(0)
cv2.namedWindow('frame', 0)
cv2.resizeWindow('frame', 640, 480)
frame = getCamFrame(cap0)
dst=frame
cv2.imshow('frame', frame)
cv2.setMouseCallback('frame', leftMouseClickEvent)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
fgbg = cv2.createBackgroundSubtractorMOG2()
while True:
frame=getCamFrame(cap0)
cv2.imshow('frame',frame)
if y3!=0 :
dst= framePerspective(frame, x0,y0,x1,y1,x2,y2,x3,y3)
frameDifferencing()
c=cv2.waitKey(1)
if c==27:
break;
cap0.release()
cv2.destroyAllWindows()