text: import os
  import random
  import time
  from threading import Thread
  from os import path
  from logging import getLogger
  
  from simplejson import load
  import cv2
  import numpy as np
  from yaml import safe_load
  
  from ndu_gate_camera.api.video_source import VideoSourceType
  from ndu_gate_camera.camera.roi_manager import ROIManager
  from ndu_gate_camera.camera.video_sources.camera_video_source import CameraVideoSource
  from ndu_gate_camera.camera.video_sources.file_video_source import FileVideoSource
  from ndu_gate_camera.camera.video_sources.ip_camera_video_source import IPCameraVideoSource
  from ndu_gate_camera.camera.video_sources.pi_camera_video_source import PiCameraVideoSource
  from ndu_gate_camera.camera.video_sources.youtube_video_source import YoutubeVideoSource
  from ndu_gate_camera.camera.video_sources.image_video_source import ImageVideoSource
  from ndu_gate_camera.utility import constants, image_helper, string_helper
  from ndu_gate_camera.utility.ndu_utility import NDUUtility
  from ndu_gate_camera.utility.download_zipped_helper import download_runner_data
  
  DEFAULT_RUNNERS = {
      # "drivermonitor": "DriverMonitorRunner",
      # "socialdistance": "SocialDistanceRunner",
      # "emotionanalysis": "EmotionAnalysisRunner",
  }
  
  log = getLogger("service")
  
  
  class NDUCameraService(Thread):
      def __init__(self, instance=None, config_dir="", handler=None, is_main_thread=True, extension_folder=None):
          self.__is_main_thread = is_main_thread
          if instance is None:
              instance = {}
          super().__init__()
          self.__result_handler = handler
          self._ndu_gate_config_dir = config_dir
          self.__extension_folder = extension_folder
          self.RUNNERS = instance.get("runners", [])
          self.SOURCE_TYPE = VideoSourceType.CAMERA
          self.SOURCE_CONFIG = None
  
          if instance.get("source"):
              self.SOURCE_CONFIG = instance.get("source")
              type_str = self.SOURCE_CONFIG.get("type", "PI_CAMERA")
              if VideoSourceType[type_str]:
                  self.SOURCE_TYPE = VideoSourceType[type_str]
              else:
                  self.SOURCE_TYPE = VideoSourceType.PI_CAMERA
  
          self.__frame_send_interval = self.SOURCE_CONFIG.get("frame_send_interval", 1000)
          self.__motion_kernel = self.SOURCE_CONFIG.get("motion_kernel", None)
          self.__camera_refresh_interval = self.SOURCE_CONFIG.get("camera_refresh_interval", 5000)
          self.__preview_show_motion_kernel = self.SOURCE_CONFIG.get("preview_show_motion_kernel", False)
          self.__preview_show = self.SOURCE_CONFIG.get("preview_show", False)
          if self.__preview_show:
              self.__preview_inited = False
              self.__last_preview_image = None
              self.__preview_show_debug_texts = self.SOURCE_CONFIG.get("preview_show_debug_texts", True)
              self.__preview_show_runner_info = self.SOURCE_CONFIG.get("preview_show_runner_info", True)
              self.__preview_show_score = self.SOURCE_CONFIG.get("preview_show_score", False)
              self.__preview_show_rect_name = self.SOURCE_CONFIG.get("preview_show_rect_name", True)
              self.__preview_show_track_id = self.SOURCE_CONFIG.get("preview_show_track_id", True)
              self.__preview_show_track_pnt = self.SOURCE_CONFIG.get("preview_show_track_pnt", False)
              if self.__preview_show_track_pnt:
                  self._track_pnt_layer = None
                  self._track_pnts = {}
              f = self.SOURCE_CONFIG.get("preview_show_rect_filter", None)
              if f is not None:
                  if isinstance(f, str):
                      self.__preview_show_rect_filters = [f]
                  elif isinstance(f, list):
                      self.__preview_show_rect_filters = f
                  else:
                      raise Exception('Bad "preview_show_rect_filter" definition!')
              else:
                  self.__preview_show_rect_filters = None
  
              self.__preview_write = self.SOURCE_CONFIG.get("preview_write", False)
              self.__preview_last_data = []
              self.__preview_last_data_show_times = []
              self.__preview_write_file_name = self.SOURCE_CONFIG.get("preview_write_file_name", "")
              self.__total_elapsed_times = []
  
          self._track_history = {}
          self._exit_requested = False
          self.__max_frame_dim = self.SOURCE_CONFIG.get("max_frame_dim", None)
          self.__min_frame_dim = self.SOURCE_CONFIG.get("min_frame_dim", None)
          self.__sleep = self.SOURCE_CONFIG.get("sleep", 0)
          self.__skip_frame = self.SOURCE_CONFIG.get("skip_frame", 0)
          self._skip = 0
          self.__color_toggle = None
  
          self.frame_sent = self.SOURCE_CONFIG.get("frame_sent", False)
  
          self.default_runners = DEFAULT_RUNNERS
          self.runners_configs = []
          self.runners_configs_by_key = {}
          self.implemented_runners = {}
          self.available_runners = {}
          self.download_urls = {}
  
          self._load_download_urls()
          self._load_runners()
          self._connect_with_runners()
  
          self.video_source = None
          self._steps = {}
          # self._start()
  
      # def run(self):
      #     self.start()
  
      def _load_download_urls(self, ):
          load_download_urls = path.join(self._ndu_gate_config_dir, 'data_download_urls.yaml')
          print("_load_download_urls config file : {}".format(load_download_urls))
          try:
              with open(load_download_urls, encoding="utf-8") as general_config:
                  self.download_urls = safe_load(general_config)
  
          except:
              print('TODO - print exception details')
  
      def _load_runners(self):
          """
          config dosyasında belirtilen NDUCameraRunner imaplementasyonlarını bulur ve _implemented_runners içerisine ekler
          Aynı şekilde herbir runner için config dosyalarını bulur ve connectors_configs içerisine ekler
          """
          runners_configs_temp = {}
          last_priority = 1000000
  
          if self.RUNNERS:
              for runner in self.RUNNERS:
                  log.debug("runner config : %s", runner)
                  try:
                      if runner.get("status", 1) == 0:  # runner status default value is 1>>>
                          log.debug("runner is not active %s", runner)
                          continue
  
                      runner_type = runner.get("type", None)
                      if runner_type is None:
                          log.warning("type not found for %s", runner)
                          continue
  
                      class_name = self.default_runners.get(runner_type, runner.get("class", None))
                      if class_name is None:
                          log.warning("class name not found for %s", runner)
                          continue
  
                      runner_class_info = NDUUtility.check_and_import(runner_type, class_name,
                                                                      package_uuids=runner.get("uuids", None),
                                                                      extension_folder=self.__extension_folder)
  
                      if runner_class_info is None or runner_class_info[0] is None:
                          log.warning("class name implementation not found for %s - %s", runner_type, class_name)
                          continue
  
                      runner_class = runner_class_info[0]
  
                      runner_key = self.__get_runner_key(runner_type, class_name)
                      self.implemented_runners[runner_key] = runner_class
  
                      configuration_name = runner['configuration']
                      config_file = self._ndu_gate_config_dir + configuration_name
  
                      if path.isfile(config_file):
                          with open(config_file, 'r', encoding="UTF-8") as conf_file:
                              runner_conf = load(conf_file)
                              runner_conf["name"] = runner["name"]
                      else:
                          log.error("config file is not found %s", config_file)
                          runner_conf = {"name": runner["name"]}
  
                      roi_manager = None
                      roi_configuration_name = runner.get('roi_configuration', None)
                      if roi_configuration_name is not None:
                          roi_config_file = self._ndu_gate_config_dir + roi_configuration_name
                          try:
                              if path.isfile(roi_config_file):
                                  with open(roi_config_file, 'r', encoding="UTF-8") as roi_conf_file:
                                      runner_roi_conf = load(roi_conf_file)
                                      roi_manager = ROIManager(runner_roi_conf, runner["name"])
                                      runner_conf["roi_manager"] = roi_manager
                              else:
                                  log.error("roi config file is not found %s", roi_config_file)
                          except Exception as e1:
                              log.error("Error on loading runner roi config %s", roi_config_file)
                              log.exception(e1)
  
                      runner_custom_conf = {}
                      custom_config_file = self._ndu_gate_config_dir + runner_type + "_custom.json"
                      if path.isfile(custom_config_file):
                          with open(custom_config_file, 'r', encoding="UTF-8") as conf_file:
                              runner_custom_conf = load(conf_file)
                      else:
                          log.error("custom config file is not found %s", custom_config_file)
  
                      runner_conf["custom_config"] = runner_custom_conf
  
                      runner_unique_key = self.__get_runner_configuration_key(runner_type, class_name, configuration_name)
  
                      runner_priority = runner.get("priority", None)
                      if runner_priority is None:
                          runner_priority = last_priority
                          last_priority = last_priority + 100
  
                      rt = runners_configs_temp[runner_unique_key] = {
                          "name": runner["name"],
                          "type": runner_type,
                          "class": runner_class,
                          "configuration_name": configuration_name,
                          "config": runner_conf,
                          "priority": runner_priority,
                          "runner_key": runner_key,
                          "runner_unique_key": runner_unique_key,
                          "step_frame": runner.get("step_frame", None),
                          "step_sec": runner.get("step_sec", None),
                          "runner_load_path": runner_class_info[1]
                      }
                      if roi_manager is not None:
                          rt["roi_manager"] = roi_manager
  
                  except Exception as e:
                      log.error("Error on loading runner config")
                      log.exception(e)
  
              runner_arr = []
              # add all configs to array
              for key in runners_configs_temp:
                  runner_arr.append(runners_configs_temp[key])
  
              self.runners_configs_by_key = runners_configs_temp
              self.runners_configs = sorted(runner_arr, key=lambda x: x["priority"], reverse=False)
          else:
              log.error("Runners - not found! Check your configuration!")
  
      def _connect_with_runners(self):
          """
          runners_configs içindeki configleri kullanarak sırayla yüklenen runner sınıflarının instance'larını oluşturur
          oluşturulan bu nesneleri available_runners içerisine ekler.
          """
          for runner_config in self.runners_configs:
              runner = None
              try:
                  runner_key = runner_config["runner_key"]
                  runner_type = runner_config["type"]
                  runner_unique_key = runner_config["runner_unique_key"]
  
                  if runner_config["config"] is None:
                      log.warning("Config not found for %s", runner_key)
                      continue
  
                  # data klasörünün hangi runner klasörüne indirileceği problemi var, şimdilik projedeki runners klasörüne indirsin
                  download_runner_data(runner_type, runner_config["runner_load_path"], self.download_urls)
  
                  if self.implemented_runners[runner_key] is None:
                      log.error("Implemented runner not found for %s", runner_key)
                  else:
                      runner = self.implemented_runners[runner_key](runner_config["config"], runner_type)
                      # runner.setName(runner_config["name"])
                      self.available_runners[runner_unique_key] = runner
  
              except Exception as e:
                  log.exception(e)
                  if runner is not None and NDUUtility.has_method(runner, 'close'):
                      runner.close()
  
      def _set_video_source(self):
          """
          SOURCE_TYPE değerine göre video_source değişkenini oluşturur.
          """
          try:
              if self.SOURCE_TYPE is VideoSourceType.VIDEO_FILE:
                  self.SOURCE_CONFIG["test_data_path"] = path.dirname(
                      path.dirname(path.abspath(__file__))) + '/data/'.replace('/', path.sep)
                  self.video_source = FileVideoSource(self.SOURCE_CONFIG)
                  pass
              elif self.SOURCE_TYPE is VideoSourceType.PI_CAMERA:
                  self.video_source = PiCameraVideoSource(show_preview=True)
              elif self.SOURCE_TYPE is VideoSourceType.VIDEO_URL:
                  # TODO
                  pass
              elif self.SOURCE_TYPE is VideoSourceType.IP_CAMERA:
                  self.video_source = IPCameraVideoSource(self.SOURCE_CONFIG)
                  pass
              elif self.SOURCE_TYPE is VideoSourceType.CAMERA:
                  self.video_source = CameraVideoSource(self.SOURCE_CONFIG)
              elif self.SOURCE_TYPE is VideoSourceType.YOUTUBE:
                  self.video_source = YoutubeVideoSource(self.SOURCE_CONFIG)
              elif self.SOURCE_TYPE is VideoSourceType.IMAGE_FILE:
                  self.video_source = ImageVideoSource(self.SOURCE_CONFIG)
              else:
                  log.error("Video source type is not supported : %s ", self.SOURCE_TYPE.value)
                  # exit(101)
          except Exception as e:
              log.error("Error during setting up video source")
              log.error(e)
  
      def exit_signal(self):
          self._exit_requested = True
  
      # main thread tarafından çağırılabilir
      def check_for_preview(self):
          if self.__is_main_thread:
              raise Exception('Main thread does not need to call check_for_preview!')
          if self.__preview_show:
              return self._show_preview()
  
      def _show_preview(self):
          if not self.__preview_inited:
              self._winname = self.SOURCE_CONFIG.get("device", "ndu_gate_camera preview")
              pos = self.SOURCE_CONFIG.get("preview_show_win_pos", [40, 30])
              self._pause = False
              cv2.namedWindow(self._winname)  # Create a named window
              cv2.moveWindow(self._winname, pos[0], pos[1])
              self.__preview_inited = True
  
          preview = self.__last_preview_image
          if preview is not None:
              self.__last_preview_image = None
              cv2.imshow(self._winname, preview)
              # while True:
              #     k = cv2.waitKey(100) & 0xFF
              #     print(k)
              while True:
                  k = cv2.waitKey(1) & 0xFF
                  if k == ord("q"):
                      self._exit_requested = True
                      break
                  elif k == ord("s"):
                      self._skip = 30
                  elif k == 32:  # space key
                      self._pause = not self._pause
                  if not self._pause:
                      break
              if self.__preview_write:
                  self._write_frame(preview)
  
      def finish_preview(self):
          if self.__preview_show and self.__preview_write and self.__out is not None:
              self.__out.release()
              import ffmpeg  # pip3 install ffmpeg-python & brew install ffmpeg
              try:
                  # daha az sıkışmış, quicktime çalabiliyor
                  fn = self.__preview_write_file_name
                  ffmpeg.input(fn).output(fn + '_ffmpeg.mp4').run(capture_stdout=True, capture_stderr=True)
  
                  # # # süper sıkışmış ama quicktime çalamıyor. Benim denemelerimde yarım yamalak kaydedebildi!
                  # ffmpeg.input(fn) \
                  #     .output(fn + '2.mp4', vcodec='libx265', crf=24, t=5) \
                  #     .run(capture_stdout=True, capture_stderr=True)
                  # # os.remove(fn)
              except ffmpeg.Error as e:
                  print('stdout:', e.stdout.decode('utf8'))
                  print('stderr:', e.stderr.decode('utf8'))
                  raise e
              finally:
                  self.__out = None
  
      def run(self):
          while not self._exit_requested:
              print("camera refreshing...")
              log.debug("camera refreshing...")
              self._run()
  
      def _run(self):
          self._set_video_source()
          if self.video_source is None:
              log.error("video source is not set!")
              return
              # exit(102)
          start_total = None
          # TODO - çalıştırma sırasına göre sonuçlar bir sonraki runnera aktarılabilir
          # TODO - runner dependency ile kimin çıktısı kimn giridisi olacak şeklinde de olabilir
  
          try:
              for runner_unique_key in self.available_runners:
                  runner_conf = self.runners_configs_by_key[runner_unique_key]
                  step_sec = runner_conf.get("step_sec", None)
                  step_frame = runner_conf.get("step_frame", None)
                  if step_sec is not None or step_frame is not None:
                      self._steps[runner_unique_key] = [step_sec, 0, step_frame]
  
              device = self.SOURCE_CONFIG.get("device", None)
              i = -1
              last_thresh = last_gray = None
              has_motion_kernel = self.__motion_kernel is not None
              results_for_preview = None
              for _frame_index, frame in self.video_source.get_frames():
                  if self._exit_requested:
                      break
                  i += 1
                  if i > self.__camera_refresh_interval:
                      break
                  if self.__skip_frame > 1 and i % self.__skip_frame != 0:
                      continue
                  if has_motion_kernel:
                      gray = image_helper.resize(frame, width=500, interpolation=cv2.INTER_NEAREST)
                      gray = cv2.cvtColor(gray, cv2.COLOR_BGR2GRAY)
                      gray = cv2.GaussianBlur(gray, (self.__motion_kernel, self.__motion_kernel), 0)
                      if last_gray is not None:
                          frame_delta = cv2.absdiff(last_gray, gray)
                          last_thresh = cv2.threshold(frame_delta, 25, 255, cv2.THRESH_BINARY)[1]
                          no_zero = cv2.countNonZero(last_thresh)
                          if self.__preview_show_motion_kernel:
                              image_helper.put_text(last_thresh, str(no_zero), [50, 50])
                          # cv2.imshow("last_thresh", last_thresh)
                          # cv2.waitKey(1)
                          if no_zero <= 1:
                              continue
                      last_gray = gray
  
                  if i % 500 == 0:
                      log.debug("frame count %s ", i)
                      print("Source Device : {} - frame {}".format(device, i))
  
                  if i % self.__frame_send_interval == 0:
                      try:
                          camera_capture_base64 = image_helper.frame2base64(frame)
                          log.info("CAMERA_CAPTURE size : %s", len(camera_capture_base64))
                          print("CAMERA_CAPTURE size : {}".format(len(camera_capture_base64)))
                          if self.frame_sent:
                              if len(camera_capture_base64) < 16000:
                                  self.__result_handler.save_result([{"data": {"CAMERA_CAPTURE": camera_capture_base64}}],
                                                                    device=device, data_type='attribute')
                          self.__result_handler.save_result([{"data": {"FRAME_COUNT": i}}], device=device,
                                                            data_type='attribute')
                          self.frame_sent = True
                      except Exception as e:
                          log.exception(e)
                          log.error("can not create CAMERA_CAPTURE")
  
                  if self.__max_frame_dim is not None:
                      frame = image_helper.resize_if_larger(frame, self.__max_frame_dim)
                  if self.__min_frame_dim is not None:
                      frame = image_helper.resize_if_smaller(frame, self.__min_frame_dim)
  
                  if self.__preview_show:
                      results_for_preview = []
                  if self._skip <= 0:
                      if self.__preview_show:
                          start_total = time.time()
                      extra_data = {
                          constants.EXTRA_DATA_KEY_RESULTS: {}
                      }
                      extra_data_results = extra_data[constants.EXTRA_DATA_KEY_RESULTS]
                      empty_resulted_runners = []
                      # TODO - check runner settings before send the frame to runner
                      for runner_unique_key in self.available_runners:
                          try:
                              start_time = time.time()
                              step = self._steps.get(runner_unique_key, None)
                              if step is not None:
                                  step_sec = step[0]
                                  last_time = step[1]
                                  step_frame = step[2]
                                  if step_frame is not None and i % step_frame != 0:
                                      continue
                                  if step_sec is not None:
                                      diff = (start_time - last_time)
                                      if diff <= step_sec:
                                          continue
                                      else:
                                          step[1] = start_time
  
                              runner_conf = self.runners_configs_by_key[runner_unique_key]
                              if "roi_manager" in runner_conf:
                                  roi_manager = runner_conf.get("roi_manager", None)
                                  frame = roi_manager.forward(frame)
                                  results = self.available_runners[runner_unique_key].process_frame(frame,
                                                                                                    extra_data=extra_data)
                                  frame, results = roi_manager.reverse(frame, results)
                              else:
                                  results = self.available_runners[runner_unique_key].process_frame(frame,
                                                                                                    extra_data=extra_data)
  
                              if results is not None and len(results) > 0:
                                  extra_data_results[runner_unique_key] = results
                                  log.debug("result : %s", results)
                              else:
                                  empty_resulted_runners.append(runner_unique_key)
                              if self.__preview_show:
                                  elapsed = time.time() - start_time
                                  results_for_preview.append({"elapsed_time": '{}: {:.4f}sn fps:{:.0f}'.format(
                                      runner_conf["type"], elapsed, 1.0 / max(elapsed, 0.001))})
  
                          except Exception as e:
                              log.exception(e)
  
                      def check_results(runner_unique_key_, results_, device_, results_for_preview_):
                          self.__result_handler.save_result(results_, device=device_,
                                                            runner_name=self.runners_configs_by_key[runner_unique_key_][
                                                                "name"])
                          if self.__preview_show:
                              results_for_preview_.extend(results_)
  
                      delete_required_keys = []
                      for runner_unique_key, results in extra_data_results.items():
                          results = self._check_track(runner_unique_key, results)
                          if results is not None and len(results) > 0:
                              check_results(runner_unique_key, results, device, results_for_preview)
                          else:
                              delete_required_keys.append(runner_unique_key)
                      for runner_unique_key in delete_required_keys:
                          del extra_data_results[runner_unique_key]
                      for runner_unique_key in empty_resulted_runners:
                          results = self._check_track(runner_unique_key, None)
                          if results is not None and len(results) > 0:
                              extra_data_results[runner_unique_key] = results
                              check_results(runner_unique_key, results, device, results_for_preview)
  
                  self._age_track_history()
                  if self.__preview_show:
                      if self._skip > 0:
                          self._skip -= 1
                          preview = frame
                      else:
                          total_elapsed_time = time.time() - start_total
                          self.__total_elapsed_times.append(total_elapsed_time)
                          len_times = len(self.__total_elapsed_times)
                          total_elapsed_time = sum(self.__total_elapsed_times) / len_times
                          if len_times > 10000:
                              self.__total_elapsed_times = []
                          results_for_preview.append({"total_elapsed_time": '{:.0f}msec fps:{:.0f}'.format(
                              total_elapsed_time * 1000, (1.0 / max(total_elapsed_time, 0.001)))})
                          preview = self._get_preview(frame, results_for_preview)
                          if self.__preview_show_motion_kernel and last_thresh is not None:
                              th = image_helper.resize(last_thresh, preview.shape[1], preview.shape[0],
                                                       interpolation=cv2.INTER_NEAREST)
                              th = cv2.cvtColor(th, cv2.COLOR_GRAY2BGR)
                              preview = cv2.addWeighted(preview, 1, th, 1, 0)
  
                      self.__last_preview_image = preview
                      if self.__is_main_thread:
                          self._show_preview()
  
                      # # write frames
                      # fn = "C:/_koray/temp/sc19_{}.jpg".format(str(i))
                      # cv2.imwrite(fn, frame)
  
                  if self.__sleep > 0 and self._skip <= 0:
                      time.sleep(self.__sleep)
  
              if self.__preview_show and self.__is_main_thread:
                  self.finish_preview()
  
              # TODO - set camera_perspective
          except Exception as e:
              log.exception(e)
  
          log.info("Video source is finished")
  
      def _check_track(self, runner_unique_key, results):
          res = []
          if results is not None:
              for result in results:
                  if constants.RESULT_KEY_TRACK_ID in result:
                      if runner_unique_key not in self._track_history:
                          self._track_history[runner_unique_key] = {}
                      history = self._track_history[runner_unique_key]
                      track_id = result[constants.RESULT_KEY_TRACK_ID]
                      if track_id != -1:
                          if track_id not in history:
                              history[track_id] = {"result": result, "age": 0}
                          else:
                              result0 = history[track_id]["result"]
                              history[track_id]["age"] = 0
                              score = result.get(constants.RESULT_KEY_SCORE, 0.9)
                              score0 = result0.get(constants.RESULT_KEY_SCORE, 0.9)
                              if score < score0:
                                  for key in [constants.RESULT_KEY_DATA, constants.RESULT_KEY_CLASS_NAME,
                                              constants.RESULT_KEY_DEBUG]:
                                      if key in result0:
                                          result[key] = result0[key]
                                  result[constants.RESULT_KEY_SCORE] = score0
                              else:
                                  history[track_id]["result"] = result
                          if constants.RESULT_KEY_DATA in result:
                              result = result.copy()
                              del result[constants.RESULT_KEY_DATA]
                      elif constants.RESULT_KEY_DATA in result:
                          result = result.copy()
                          del result[constants.RESULT_KEY_DATA]
                      res.append(result)
                  else:
                      res.append(result)
  
          for i, (runner_unique_key, history) in enumerate(self._track_history.items()):
              del_lst = []
              for track_id, h in history.items():
                  if h["age"] > 30:
                      result = history[track_id]["result"]
                      res.append(result)
                      del_lst.append(track_id)
              for track_id in del_lst:
                  del history[track_id]
          return res
  
      def _age_track_history(self):
          for i, (runner_unique_key, history) in enumerate(self._track_history.items()):
              for track_id, h in history.items():
                  h["age"] += 1
  
      def _write_frame(self, frame):
          def get_free_file_name(fn):
              if os.path.exists(fn):
                  filename, file_extension = os.path.splitext(fn)
                  i = 1
                  while os.path.exists(fn):
                      # sil fn = f"{filename}{i}{file_extension}"
                      fn = "{}{}{}".format(filename, i, file_extension)
                      i += 1
              return fn
  
          try:
              self.__out.write(frame)
          except:
              shape = frame.shape[1], frame.shape[0]
              fourcc = cv2.VideoWriter_fourcc(*'MP4V')
              # fourcc = cv2.VideoWriter_fourcc(*'AVC1')
              # fourcc = cv2.VideoWriter_fourcc(*'XVID')
              self.__preview_write_file_name = get_free_file_name(self.__preview_write_file_name)
              self.__out = cv2.VideoWriter(self.__preview_write_file_name, fourcc, 24.0, shape)
              self.__out.write(frame)
  
      def _new_color(self):
          if self.__color_toggle is None:
              self.__color_toggle = [random.randint(50, 255), random.randint(50, 255), random.randint(50, 255)]
              return self.__color_toggle
          else:
              c = self.__color_toggle
              self.__color_toggle = None
              return [max(50, 255 - c[0]), max(50, 255 - c[1]), max(50, 255 - c[2])]
  
      def _get_preview(self, image, results):
          def get_color(obj, class_preview_key_, color=None):
              if color is None:
                  color = [255, 255, 255]
                  if class_preview_key_ is not None:
                      if not hasattr(obj, "__colors"):
                          setattr(obj, "__colors", {})
                      dic = getattr(obj, "__colors")
                      if class_preview_key_ in dic:
                          color = dic[class_preview_key_]
                      else:
                          color = dic[class_preview_key_] = self._new_color()
              return color
  
          def draw_rect(obj, img, c1_, c2_, class_preview_key_, color=None):
              if color is None:
                  color = get_color(obj, class_preview_key_, color)
              cv2.rectangle(img, (c1_[0], c1_[1]), (c2_[0], c2_[1]), color=[1, 1, 1], thickness=3)
              cv2.rectangle(img, (c1_[0], c1_[1]), (c2_[0], c2_[1]), color=color, thickness=2)
  
          show_debug_texts = self.__preview_show_debug_texts
          show_runner_info = self.__preview_show_runner_info
          show_score = self.__preview_show_score
          rect_filters = self.__preview_show_rect_filters
          show_rect_name = self.__preview_show_rect_name
          show_track_id = self.__preview_show_track_id
          show_track_pnt = self.__preview_show_track_pnt
  
          h, w, *_ = image.shape
          line_height = 20
          font_scale = 0.4
          current_line = [20, 0]
          current_line_bottom = [20, h]
          data_added = []
          if results is not None:
              for result in results:
                  debug_texts = []
                  text_type = ""
                  has_data = False
  
                  if show_runner_info:
                      total_elapsed_time = result.get("total_elapsed_time", None)
                      if total_elapsed_time is not None:
                          image_helper.put_text(image, total_elapsed_time, [w - 150, h - 30], color=[200, 200, 128],
                                                font_scale=0.4)
                          continue
  
                  values = {}
                  elapsed_time = values["elapsed_time"] = result.get("elapsed_time", None)
                  class_name = values[constants.RESULT_KEY_CLASS_NAME] = result.get(constants.RESULT_KEY_CLASS_NAME, None)
                  class_preview_key = values[constants.RESULT_KEY_PREVIEW_KEY] = result.get(
                      constants.RESULT_KEY_PREVIEW_KEY, class_name)
                  score = values[constants.RESULT_KEY_SCORE] = result.get(constants.RESULT_KEY_SCORE, None)
                  rect = values[constants.RESULT_KEY_RECT] = result.get(constants.RESULT_KEY_RECT, None)
                  rect_debug_text = values[constants.RESULT_KEY_RECT_DEBUG_TEXT] = result.get(
                      constants.RESULT_KEY_RECT_DEBUG_TEXT, None)
                  data = values[constants.RESULT_KEY_DATA] = result.get(constants.RESULT_KEY_DATA, None)
                  debug_text = values[constants.RESULT_KEY_DEBUG] = result.get(constants.RESULT_KEY_DEBUG, None)
  
                  if debug_text is not None:
                      debug_texts.append(debug_text)
  
                  def _get_score_txt(text_, score_):
                      res = "%{:.2f} ".format(score_ * 100) if isinstance(score_, float) else score_
                      if text_ is not None and len(text_) > 0:
                          return "{}- {} ".format(text_, res)
                      else:
                          return res
  
                  text = ""
                  if class_name is not None:
                      text = NDUUtility.debug_conv_turkish(class_name) + " "
                      if show_score and score is not None:
                          text = _get_score_txt(text, score)
                  elif show_score and score is not None:
                      text = _get_score_txt(None, score)
  
                  if data is not None:
                      add_txt = " data: " + str(data)
                      data_added.append(add_txt)
                      text = text + add_txt
                      has_data = True
                  if rect is not None:
                      c = np.array(rect[:4], dtype=np.int32)
                      c1, c2 = [c[1], c[0]], (c[3], c[2])
                      color_rect = get_color(self, class_preview_key, result.get(constants.RESULT_KEY_RECT_COLOR, None))
                      show_rect = True
                      if rect_filters is not None:
                          show_rect = False
                          for rect_filter in rect_filters:
                              if string_helper.wildcard(class_name, rect_filter):
                                  show_rect = True
                                  break
                      if show_rect:
                          draw_rect(self, image, c1, c2, class_preview_key, color_rect)
                      else:
                          text = ""
                      if show_rect and show_rect_name and len(text) > 0:
                          # c1[1] = c1[1] + line_height
                          c1[1] -= 10
                          if rect_debug_text is not None:
                              text += " - " + rect_debug_text
                          image_helper.put_text(image, text, c1)
                          text = ""
                      if show_rect and show_track_id:
                          track_id = result.get(constants.RESULT_KEY_TRACK_ID, None)
                          if track_id is not None:
                              # c1[1] -= line_height * 1.5
                              c1[1] -= 15
                              image_helper.put_text(image, "{}".format(track_id), c1)
                      if show_track_pnt:
                          track_id = result.get(constants.RESULT_KEY_TRACK_ID, None)
                          if track_id is not None:
                              if self._track_pnt_layer is None:
                                  h, w = image_helper.image_h_w(image)
                                  self._track_pnt_layer = np.zeros((h, w, 3), np.uint8)
                              self._track_pnt_layer = image_helper.change_brightness(self._track_pnt_layer, -1)
                              y1, x1, y2, x2 = tuple(rect)
                              pnt = (int(x1 + (x2 - x1) * 0.5), int(y2))
                              pnts_key = str(track_id)
                              pnts = self._track_pnts.get(pnts_key, [])
                              pnts.append(pnt)
                              self._track_pnts[pnts_key] = pnts
                              if len(pnts) > 1:
                                  pts = np.array(pnts, np.int32)
                                  # cv2.polylines(self._track_pnt_layer, [pts], False, color, 5)
                                  # cv2.polylines(image, [pts], False, color_rect, 5)
                                  thickness = 10
                                  for i in range(len(pnts) - 1, 0, -1):
                                      p0 = pnts[i]
                                      p1 = pnts[i - 1]
                                      cv2.line(image, p0, p1, color_rect, thickness)
                                      if i % 10 == 0:
                                          thickness -= 1
                                          if thickness < 1:
                                              # pnts = pnts[i: len(pnts)]
                                              break
  
                              # color = [255,255,255]
                              # cv2.circle(self._track_pnt_layer, pnt, 1, color, 8)
                              # image = cv2.addWeighted(image, 1, self._track_pnt_layer, 1,)
                              # ret, mask = cv2.threshold(self._track_pnt_layer, 1, 255, cv2.THRESH_BINARY)
                              # mask = cv2.bitwise_not(mask)
                              # mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
                              # image = cv2.bitwise_and(image, image, mask=mask)
                              # image = cv2.addWeighted(image, 1, self._track_pnt_layer, 0.1, 1)
                              # image += self._track_pnt_layer
                              # image = self._track_pnt_layer.copy()
  
                  if elapsed_time is not None:
                      text_type = elapsed_time + " " + text_type
                  if len(text) > 0:
                      text_type = text_type + text + " "
                  if show_runner_info and len(text_type) > 0:
                      current_line[1] += line_height
                      if show_runner_info:
                          if not has_data:
                              image_helper.put_text(image, text_type, current_line, font_scale=font_scale)
                          else:
                              image_helper.put_text(image, text_type, current_line, color=[0, 0, 255],
                                                    font_scale=font_scale)
                  if show_debug_texts and len(debug_texts) > 0:
                      for debug_text in debug_texts:
                          current_line_bottom[1] -= line_height * 2
                          image_helper.put_text(image, debug_text, current_line_bottom, color=[255, 250, 99],
                                                font_scale=font_scale * 2.75, thickness=2, back_color=[1, 1, 1])
  
          if show_runner_info:
              if len(data_added) > 0:
                  show_last_data_frame_time = time.time()
                  for data in data_added:
                      if data not in self.__preview_last_data:
                          self.__preview_last_data.append(data)
                          self.__preview_last_data_show_times.append(show_last_data_frame_time)
                      else:
                          self.__preview_last_data_show_times[
                              self.__preview_last_data.index(data)] = show_last_data_frame_time
              for i in reversed(range(len(self.__preview_last_data))):
                  elapsed = time.time() - self.__preview_last_data_show_times[i]
                  if elapsed > 5:
                      del self.__preview_last_data_show_times[i]
                      del self.__preview_last_data[i]
              for last_data in self.__preview_last_data:
                  current_line[1] += line_height
                  image_helper.put_text(image, last_data, current_line, color=[0, 255, 255])
  
          return image
  
      @staticmethod
      def __get_runner_configuration_key(runner_type, class_name, configuration):
          if configuration is None:
              configuration = runner_type + ".json"
          return runner_type + "_" + class_name + "_" + configuration
  
      @staticmethod
      def __get_runner_key(type, class_name):
          return type + "_" + class_name
  
  
  if __name__ == '__main__':
      NDUCameraService(constants.DEFAULT_NDU_GATE_CONF.replace('/', path.sep))
  