text: #!/usr/bin/env python3
  """
   Copyright (c) 2018 Intel Corporation.
   Permission is hereby granted, free of charge, to any person obtaining
   a copy of this software and associated documentation files (the
   "Software"), to deal in the Software without restriction, including
   without limitation the rights to use, copy, modify, merge, publish,
   distribute, sublicense, and/or sell copies of the Software, and to
   permit persons to whom the Software is furnished to do so, subject to
   the following conditions:
   The above copyright notice and this permission notice shall be
   included in all copies or substantial portions of the Software.
   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
   LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
   OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
   WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  """
  
  import os
  import sys
  import math
  import time
  from collections import namedtuple
  from argparse import ArgumentParser
  import logging as log
  import numpy as np
  from inference import Network
  from influxdb import InfluxDBClient
  from flask import Flask, render_template, Response
  import cv2
  import json
  
  # Constants
  CONFIG_FILE = "../resources/config.json"
  MAX_FRAME_GONE = 3
  INTEREST_COUNT_TIME = 5
  SENTIMENT_LABEL = ['neutral', 'happy', 'sad', 'surprise', 'anger']
  IPADDRESS = "localhost"
  PORT = 8086
  DATABASE_NAME = "Retail_Analytics"
  CENTROID_DISTANCE = 150
  
  # Global variables
  check_feed_type = [False, False, False]  # [shopper, traffic, shelf]
  centroids = []
  tracked_person = []
  person_id = 0
  interested = 0
  not_interested = 0
  db_client = None
  myriad_plugin = None
  Point = namedtuple("Point", "x,y")
  accepted_devices = ['CPU', 'GPU', 'MYRIAD', 'HETERO:FPGA,CPU', 'HDDL']
  is_async_mode = True
  template_dir = os.path.abspath('../templates')
  
  class Centroid:
      """
      Store centroid details of the face detected for tracking
      """
  
      def __init__(self, p_id, point, gone_count):
          self.id = p_id
          self.point = point
          self.gone_count = gone_count
  
  
  class Person:
      """
      Store the data of the people for tracking
      """
  
      def __init__(self, p_id, in_time):
          self.id = p_id
          self.counted = False
          self.gone = False
          self.in_time = in_time
          self.out_time = None
          self.looking = 0
          self.positive = 0
          self.negative = 0
          self.neutral = 0
          self.sentiment = ''
  
  
  class VideoCap:
      """
      Store the data and manage multiple input video feeds
      """
  
      def __init__(self, input_name, input_number, feed_type, labels=[]):
          self.vc = cv2.VideoCapture(input_name)
          self.input_number = input_number
          self.type = feed_type
          self.infer_network = None
          self.nchw = []
          self.utime = time.time()
          self.curr_req = 0
          self.next_req = 1
  
          if self.type == 'shopper':
              self.nchw_hp = []
              self.nchw_md = []
              self.thresh = 0.7
  
          if self.type == 'shelf' or self.type == 'traffic':
              self.thresh = 0.145
              self.labels = labels
              self.labels_map = []
              self.last_correct_count = [0] * len(self.labels)
              self.total_count = [0] * len(self.labels)
              self.current_count = [0] * len(self.labels)
              self.changed_count = [False] * len(self.labels)
              self.candidate_count = [0] * len(self.labels)
              self.candidate_confidence = [0] * len(self.labels)
              self.CONF_CANDIDATE_CONFIDENCE = 6
  
              if self.type == 'traffic':
                  self.mog = cv2.createBackgroundSubtractorMOG2()
                  self.CONF_CANDIDATE_CONFIDENCE = 3
                  self.thresh = 0.45
  
  
  def parse_conf_file():
      """
      Parse the configuration file and store the data in VideoCap object
  
      :return video_caps: List of VideoCap object containing input stream data
      """
      global CONFIG_FILE
      global check_feed_type
  
      video_caps = []
  
      assert os.path.isfile(CONFIG_FILE), "{} file doesn't exist".format(CONFIG_FILE)
      config = json.loads(open(CONFIG_FILE).read())
      for idx, item in enumerate(config['inputs']):
          labels = []
          parse_video = item['video']
          input_number = idx + 1
          if 'type' in item.keys():
              feed_type = item['type']
              if len(feed_type) == 0:
                  print("Ignoring video {}... Format error".format(parse_video))
                  continue
              if feed_type == 'shelf':
                  check_feed_type[2] = True
                  if 'label' in item.keys():
                      labels = [item['label']]
                      if len(labels) == 0:
                          print("Ignoring video {}... Format error".format(parse_video))
                          continue
                  else:
                      print("Format error while reading labels for {}".format(feed_type))
                      continue
              elif feed_type == 'traffic':
                  check_feed_type[1] = True
                  labels = ['person']
              elif feed_type == 'shopper':
                  check_feed_type[0] = True
              if parse_video.isdigit():
                  video_cap = VideoCap(int(parse_video), input_number, feed_type, labels)
              else:
                  assert os.path.isfile(parse_video), "{} doesn't exist".format(parse_video)
                  video_cap = VideoCap(parse_video, input_number, feed_type, labels)
              video_cap.input_name = parse_video
              video_caps.append(video_cap)
          else:
              print("Feed type not specified for ", parse_video)
  
      for video_cap in video_caps:
          assert video_cap.vc.isOpened(), "Could not open {} for reading".format(video_cap.input_name)
          video_cap.input_width = video_cap.vc.get(3)
          video_cap.input_height = video_cap.vc.get(4)
          if video_cap.type == 'traffic':
              video_cap.accumulated_frame = np.zeros(
                  (int(video_cap.input_height), int(video_cap.input_width)), np.uint8)
  
      return video_caps
  
  
  def load_model_device(infer_network, model, device, in_size, out_size, num_requests, cpu_extension, tag):
      """
      Loads the networks
  
      :param infer_network: Object of the Network() class
      :param model: .xml file of pre trained model
      :param device: Target device
      :param in_size: Number of input layers
      :param out_size: Number of output layers
      :param num_requests: Index of Infer request value. Limited to device capabilities
      :param cpu_extension: extension for the CPU device
      :return:  Shape of input layer
      """
      if 'MULTI' not in device and device not in accepted_devices:
          print("Unsupported device: " + device)
          sys.exit(1)
      elif 'MULTI' in device:
          target_devices = device.split(':')[1].split(',')
          for multi_device in target_devices:
              if multi_device not in accepted_devices:
                  print("Unsupported device: " + device)
                  sys.exit(1)
  
      global myriad_plugin
      if device == 'MYRIAD':
          if myriad_plugin is None:
              myriad_plugin, (nchw) = infer_network.load_model(model, device, in_size, out_size, num_requests)
          else:
              nchw = infer_network.load_model(model, device, in_size, out_size, num_requests, plugin=myriad_plugin)[1]
      else:
          nchw = infer_network.load_model(model, device, in_size, out_size, num_requests, cpu_extension, tag)[1]
  
      return nchw
  
  
  def load_models(video_caps):
      """
      Load the required models
  
      :param video_caps: List of VideoCap objects
      :return: None
      """
      global check_feed_type
      plugin = None
  
      face_device = os.environ['FACE_DEVICE'] if 'FACE_DEVICE' in os.environ.keys() else "CPU"
      mood_device = os.environ['MOOD_DEVICE'] if 'MOOD_DEVICE' in os.environ.keys() else "CPU"
      pose_device = os.environ['POSE_DEVICE'] if 'POSE_DEVICE' in os.environ.keys() else "CPU"
      obj_device = os.environ['OBJ_DEVICE'] if 'OBJ_DEVICE' in os.environ.keys() else "CPU"
      person_device = os.environ['PERSON_DEVICE'] if 'PERSON_DEVICE' in os.environ.keys() else "CPU"
  
      cpu_extension = os.environ['CPU_EXTENSION'] if 'CPU_EXTENSION' in os.environ.keys() else None
      face_model = os.environ['FACE_MODEL'] if 'FACE_MODEL' in os.environ.keys() else None
      pose_model = os.environ['POSE_MODEL'] if 'POSE_MODEL' in os.environ.keys() else None
      mood_model = os.environ['MOOD_MODEL'] if 'MOOD_MODEL' in os.environ.keys() else None
      obj_model = os.environ['OBJ_MODEL'] if 'OBJ_MODEL' in os.environ.keys() else None
      person_model = os.environ['PERSON_MODEL'] if 'PERSON_MODEL' in os.environ.keys() else None
  
      # Check if one the feed type is "shopper". If yes, load the face, head pose and mood detection model
      if check_feed_type[0]:
          assert face_model, 'Please specify the path to face detection model using the environment variable FACE_MODEL'
          assert pose_model, 'Please specify the path to head pose model using the environment variable POSE_MODEL'
          assert mood_model, 'Please specify the path to mood detection model using the environment variable MOOD_MODEL'
          
          infer_network_face = Network()
          infer_network_pose = Network()
          infer_network_mood = Network()
  
          tag_face = {"VPU_HDDL_GRAPH_TAG":"tagFace"}
          tag_pose = {"VPU_HDDL_GRAPH_TAG":"tagPose"}
          tag_mood = {"VPU_HDDL_GRAPH_TAG":"tagMood"}
          nchw_fd = load_model_device(infer_network_face, face_model, face_device, 1, 1, 2, cpu_extension, tag_face)
          nchw_hp = load_model_device(infer_network_pose, pose_model, pose_device, 1, 3, 2, cpu_extension, tag_pose)
          nchw_md = load_model_device(infer_network_mood, mood_model, mood_device, 1, 1, 2, cpu_extension, tag_mood)
  
      # Check if one the feed type is "traffic" or "shelf". If yes, load the mobilenet-ssd model
      if check_feed_type[2]:
          infer_network = Network()
          tag_obj = {"VPU_HDDL_GRAPH_TAG":"tagMobile"}
          nchw = load_model_device(infer_network, obj_model, obj_device, 1, 1, 2, cpu_extension, tag_obj)
      if check_feed_type[1]:
          infer_network_person = Network()
          tag_person = {"VPU_HDDL_GRAPH_TAG":"tagPerson"}
          nchw_pr = load_model_device(infer_network_person, person_model, person_device, 2, 1, 2,
                                      cpu_extension, tag_person)
      for video_cap in video_caps:
          if video_cap.type == 'shopper':
              video_cap.infer_network = infer_network_face
              video_cap.infer_network_hp = infer_network_pose
              video_cap.infer_network_md = infer_network_mood
              video_cap.nchw.extend(nchw_fd)
              video_cap.nchw_hp.extend(nchw_hp)
              video_cap.nchw_md.extend(nchw_md)
  
          if video_cap.type == 'shelf':
              video_cap.infer_network = infer_network
              video_cap.nchw.extend(nchw)
          if video_cap.type == 'traffic':
              video_cap.infer_network = infer_network_person
              video_cap.nchw.extend(nchw_pr)
  
  
  def object_detection(video_cap, res):
      """
      Parse the inference result to get the detected object
  
      :param video_cap: VideoCap object of the frame on which object is detected
      :param res: Inference output
      :return obj_det: List of coordinates of bounding boxes of the objects detected
      """
      obj_det = []
  
      for obj in res[0][0]:
          label = int(obj[1]) - 1
  
          # Draw objects only when probability is more than specified threshold
          if obj[2] > video_cap.thresh:
              # If the feed type is traffic shelf, look only for the person
              if  video_cap.type == 'traffic' and label == 0:
                  video_cap.current_count[label] += 1
  
              # If the feed type is traffic or shelf, look only for the objects specified by the user
              if video_cap.type == 'shelf':
                  if label not in video_cap.labels_map:
                      continue
                  label_idx = video_cap.labels_map.index(label)
                  video_cap.current_count[label_idx] += 1
  
              if obj[3] < 0:
                  obj[3] = 0
              if obj[4] < 0:
                  obj[4] = 0
  
              xmin = int(obj[3] * video_cap.input_width)
              ymin = int(obj[4] * video_cap.input_height)
              xmax = int(obj[5] * video_cap.input_width)
              ymax = int(obj[6] * video_cap.input_height)
              obj_det.append([xmin, ymin, xmax, ymax])
  
      return obj_det
  
  
  def get_used_labels(video_caps):
      """
      Read the model's label file and get the position of labels required by the application
  
      :param video_caps: List of VideoCap objects
      :return labels: List of labels present in the label file
      """
      global check_feed_type
  
      if check_feed_type[1] is False and check_feed_type[2] is False:
          return
  
      label_file = os.environ['LABEL_FILE'] if 'LABEL_FILE' in os.environ.keys() else None
      assert label_file, "Please specify the path label file using the environmental variable LABEL_FILE"
      assert os.path.isfile(label_file), "{} file doesn't exist".format(label_file)
      with open(label_file, 'r') as label_file:
          labels = [x.strip() for x in label_file]
  
      assert labels != [], "No labels found in {} file".format(label_file)
      for video_cap in video_caps:
          if video_cap.type == 'shelf' or video_cap.type == 'traffic':
              for label in video_cap.labels:
                  if label in labels:
                      label_idx = labels.index(label)
                      video_cap.labels_map.append(label_idx)
                  else:
                      video_cap.labels_map.append(False)
  
      return labels
  
  
  def process_output(video_cap):
      """
      Count the number of object detected
  
      :param video_cap: VideoCap object
      :return: None
      """
      for i in range(len(video_cap.labels)):
          if video_cap.candidate_count[i] == video_cap.current_count[i]:
              video_cap.candidate_confidence[i] += 1
          else:
              video_cap.candidate_confidence[i] = 0
              video_cap.candidate_count[i] = video_cap.current_count[i]
  
          if video_cap.candidate_confidence[i] == video_cap.CONF_CANDIDATE_CONFIDENCE:
              video_cap.candidate_confidence[i] = 0
              video_cap.changed_count[i] = True
          else:
              continue
          if video_cap.current_count[i] > video_cap.last_correct_count[i]:
              video_cap.total_count[i] += video_cap.current_count[i] - video_cap.last_correct_count[i]
  
          video_cap.last_correct_count[i] = video_cap.current_count[i]
  
  
  def remove_centroid(p_id):
      """
      Remove the centroid from the "centroids" list when the person is out of the frame and
      set the person.gone variable as true
  
      :param p_id: ID of the person whose centroid data has to be deleted
      :return: None
      """
      global centroids
      global tracked_person
  
      for idx, centroid in enumerate(centroids):
          if centroid.id is p_id:
              del centroids[idx]
              break
  
      if tracked_person[p_id]:
          tracked_person[p_id].gone = True
          tracked_person[p_id].out_time = time.time()
  
  
  def add_centroid(point):
      """
      Add the centroid of the object to the "centroids" list
  
      :param point: Centroid point to be added
      :return: None
      """
      global person_id
      global centroids
      global tracked_person
  
      centroid = Centroid(person_id, point, gone_count=0)
      person = Person(person_id, time.time())
      centroids.append(centroid)
      tracked_person.append(person)
      person_id += 1
  
  
  def closest_centroid(point):
      """
      Find the closest centroid
  
      :param point: Coordinate of the point for which the closest centroid point has to be detected
      :return p_idx: Id of the closest centroid
              dist: Distance of point from the closest centroid
      """
      global centroids
      p_idx = 0
      dist = sys.float_info.max
  
      for idx, centroid in enumerate(centroids):
          _point = centroid.point
          dx = point.x - _point.x
          dy = point.y - _point.y
          _dist = math.sqrt(dx * dx + dy * dy)
          if _dist < dist:
              dist = _dist
              p_idx = centroid.id
  
      return [p_idx, dist]
  
  
  def update_centroid(points, looking, sentiment, fps):
      """
      Update the centroid data in the centroids list and check whether the person is interested or not interested
  
      :param points: List of centroids of the faces detected
      :param looking: List of bool values indicating if the person is looking at the camera or not
      :param sentiment: List containing the mood of the people looking at the camera
      :param fps: FPS of the input stream
      :return: None
      """
      global MAX_FRAME_GONE
      global INTEREST_COUNT_TIME
      global interested
      global not_interested
      global centroids
      global tracked_person
  
      if len(points) is 0:
          for idx, centroid in enumerate(centroids):
              centroid.gone_count += 1
              if centroid.gone_count > MAX_FRAME_GONE:
                  remove_centroid(centroid.id)
  
      if not centroids:
          for idx, point in enumerate(points):
              add_centroid(point)
      else:
          checked_points = len(points) * [None]
          checked_points_dist = len(points) * [None]
          for idx, point in enumerate(points):
              p_id, dist = closest_centroid(point)
              if dist > CENTROID_DISTANCE:
                  continue
  
              if p_id in checked_points:
                  p_idx = checked_points.index(p_id)
                  if checked_points_dist[p_idx] > dist:
                      checked_points[p_idx] = None
                      checked_points_dist[p_idx] = None
  
              checked_points[idx] = p_id
              checked_points_dist[idx] = dist
  
          for centroid in centroids:
              if centroid.id in checked_points:
                  p_idx = checked_points.index(centroid.id)
                  centroid.point = points[p_idx]
                  centroid.gone_count = 0
              else:
                  centroid.gone_count += 1
                  if centroid.gone_count > MAX_FRAME_GONE:
                      remove_centroid(centroid.id)
  
          for idx in range(len(checked_points)):
              if checked_points[idx] is None:
                  add_centroid(points[idx])
              else:
                  if looking[idx] is True:
                      tracked_person[checked_points[idx]].sentiment = sentiment[idx]
                      tracked_person[checked_points[idx]].looking += 1
                      if sentiment[idx] == "happy" or sentiment[idx] == "surprise":
                          tracked_person[checked_points[idx]].positive += 1
                      elif sentiment[idx] == 'sad' or sentiment[idx] == 'anger':
                          tracked_person[checked_points[idx]].negative += 1
                      elif sentiment[idx] == 'neutral':
                          tracked_person[checked_points[idx]].neutral += 1
                  else:
                      tracked_person[checked_points[idx]].sentiment = "Not looking"
  
          for person in tracked_person:
              if person.counted is False:
                  positive = person.positive + person.neutral
  
                  # If the person is looking at the camera for specified time
                  # and his mood is positive, increment the interested variable
                  if (person.looking > fps * INTEREST_COUNT_TIME) and (positive > person.negative):
                      interested += 1
                      person.counted = True
  
                  # If the person is gone out of the frame, increment the not_interested variable
                  if person.gone is True:
                      not_interested += 1
                      person.counted = True
  
  
  def detect_head_pose_and_emotions(video_cap, object_det, cur_request_id_sh, next_request_id_sh):
      """
      Detect the head pose and emotions of the faces detected
  
      :param video_cap: VideoCap object
      :param object_det: List of faces detected in the frame
      :return: None
      """
  
      global SENTIMENT_LABEL
      global centroids
      global is_async_mode
  
      frame_centroids = []
      looking = []
      sentiment = []
  
      for face in object_det:
          xmin, ymin, xmax, ymax = face
  
          # Find the centroid of the face
          width = xmax - xmin
          height = ymax - ymin
          x = xmin + int(width / 2)
          y = ymin + int(height / 2)
          point = Point(x, y)
          frame_centroids.append(point)
  
          # Check the head pose
          if is_async_mode:
              head_pose = video_cap.next_frame[ymin:ymax, xmin:xmax]
              in_frame = cv2.resize(head_pose, (video_cap.nchw_hp[3], video_cap.nchw_hp[2]))
              in_frame = in_frame.transpose((2, 0, 1))
              in_frame = in_frame.reshape((video_cap.nchw_hp[0], video_cap.nchw_hp[1],
                                       video_cap.nchw_hp[2], video_cap.nchw_hp[3]))
  
              video_cap.infer_network_hp.exec_net(video_cap.next_req, in_frame)
          else:
              head_pose = video_cap.frame[ymin:ymax, xmin:xmax]
              in_frame = cv2.resize(head_pose, (video_cap.nchw_hp[3], video_cap.nchw_hp[2]))
              in_frame = in_frame.transpose((2, 0, 1))
              in_frame = in_frame.reshape((video_cap.nchw_hp[0], video_cap.nchw_hp[1],
                                       video_cap.nchw_hp[2], video_cap.nchw_hp[3]))
  
              video_cap.infer_network_hp.exec_net(video_cap.curr_req, in_frame)
   
          if video_cap.infer_network_hp.wait(video_cap.curr_req) == 0:
  
              # Parse head pose detection results
              angle_p_fc = video_cap.infer_network_hp.get_output(video_cap.curr_req, "angle_p_fc")
              angle_y_fc = video_cap.infer_network_hp.get_output(video_cap.curr_req, "angle_y_fc")
  
              # Check if the person is looking at the camera
              if (angle_y_fc > -22.5) & (angle_y_fc < 22.5) & (angle_p_fc > -22.5) & (angle_p_fc < 22.5):
                  looking.append(True)
  
                  # Find the emotions of the person
                  in_frame = cv2.resize(head_pose, (video_cap.nchw_md[3], video_cap.nchw_md[2]))
                  in_frame = in_frame.transpose((2, 0, 1))
                  in_frame = in_frame.reshape((video_cap.nchw_md[0], video_cap.nchw_md[1],
                                               video_cap.nchw_md[2], video_cap.nchw_md[3]))
                  video_cap.infer_network_md.exec_net(0, in_frame)
                  video_cap.infer_network_md.wait(0)
                  res = video_cap.infer_network_md.get_output(0)
                  emotions = np.argmax(res)
                  sentiment.append(SENTIMENT_LABEL[emotions])
              else:
                  looking.append(False)
                  sentiment.append(-1)
  
          if is_async_mode:
              video_cap.curr_req, video_cap.next_req = video_cap.next_req, video_cap.curr_req
              video_cap.frame = video_cap.next_frame
      update_centroid(frame_centroids, looking, sentiment, video_cap.vc.get(cv2.CAP_PROP_FPS))
      for idx, centroid in enumerate(centroids):
          cv2.rectangle(video_cap.frame, (centroid.point.x, centroid.point.y),
                        (centroid.point.x + 1, centroid.point.y + 1), (0, 255, 0), 4, 16)
          cv2.putText(video_cap.frame, "person:{}".format(centroid.id), (centroid.point.x + 1, centroid.point.y - 5),
                      cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)
  
  
  def heatmap_generation(video_cap):
      """
      Generates the heatmap
  
      :param video_cap: VideoCap of input feed for which the heatmap has to be generated
      :return: None
      """
      # Convert to grayscale
      gray = cv2.cvtColor(video_cap.frame, cv2.COLOR_BGR2GRAY)
  
      # Remove the background
      fgbgmask = video_cap.mog.apply(gray)
  
      # Threshold the image
      thresh = 2
      max_value = 2
      threshold_frame = cv2.threshold(fgbgmask, thresh, max_value, cv2.THRESH_BINARY)[1]
  
      # Add threshold image to the accumulated image
      video_cap.accumulated_frame = cv2.add(threshold_frame, video_cap.accumulated_frame)
      colormap_frame = cv2.applyColorMap(video_cap.accumulated_frame, cv2.COLORMAP_HOT)
      video_cap.frame = cv2.addWeighted(video_cap.frame, 0.6, colormap_frame, 0.4, 0)
  
  
  def update_info_shopper(video_cap):
      """
      Send "shopper" data to InfluxDB
  
      :param video_cap: List of VideoCap object
      :return: None
      """
      global tracked_person
      global interested
      global not_interested
      global db_client
  
      json_body = [{
          "measurement": "{}_interest".format(video_cap.type),
          "fields": {
              "time": time.time(),
              "Interested": interested,
              "Not Interested": not_interested,
              "Total Count": len(tracked_person)
          }
      }]
      db_client.write_points(json_body)
      for person in tracked_person:
          if person.gone is False:
              tm = time.time() - person.in_time
              looking_time = person.looking / video_cap.vc.get(cv2.CAP_PROP_FPS)
              json_body = [{
                  "measurement": "{}_duration".format(video_cap.type),
                  "fields": {
                      "person": person.id,
                      "Looking time": looking_time,
                      "Time in frame": tm,
                      "Current Mood": person.sentiment
                  }
              }]
          db_client.write_points(json_body)
  
  
  def update_info_object(labels, video_cap):
      """
      Send "traffic" and "shelf" data to InfluxDB
  
      :param labels: List of labels present in label file
      :param video_cap: VideoCap object
      :return: None
      """
      global db_client
  
      for idx, label in enumerate(video_cap.labels_map):
          json_body = [
              {"measurement": video_cap.type,
               "tags": {
                   "object": labels[label],
               },
               "fields": {
                   "time": time.time(),
                   "Current Count": video_cap.current_count[idx],
                   "Total Count": video_cap.total_count[idx],
               }
               }]
          db_client.write_points(json_body)
  
  
  def create_database():
      """
      Connect to InfluxDB and create the database
  
      :return: None
      """
      global db_client
      global IPADDRESS
      IPADDRESS = os.environ['DB_IPADDRESS'] if 'DB_IPADDRESS' in os.environ.keys() else "localhost"
      proxy = {"http": "http://{}:{}".format(IPADDRESS, PORT)}
      db_client = InfluxDBClient(host=IPADDRESS, port=PORT, proxies=proxy, database=DATABASE_NAME)
      db_client.create_database(DATABASE_NAME)
  
  
  def retail_analytics():
      """
      Detect objects on multiple input video feeds and process the output
  
      :return: None
      """
  
      global centroids
      global tracked_person
      global db_client
      global is_async_mode, cur_request_id, next_request_id
  
      objdetect = []
      log.basicConfig(format="[ %(levelname)s ] %(message)s", level=log.INFO, stream=sys.stdout)
      logger = log.getLogger()
  
      video_caps = parse_conf_file()
      assert len(video_caps) != 0, "No input source given in Configuration file"
      flag = os.environ['FLAG'] if 'FLAG' in os.environ.keys() else "async"
      load_models(video_caps)
      labels = get_used_labels(video_caps)
      create_database()
  
      min_fps = min([i.vc.get(cv2.CAP_PROP_FPS) for i in video_caps])
      no_more_data = [False] * len(video_caps)
      frames = [None] * len(video_caps)
      start_time = time.time()
      if flag == "async":
          is_async_mode = True
          print('Application running in async mode')
      else:
          is_async_mode = False
          print('Application running in sync mode')
      cur_request_id_tr = 0
      next_request_id_tr = 1
      cur_request_id = 0
      next_request_id = 1
      cur_request_id_sf = 0
      next_request_id_sf = 1
      cur_request_id_sh = 0
      next_request_id_sh = 1
      input_blob = ["data", "im_info"]
      det_time = 0
      cur_request_id = 0
  
      # Main loop for object detection in multiple video streams
      while True:
          for idx, video_cap in enumerate(video_caps):
              vfps = int(round(video_cap.vc.get(cv2.CAP_PROP_FPS)))
              for i in range(0, int(round(vfps / min_fps))):
                  if is_async_mode:
                      ret, video_cap.next_frame = video_cap.vc.read()
                  else:
                      ret, video_cap.frame = video_cap.vc.read()
  
                  # If no new frame or error in reading the frame, exit the loop
                  if not ret:
                      no_more_data[idx] = True
                      break
  
                  if video_cap.type == 'traffic' or video_cap.type == 'shelf':
                      video_cap.current_count = [0] * len(video_cap.labels)
                      video_cap.changed_count = [False] * len(video_cap.labels)
                  inf_start = time.time()
                  if is_async_mode:
                      in_frame = cv2.resize(video_cap.next_frame, (video_cap.nchw[3], video_cap.nchw[2]))
                      in_frame = in_frame.transpose((2, 0, 1))
                      in_frame = in_frame.reshape((video_cap.nchw[0], video_cap.nchw[1], video_cap.nchw[2], video_cap.nchw[3]))
                      if video_cap.type == 'traffic':
                          video_cap.infer_network.exec_net(next_request_id_tr, in_frame, input_blob, video_cap.vc.get(3), video_cap.vc.get(4))
                          cur_request_id = cur_request_id_tr
                      elif video_cap.type == 'shelf':
                          video_cap.infer_network.exec_net(next_request_id_sf, in_frame)
                          cur_request_id = cur_request_id_sf
                      else:
                          video_cap.infer_network.exec_net(next_request_id_sh, in_frame)
                          cur_request_id = cur_request_id_sh
                      video_cap.frame = video_cap.next_frame
                  else:
                      in_frame = cv2.resize(video_cap.frame, (video_cap.nchw[3], video_cap.nchw[2]))
                      in_frame = in_frame.transpose((2, 0, 1))
                      in_frame = in_frame.reshape((video_cap.nchw[0], video_cap.nchw[1], video_cap.nchw[2], video_cap.nchw[3]))
                      if video_cap.type == 'traffic':
                          video_cap.infer_network.exec_net(cur_request_id_tr, in_frame, input_blob, video_cap.vc.get(3), video_cap.vc.get(4))
                      elif video_cap.type == 'shelf':
                          video_cap.infer_network.exec_net(cur_request_id_sf, in_frame)
                      else:
                          video_cap.infer_network.exec_net(cur_request_id_sh, in_frame)
                  if video_cap.infer_network.wait(cur_request_id) == 0:
                      inf_end = time.time()
                      det_time = inf_end - inf_start
  
                      # Pass the frame to the inference engine and get the results
                      res = video_cap.infer_network.get_output(cur_request_id)
  
                      # Process the result obtained from the inference engine
                      object_det = object_detection(video_cap, res)
  
                      # If the feed type is "traffic" or "shelf", check the current and total count of the object
                      if video_cap.type == 'traffic' or video_cap.type == 'shelf':
                          process_output(video_cap)
  
                          # If feed type is "traffic", generate the heatmap
                          if video_cap.type == 'traffic':
                              heatmap_generation(video_cap)
  
                          # Send the data to InfluxDB
                          if time.time() >= video_cap.utime + 1:
                              update_info_object(labels, video_cap)
                              video_cap.utime = time.time()
  
                      else:
                          # Detect head pose and emotions of the faces detected
                          detect_head_pose_and_emotions(video_cap, object_det, cur_request_id_sh, next_request_id_sh)
  
                          # Send the data to InfluxDB
                          if time.time() >= video_cap.utime + 1:
                              update_info_shopper(video_cap)
                              video_cap.utime = time.time()
  
                  if is_async_mode:
                      if video_cap.type == 'traffic':
                          cur_request_id_tr, next_request_id_tr = next_request_id_tr, cur_request_id_tr
                      elif video_cap.type == 'shopper':
                          cur_request_id_sh, next_request_id_sh = next_request_id_sh, cur_request_id_sh
                      else:
                          cur_request_id_sf, next_request_id_sf = next_request_id_sf, cur_request_id_sf                
  
                  fps_time = time.time() - start_time
                  fps_message = "FPS: {:.3f} fps".format(1 / fps_time)
                  start_time = time.time()
                  inf_time_message = "Inference time: N\A for async mode" if is_async_mode else\
                  "Inference time: {:.3f} ms".format(det_time * 1000)
                  cv2.putText(video_cap.frame, inf_time_message, (10, 15), cv2.FONT_HERSHEY_COMPLEX, 0.5,
                              (200, 10, 10), 1)
                  cv2.putText(video_cap.frame, fps_message, (10, int(video_cap.input_height) - 10),
                              cv2.FONT_HERSHEY_COMPLEX, 0.5, (200, 10, 10), 1)
  
              # If no new frame, continue to the next input feed
              if no_more_data[idx] is True:
                  continue
  
              # Print the results on the frame and stream it
              message = "Feed Type: {}".format(video_cap.type)
              cv2.putText(video_cap.frame, message, (10, 30),
                          cv2.FONT_HERSHEY_COMPLEX, 0.5, (200, 10, 10), 1)
  
              if video_cap.type == 'traffic' or video_cap.type == 'shelf':
                  ht = 50
                  for indx, label in enumerate(video_cap.labels_map):
                      message = "{} -> Total Count: {}, Current Count: {}".format(labels[label],
                                                                                  video_cap.total_count[indx],
                                                                                  video_cap.current_count[indx])
                      cv2.putText(video_cap.frame, message, (10, ht), cv2.FONT_HERSHEY_COMPLEX, 0.5, (200, 10, 10), 1)
                      ht += 20
              else:
                  message = "Face -> Total Count: {}, Current Count: {}".format(len(tracked_person), len(centroids))
                  cv2.putText(video_cap.frame, message, (10, 50), cv2.FONT_HERSHEY_COMPLEX, 0.5, (200, 10, 10), 1)
                  ht = 75
                  for person in tracked_person:
                      if person.gone is False:
                          message = "Person {} is {}".format(person.id, person.sentiment)
                          cv2.putText(video_cap.frame, message, (10, ht), cv2.FONT_HERSHEY_COMPLEX, 0.5, (200, 10, 10), 1)
                          ht += 20
  
              frames[idx] = video_cap.frame
  
          # Resize the processed frames to stream on Grafana
          for idx, img in enumerate(frames):
              frames[idx] = cv2.resize(img, (480, 360))
  
          # Encode the frames into a memory buffer.
          ret, img = cv2.imencode('.jpg', np.hstack(frames))
          img = img.tobytes()
  
          # Yield the output frame to the server
          yield (b'--frame\r\n'
                 b'Content-Type: image/jpeg\r\n\r\n' + img + b'\r\n\r\n')
  
          # If no more frames, exit the loop
          if False not in no_more_data:
              break
  
  
  # Create object for Flask class
  app = Flask(__name__, template_folder=template_dir)
  app.logger.disabled = True
  log_ = log.getLogger('werkzeug')
  log_.disabled = True
  
  
  # Trigger the index() function on opening "0.0.0.0:5000/" URL
  @app.route('/')
  def index():
      """
      Trigger the index() function on opening "0.0.0.0:5000/" URL
      :return: html file
      """
      return render_template('index.html')
  
  
  # Trigger the video_feed() function on opening "0.0.0.0:5000/video_feed" URL
  @app.route('/video_feed')
  def video_feed():
      """
      Trigger the video_feed() function on opening "0.0.0.0:5000/video_feed" URL
      :return:
      """
      return Response(retail_analytics(), mimetype='multipart/x-mixed-replace; boundary=frame')
  
  
  if __name__ == '__main__':
      app.run(host='0.0.0.0')
  
  