Amazing-Python-Scripts
710 строк · 25.0 Кб
1#!/usr/bin/env python3
2"""
3* Copyright (c) 2018 Intel Corporation.
4*
5* Permission is hereby granted, free of charge, to any person obtaining
6* a copy of this software and associated documentation files (the
7* "Software"), to deal in the Software without restriction, including
8* without limitation the rights to use, copy, modify, merge, publish,
9* distribute, sublicense, and/or sell copies of the Software, and to
10* permit persons to whom the Software is furnished to do so, subject to
11* the following conditions:
12*
13* The above copyright notice and this permission notice shall be
14* included in all copies or substantial portions of the Software.
15*
16* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23"""
24
25
26from __future__ import print_function27import sys28import os29from argparse import ArgumentParser30import cv231import numpy32import time33import collections34import queue35import signal36import json37import pathlib38from inference import Network39
40# CONSTANTS
41CONFIG_FILE = '../resources/config.json'42EVENT_FILE = "../UI/resources/video_data/events.json"43DATA_FILE = "../UI/resources/video_data/data.json"44TARGET_DEVICE = "CPU"45OUTPUT_VIDEO_PATH = "../UI/resources/videos"46CPU_EXTENSION = ""47LOOP_VIDEO = False48UI = False49CONF_THRESHOLD_VALUE = 0.5550LOG_FILE_PATH = "./intruders.log"51LOG_WIN_HEIGHT = 43252LOG_WIN_WIDTH = 41053CONF_CANDIDATE_CONFIDENCE = 454CODEC = 0x3163766155
56# Opencv windows per each row
57CONF_WINDOW_COLUMNS = 258
59# Global variables
60model_xml = ''61model_bin = ''62conf_labels_file_path = ''63video_caps = []64accepted_devices = ["CPU", "GPU", "HETERO:FPGA,CPU", "MYRIAD", "HDDL"]65is_async_mode = True66
67# Event class to store the intruder details
68
69
70class Event:71def __init__(self, event_time=None, intruder=None, count=None, frame=None):72self.time = event_time73self.intruder = intruder74self.count = count75self.frame = frame76
77
78# VideoCap class to manage the input source
79class VideoCap:80def __init__(self, vc, cam_name, cams, is_cam):81self.input_width = vc.get(3)82self.input_height = vc.get(4)83self.vc = vc84self.cam_name = cam_name85self.is_cam = is_cam86self.no_of_labels = 087self.last_correct_count = []88self.total_count = []89self.current_count = []90self.changed_count = []91self.candidate_count = []92self.candidate_confidence = []93self.frame = None94self.loop_frames = 095self.frame_count = 096self.events = []97self.video_name = 'video{}.mp4'.format(cams)98self.vw = None99
100def init(self, size):101self.no_of_labels = size102for i in range(size):103self.last_correct_count.append(0)104self.total_count.append(0)105self.changed_count.append(False)106self.current_count.append(0)107self.candidate_count.append(0)108self.candidate_confidence.append(0)109
110def init_vw(self, h, w):111self.vw = cv2.VideoWriter(os.path.join(OUTPUT_VIDEO_PATH, self.video_name), CODEC,112self.vc.get(cv2.CAP_PROP_FPS), (w, h), True)113if not self.vw.isOpened():114return -1, self.video_name115return 0, ''116
117
118def parse_args():119"""120Parse the command line argument
121
122:return status: 0 on success, negative value on failure
123"""
124global LOOP_VIDEO125global conf_labels_file_path126global model_xml127global model_bin128global CPU_EXTENSION129global UI130global TARGET_DEVICE131global is_async_mode132
133try:134model_xml = os.environ["MODEL"]135model_bin = os.path.splitext(model_xml)[0] + ".bin"136except:137return -2138
139try:140conf_labels_file_path = os.environ["LABEL_FILE"]141except:142return -3143
144CPU_EXTENSION = os.environ[145'CPU_EXTENSION'] if 'CPU_EXTENSION' in os.environ.keys() else None146
147try:148LOOP_VIDEO = os.environ["LOOP_VIDEO"]149if LOOP_VIDEO == "True" or LOOP_VIDEO == "true":150LOOP_VIDEO = True151elif LOOP_VIDEO == "False" or LOOP_VIDEO == "false":152LOOP_VIDEO = False153else:154print("Invalid input for LOOP_VIDEO. Defaulting to LOOP_VIDEO = False")155LOOP_VIDEO = False156except:157LOOP_VIDEO = False158
159if 'DEVICE' in os.environ.keys():160TARGET_DEVICE = os.environ['DEVICE']161
162try:163UI = os.environ["UI"]164if UI == "True" or UI == "true":165UI = True166elif UI == "False" or UI == "false":167UI = False168else:169print("Invalid input for UI. Defaulting to UI = False")170UI = False171except:172UI = False173
174if 'FLAG' in os.environ.keys():175async_mode = os.environ['FLAG']176if async_mode == "sync":177is_async_mode = False178else:179is_async_mode = True180
181
182def check_args():183"""184Validate the command line arguments
185:return status: 0 on success, negative value on failure
186"""
187global model_xml188global conf_labels_file_path189
190if model_xml == '':191return -2192
193if conf_labels_file_path == '':194return -3195
196if 'MULTI' not in TARGET_DEVICE and TARGET_DEVICE not in accepted_devices:197print("Unsupported device: " + TARGET_DEVICE)198return -17199elif 'MULTI' in TARGET_DEVICE:200target_devices = TARGET_DEVICE.split(':')[1].split(',')201for multi_device in target_devices:202if multi_device not in accepted_devices:203print("Unsupported device: " + TARGET_DEVICE)204return -17205return 0206
207
208def get_used_labels(req_labels):209"""210Read the model's label file and get the position of labels required by the application
211
212:param req_labels: intruders to be detected in the input source
213:return status: 0 on success, negative value on failure
214labels: On success, list of labels present in model's label file
215used_labels: On success, list of bool values where true indicates that a label in labels list at that position is
216used in the application
217"""
218global conf_labels_file_path219used_labels = []220
221if conf_labels_file_path:222labels = []223with open(conf_labels_file_path, 'r') as label_file:224if not label_file:225return [-4, [], []]226labels = [x.strip() for x in label_file]227
228if not labels:229return [-5, [], []]230
231for label in labels:232if label in req_labels:233used_labels.append(True)234else:235used_labels.append(False)236
237return [0, labels, used_labels]238
239return [-6, [], []]240
241
242def get_input():243"""244Parse the configuration file
245
246:return status: 0 on success, negative value on failure
247streams: On success, list of VideoCap containing configuration file data
248labels: On success, labels or intruder to be detected
249"""
250global CONFIG_FILE251global video_caps252labels = []253streams = []254
255assert os.path.isfile(256CONFIG_FILE), "{} file doesn't exist".format(CONFIG_FILE)257config = json.loads(open(CONFIG_FILE).read())258for id, item in enumerate(config['inputs']):259for idx, video in enumerate(item['video']):260cams = idx + 1261cam_name = "Cam {}".format(idx)262if video.isdigit():263video_cap = VideoCap(cv2.VideoCapture(264int(video)), cam_name, cams, is_cam=True)265else:266if os.path.isfile(video):267video_cap = VideoCap(cv2.VideoCapture(268video), cam_name, cams, is_cam=False)269else:270return [-8, [video]]271video_caps.append(video_cap)272labels = item['label']273
274for video_cap in video_caps:275if not video_cap.vc.isOpened():276return [-9, [video_cap.cam_name]]277
278video_cap.init(len(labels))279return [0, labels]280
281
282def save_json():283"""284Write the video results to json files
285
286:return status: 0 on success, negative value on failure
287"""
288global video_caps289global EVENT_FILE290global DATA_FILE291events = []292if video_caps:293events = video_caps[0].events294total = 0295event_json = open(EVENT_FILE, 'w')296if not event_json:297return -10298
299data_json = open(DATA_FILE, 'w')300if not data_json:301return -11302
303data_json.write("{\n\t\"video1\": {\n")304event_json.write("{\n\t\"video1\": {\n")305events_size = len(events) - 1306if events:307fps = video_caps[0].vc.get(cv2.CAP_PROP_FPS)308for i in range(events_size):309event_json.write("\t\t\"%d\":{\n" % (i))310event_json.write("\t\t\t\"time\":\"%s\",\n" % events[i].time)311event_json.write("\t\t\t\"content\":\"%s\",\n" %312events[i].intruder)313event_json.write("\t\t\t\"videoTime\":\"%d\"\n" %314float(events[i].frame / fps))315event_json.write("\t\t},\n")316data_json.write("\t\t\"%d\": \"%d\",\n" %317(float(events[i].frame / fps), events[i].count))318event_json.write("\t\t\"%d\":{\n" % events_size)319event_json.write("\t\t\t\"time\":\"%s\",\n" % events[events_size].time)320event_json.write("\t\t\t\"content\":\"%s\",\n" %321events[events_size].intruder)322event_json.write("\t\t\t\"videoTime\":\"%d\"\n" %323float(events[events_size].frame / fps))324event_json.write("\t\t}\n")325data_json.write("\t\t\"%d\": \"%d\"\n" % (326float(events[events_size].frame / fps), events[events_size].count))327total = events[events_size].count328event_json.write("\t}\n")329event_json.write("}")330data_json.write("\t},\n")331data_json.write("\t\"totals\":{\n")332data_json.write("\t\t\"video1\": \"%d\"\n" % total)333data_json.write("\t}\n")334data_json.write("}")335event_json.close()336data_json.close()337return 0338
339
340def arrange_windows():341"""342Arranges the windows so that they are not overlapping
343
344:return: None
345"""
346global CONF_WINDOW_COLUMNS347global video_caps348spacer = 470349row_spacer = 250350cols = 0351rows = 0352window_width = 768353window_height = 432354
355# Arrange log window356cv2.namedWindow("Intruder Log", cv2.WINDOW_AUTOSIZE)357cv2.moveWindow("Intruder Log", 0, 0)358
359# Arrange video windows360for idx in range(len(video_caps)):361if cols == CONF_WINDOW_COLUMNS:362rows += 1363cols = 1364cv2.namedWindow(video_caps[idx].cam_name, cv2.WINDOW_NORMAL)365cv2.resizeWindow(video_caps[idx].cam_name,366window_width, window_height)367cv2.moveWindow(video_caps[idx].cam_name,368spacer * cols, row_spacer * rows)369else:370cols += 1371cv2.namedWindow(video_caps[idx].cam_name, cv2.WINDOW_NORMAL)372cv2.resizeWindow(video_caps[idx].cam_name,373window_width, window_height)374cv2.moveWindow(video_caps[idx].cam_name,375spacer * cols, row_spacer * rows)376
377
378# Signal handler
379def signal_handler(sig, frame):380global video_caps381global EVENT_FILE382global DATA_FILE383if video_caps:384ret = save_json()385if ret != 0:386if ret == -10:387print("Could not create event JSON file " + EVENT_FILE + "!")388elif ret == -11:389print("Could not create data JSON file " + DATA_FILE + "!")390
391clean_up()392sys.exit(0)393
394
395def clean_up():396"""397Destroys all the opencv windows and releases the objects of videoCapture and videoWriter
398"""
399global video_caps400cv2.destroyAllWindows()401for video_cap in video_caps:402if video_cap.vw:403video_cap.vw.release()404if video_cap.vc:405video_cap.vc.release()406
407
408def intruder_detector():409"""410Process the input source frame by frame and detects intruder, if any.
411
412:return status: 0 on success, negative value on failure
413"""
414global CONF_CANDIDATE_CONFIDENCE415global LOG_WIN_HEIGHT416global LOG_WIN_WIDTH417global CONFIG_FILE418global video_caps419global conf_labels_file_path420global is_async_mode421global UI422global LOOP_VIDEO423
424parse_args()425ret = check_args()426if ret != 0:427return ret, ""428
429if not os.path.isfile(CONFIG_FILE):430return -12, ""431
432if not os.path.isfile(conf_labels_file_path):433return -13, ""434
435# Creates subdirectory to save output snapshots436pathlib.Path(os.getcwd() + '/output/').mkdir(parents=True, exist_ok=True)437
438# Read the configuration file439ret, req_labels = get_input()440if ret != 0:441return ret, req_labels[0]442
443if not video_caps:444return -14, ''445
446# Get the labels that are used in the application447ret, label_names, used_labels = get_used_labels(req_labels)448if ret != 0:449return ret, ''450if True not in used_labels:451return -15, ''452
453# Init a rolling log to store events454rolling_log_size = int((LOG_WIN_HEIGHT - 15) / 20)455log_list = collections.deque(maxlen=rolling_log_size)456
457# Open a file for intruder logs458log_file = open(LOG_FILE_PATH, 'w')459if not log_file:460return -16, ''461
462# Initializing VideoWriter for each source463if UI and not LOOP_VIDEO:464for video_cap in video_caps:465ret, ret_value = video_cap.init_vw(466int(video_cap.input_height), int(video_cap.input_width))467if ret != 0:468return ret, ret_value469
470# Initialise the class471infer_network = Network()472# Load the network to IE plugin to get shape of input layer473n, c, h, w = infer_network.load_model(474model_xml, TARGET_DEVICE, 1, 1, 2, CPU_EXTENSION)[1]475# Arrange windows so that they are not overlapping476arrange_windows()477
478min_fps = min([i.vc.get(cv2.CAP_PROP_FPS) for i in video_caps])479signal.signal(signal.SIGINT, signal_handler, )480no_more_data = [False] * len(video_caps)481start_time = time.time()482inf_time = 0483next_request_id = 1484cur_request_id = 0485# Main loop starts here. Loop over all the video captures486
487if is_async_mode:488print("Application running in async mode...")489else:490print("Application running in sync mode...")491
492while True:493for idx, video_cap in enumerate(video_caps):494# Get a new frame495vfps = int(round(video_cap.vc.get(cv2.CAP_PROP_FPS)))496for i in range(0, int(round(vfps / min_fps))):497if is_async_mode:498ret, video_cap.next_frame = video_cap.vc.read()499else:500ret, video_cap.frame = video_cap.vc.read()501video_cap.loop_frames += 1502# If no new frame or error in reading a frame, exit the loop503if not ret:504no_more_data[idx] = True505break506if no_more_data[idx]:507stream_end_frame = numpy.zeros((int(video_cap.input_height), int(video_cap.input_width), 1),508dtype='uint8')509stream_end_message = "Stream from {} has ended.".format(510video_cap.cam_name)511cv2.putText(stream_end_frame, stream_end_message, (int(video_cap.input_width / 2) - 30,512int(video_cap.input_height / 2) - 30),513cv2.FONT_HERSHEY_COMPLEX, 0.5, (255, 255, 255), 1)514cv2.imshow(video_cap.cam_name, stream_end_frame)515continue516for i in range(video_cap.no_of_labels):517video_cap.current_count[i] = 0518video_cap.changed_count[i] = False519
520# Resize to expected size (in model .xml file)521# Input frame is resized to infer resolution522if is_async_mode:523in_frame = cv2.resize(video_cap.next_frame, (w, h))524in_frame = in_frame.transpose((2, 0, 1))525in_frame = in_frame.reshape((n, c, h, w))526
527# Start asynchronous inference for specified request.528infer_network.exec_net(next_request_id, in_frame)529video_cap.frame = video_cap.next_frame530# Async enabled and only one video capture531if len(video_caps) == 1:532videoCapResult = video_cap533# Async enabled and more than one video capture534else:535# Get previous index536videoCapResult = video_caps[idx -5371 if idx - 1 >= 0 else len(video_caps) - 1]538
539else:540in_frame = cv2.resize(video_cap.frame, (w, h))541in_frame = in_frame.transpose((2, 0, 1))542in_frame = in_frame.reshape((n, c, h, w))543
544# Start synchronous inference for specified request.545infer_network.exec_net(cur_request_id, in_frame)546videoCapResult = video_cap547
548inf_start = time.time()549# Wait for the result550if infer_network.wait(cur_request_id) == 0:551inf_time = time.time() - inf_start552# Results of the output layer of the network553res = infer_network.get_output(cur_request_id)554for obj in res[0][0]:555label = int(obj[1]) - 1556# Draw the bounding box around the object when the probability is more than specified threshold557if obj[2] > CONF_THRESHOLD_VALUE and used_labels[label]:558videoCapResult.current_count[label] += 1559xmin = int(obj[3] * videoCapResult.input_width)560ymin = int(obj[4] * videoCapResult.input_height)561xmax = int(obj[5] * videoCapResult.input_width)562ymax = int(obj[6] * videoCapResult.input_height)563# Draw bounding box around the intruder detected564cv2.rectangle(videoCapResult.frame, (xmin, ymin),565(xmax, ymax), (0, 255, 0), 4, 16)566
567for i in range(videoCapResult.no_of_labels):568if videoCapResult.candidate_count[i] == videoCapResult.current_count[i]:569videoCapResult.candidate_confidence[i] += 1570else:571videoCapResult.candidate_confidence[i] = 0572videoCapResult.candidate_count[i] = videoCapResult.current_count[i]573
574if videoCapResult.candidate_confidence[i] == CONF_CANDIDATE_CONFIDENCE:575videoCapResult.candidate_confidence[i] = 0576videoCapResult.changed_count[i] = True577else:578continue579
580if videoCapResult.current_count[i] > videoCapResult.last_correct_count[i]:581videoCapResult.total_count[i] += videoCapResult.current_count[i] - \582videoCapResult.last_correct_count[i]583det_objs = videoCapResult.current_count[i] - \584videoCapResult.last_correct_count[i]585total_count = sum(videoCapResult.total_count)586for det_obj in range(det_objs):587current_time = time.strftime("%H:%M:%S")588log = "{} - Intruder {} detected on {}".format(current_time, label_names[i],589videoCapResult.cam_name)590log_list.append(log)591log_file.write(log + "\n")592event = Event(event_time=current_time, intruder=label_names[i], count=total_count,593frame=videoCapResult.frame_count)594videoCapResult.events.append(event)595
596snapshot_name = "output/intruder_{}.png".format(597total_count)598cv2.imwrite(snapshot_name, videoCapResult.frame)599videoCapResult.last_correct_count[i] = videoCapResult.current_count[i]600
601# Create intruder log window, add logs to the frame and display it602log_window = numpy.zeros(603(LOG_WIN_HEIGHT, LOG_WIN_WIDTH, 1), dtype='uint8')604for i, log in enumerate(log_list):605cv2.putText(log_window, log, (10, 20 * i + 15),606cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)607cv2.imshow("Intruder Log", log_window)608videoCapResult.frame_count += 1609
610# Video output611if UI and not LOOP_VIDEO:612videoCapResult.vw.write(videoCapResult.frame)613
614log_message = "Async mode is on." if is_async_mode else \615"Async mode is off."616cv2.putText(videoCapResult.frame, log_message, (10, int(videoCapResult.input_height) - 50),617cv2.FONT_HERSHEY_SIMPLEX, 0.5, (200, 10, 10), 1)618inf_time_message = "Inference time: N\A for async mode" if is_async_mode else \619"Inference time: {:.3f} ms".format(inf_time * 1000)620cv2.putText(videoCapResult.frame, inf_time_message, (10, int(videoCapResult.input_height) - 30),621cv2.FONT_HERSHEY_COMPLEX, 0.5, (200, 10, 10), 1)622fps_time = time.time() - start_time623fps_message = "FPS: {:.3f} fps".format(1/fps_time)624cv2.putText(videoCapResult.frame, fps_message, (10, int(videoCapResult.input_height) - 10),625cv2.FONT_HERSHEY_COMPLEX, 0.5, (200, 10, 10), 1)626
627# Display the video output628cv2.imshow(videoCapResult.cam_name, videoCapResult.frame)629
630start_time = time.time()631
632# Loop video to mimic continuous input if LOOP_VIDEO flag is True633if LOOP_VIDEO and not videoCapResult.is_cam:634vfps = int(round(videoCapResult.vc.get(cv2.CAP_PROP_FPS)))635# If a video capture has ended restart it636if videoCapResult.loop_frames > videoCapResult.vc.get(cv2.CAP_PROP_FRAME_COUNT) - int(round(vfps / min_fps)):637videoCapResult.loop_frames = 0638videoCapResult.vc.set(cv2.CAP_PROP_POS_FRAMES, 0)639
640if is_async_mode:641# Swap infer request IDs642cur_request_id, next_request_id = next_request_id, cur_request_id643
644if cv2.waitKey(1) == 27:645break646
647if cv2.waitKey(1) == 9:648is_async_mode = not is_async_mode649print("Switched to {} mode".format(650"async" if is_async_mode else "sync"))651
652if False not in no_more_data:653break654
655ret = save_json()656if ret != 0:657return ret, ''658
659infer_network.clean()660log_file.close()661return [0, '']662
663
664if __name__ == '__main__':665status, value = intruder_detector()666
667if status == 0:668print("Success!")669elif status == -1:670print("Could not open for write" + value + "!")671elif status == -2:672print("Path to the .xml file not specified!")673print("Specify it using %env MODEL")674elif status == -3:675print("You need to specify the path to the labels file")676print("Specify it using %env LABEL_FILE")677elif status == -4:678print("Error in opening labels file!")679elif status == -5:680print("No labels found in label file!")681elif status == -6:682print("Labels file not found!")683elif status == -7:684print("Error in opening Configuration file " + CONFIG_FILE + "!")685elif status == -8:686print("Could not find the video file " + value + "!")687elif status == -9:688print("\nCould not open " + value + " for reading!")689elif status == -10:690print("Could not create event JSON file " + EVENT_FILE + "!")691elif status == -11:692print("Could not create data JSON file " + DATA_FILE + "!")693elif status == -12:694print(CONFIG_FILE + " configuration file not found!")695elif status == -13:696print(conf_labels_file_path + " label file not found!")697elif status == -14:698print("No input source found in configuration file!")699elif status == -15:700print("Error: No labels currently in use. Please edit " +701CONFIG_FILE+" file!")702elif status == -16:703print("Error in opening intruder log file!")704elif status == -17:705print("Path to cpu extensions library path not specified")706print("Specify it using %env CPU_EXTENSION")707else:708print("Unknown error occurred!")709
710clean_up()711