import numpy as np
import cv2
#yolov3目标检测
def model_output(image, w, h):
net = cv2.dnn.readNetFromDarknet('./yolo-coco/yolov3-tiny.cfg', './yolo-coco/yolov3-tiny.weights')
#加载已训练好的yolov3网络的权重和相应的配置数据
blob_image = cv2.dnn.blobFromImage(image, 1/255.)
#图像预处理,归一化作用,确保输入数据在相同的尺度范围内,避免不同图像之间的像素值差异过大。
# 这有助于网络模型更好地学习图像特征,提高模型的稳定性和收敛速度,提高识别精度。
# print(blob_image)
net.setInput(blob_image) #将blob_image输入到网络
out_names = net.getUnconnectedOutLayersNames()
# 获取网络输出层信息(所有输出层的名字)Net类中的forward函数需要一个结束层,
# 它应该在网络中运行到该层。因此我们通过getUnconnectedOutLayersNames来识别网络的最后一层从而运行整个网络
# print(out_names)
outs = net.forward(out_names)#模型开始正向推理,得到输出结果
ids = [] #表示识别的目标归属于哪一类 类别的编号0~79
confidences = [] # 表示识别目标是某种物体的置信度
boxes = [] # # 用于存放识别物体的矩形框的信息,包括框的左上角横坐标x和纵坐标y以及框的高h和宽w
# print(len(outs))
'''
是YOLO算法在图片中检测到的bbx的信息
# 由于YOLO v3-tiny有两个输出
# 因此outs是一个长度为2的列表
# 其中,列表中每一个元素的维度是(num_detection, 85)
# num_detections表示该层输出检测到bbx的个数
# 85:因为该模型在COCO数据集上训练,[5:]表示类别概率;[0:4]表示bbx的位置信息;[5]表示置信度
每个格子预测的 box 的置信度 = 预测 box 中包含物体的概率 * 预测 box 位置的准确度
每个格子预测的类别置信度 = 预测的类别的概率
'''
#遍历检测结果, 获得检测框
for out in outs:
# 遍历每一个输出层的输出
# print("len(out):",len(out))
# print(out)
#遍历某个输出层中的每一个目标
for detection in out:
# print(detection)
# print("len(detection):",len(detection))
scores = detection[5:] ## 当前目标属于某一类别的概率
# print("scores:",scores)
class_id = np.argmax(scores) # 概率最大的ID
# print("class_id:",class_id)
confidence = scores[class_id]#得到最大概率值
# print("confidence:",confidence)
#只保留置信度大于0.5的检测框
if confidence > .5:
ids.append(class_id)
confidences.append(confidence)
# 将边界框的坐标还原至与原图片匹配,YOLO返回的是检测矩形框的中心坐标以及矩形框的宽度和高度
center_x, center_y = int(detection[0] * w), int(detection[1] * h)
width, height = int(detection[2] * w), int(detection[3] * h)
x = int(center_x - (width / 2)) # 计算矩形框的左上角的横坐标
y = int(center_y - (height / 2)) # 计算矩形框的左上角的纵坐标
boxes.append([x, y, width, height])
# print(ids)
#通过非极大值抑制去掉多余的检测框
indices = cv2.dnn.NMSBoxes(boxes, confidences, 0.5, 0.5)
# 返回的indices是一个一维数组,数组中的元素是保留下来的检测框boxes的索引位置
# print(indices)
return indices, boxes, confidences, ids
def create_yanmo(frame):
im=np.zeros(frame.shape,dtype=np.uint8)
cv2.fillPoly(im,[np.array(points).reshape(-1,1,2)],(255,255,255))
new_im=cv2.addWeighted(frame,.8,im,.2,10)
return new_im
def alerm_per(new_im,points,person_axis):
im1=np.zeros(new_im.shape,dtype=np.uint8)
cv2.fillPoly(im1,[np.array(points).reshape(-1,1,2)],(255,255,255))
im2 = np.zeros(new_im.shape, dtype=np.uint8)
cv2.fillPoly(im2, [np.array(person_axis).reshape(-1, 1, 2)], (255, 255, 255))
intersact=cv2.bitwise_and(im1,im2)
return np.sum(np.greater(intersact,0))
def inspect_per(new_im):
suoying,boxes,confident,ids=model_output(new_im, new_im.shape[1], new_im.shape[0])
for i in suoying: # 宽 高
if ids[i]==0:
box=boxes[i]
#求出行人的点坐标传给求相交区域的函数
person_axis=[(box[0],box[1]),(box[0]+box[2],box[1]),(box[0]+box[2],box[1]+box[3]),(box[0],box[1]+box[3])]
alerm_draw=alerm_per(new_im,points,person_axis) #检测危险区域并给危险区域行人画红框
if alerm_draw>0:
cv2.rectangle(new_im, (box[0], box[1]), (box[0] + box[2], box[1] + box[3]), (0, 0, 255), 4)
else:
cv2.rectangle(new_im,(box[0],box[1]),(box[0]+box[2],box[1]+box[3]),(255,0,0),3)
#循环读取每一帧
def cicrle_frame(capture):
pause=False
while True:
if not pause:
ret,frame=capture.read()
if not ret:
break
new_im=create_yanmo(frame) #1.掩膜
inspect_per(new_im) #2.检测行人画框函数
cv2.imshow('shuai1',new_im)
cv2.setMouseCallback('shuai1', on_click, frame)
key=cv2.waitKey(10)
if key==ord('q'):
break
if key==ord(' '):
pause=not pause
#描点划线函数
points=[]
def on_click(event,x,y,flag,frame):
if event==cv2.EVENT_LBUTTONDOWN:
points.append((x,y))
for i in range(len(points)):
cv2.circle(frame,points[i],5,(255,255,255),3)
for i in range(len(points)-1):
cv2.line(frame,points[i],points[i+1],(255,255,255),3)
cv2.imshow('shuai',frame)
#读取第一帧函数 描点划线
def read_frame(capture):
ret,frame=capture.read()
cv2.imshow('shuai',frame)
cv2.setMouseCallback('shuai',on_click,frame)
cv2.waitKey(0)
def main():
capture=cv2.VideoCapture('a.avi')
#读取第一帧 描点划线
read_frame(capture)
cv2.destroyAllWindows() #完了以后毁坏窗口,已便使用新的窗口 不会造成窗口冗余
#循环读取每一帧 将掩膜放进循环
cicrle_frame(capture)
if __name__=='__main__':
main()