class_docstr: This is the object detection class for images in the ImageAI library. It allows you to detect the 80 objects in the COCO dataset [ https://cocodataset.org/#home ] in any image. 
  
  This class provides support for RetinaNet, YOLOv3 and TinyYOLOv3 object detection networks . After instantiating this class, you can set its properties and make object detections using pretrained models.
  
  The following functions are required to be called before object detection can be made
  
  * setModelPath: Used to specify the filepath to the pretrained model.
  
  * At least of of the following and it must correspond to the model set in the setModelPath()
  [setModelTypeAsRetinaNet(), setModelTypeAsYOLOv3(), setModelTypeAsTinyYOLOv3()]
  
  * loadModel: [This must be called once only before performing object detection]
  Once the above functions have been called, you can call the detectObjectsFromImage() function of
  the object detection instance object at anytime to obtain observable objects in any image.
  
  * detectObjectsFromImage: Used to perform object detection on an image
class_docstr_tok: ["This","is","the","object","detection","class","for","images","in","the","ImageAI","library",".","It","allows","you","to","detect","the","80","objects","in","the","COCO","dataset","[","https",":","\/\/cocodataset.org\/","#","home","]","in","any","image",".","This","class","provides","support","for","RetinaNet",",","YOLOv3","and","TinyYOLOv3","object","detection","networks",".","After","instantiating","this","class",",","you","can","set","its","properties","and","make","object","detections","using","pretrained","models",".","The","following","functions","are","required","to","be","called","before","object","detection","can","be","made","*","setModelPath",":","Used","to","specify","the","filepath","to","the","pretrained","model",".","*","At","least","of","of","the","following","and","it","must","correspond","to","the","model","set","in","the","setModelPath","(",")","[","setModelTypeAsRetinaNet","(",")",",","setModelTypeAsYOLOv3","(",")",",","setModelTypeAsTinyYOLOv3","(",")","]","*","loadModel",":","[","This","must","be","called","once","only","before","performing","object","detection","]","Once","the","above","functions","have","been","called",",","you","can","call","the","detectObjectsFromImage","(",")","function","of","the","object","detection","instance","object","at","anytime","to","obtain","observable","objects","in","any","image",".","*","detectObjectsFromImage",":","Used","to","perform","object","detection","on","an","image"]
docstr: Detects objects in an image using the unique classes provided
  by COCO.
  
  :param input_image: path to an image file, cv2 image or PIL image
  :param output_image_path: path to save input image with predictions rendered
  :param output_type: type of output for rendered image. Acceptable values are 'file' and 'array` ( a cv2 image )
  :param extract_detected_objects: extract each object based on the output type
  :param minimum_percentage_probability: the minimum confidence a detected object must have
  :param display_percentage_probability: to diplay/not display the confidence on rendered image   
  :param display_object_name: to diplay/not display the object name on rendered image  
  :param display_box: to diplay/not display the object bounding box on rendered image 
  :param custom_objects: a dictionary of detectable objects set to boolean values
  
  :returns: A list of tuples containing the label of detected object and the
  confidence.
code: def detectObjectsFromImage(
      self,
      input_image: Union[str, np.ndarray, Image.Image],
      output_image_path: str = None,
      output_type: str = "file",
      extract_detected_objects: bool = False,
      minimum_percentage_probability: int = 50,
      display_percentage_probability: bool = True,
      display_object_name: bool = True,
      display_box: bool = True,
      custom_objects: List = None,
  ) -> Union[
      List[List[Tuple[str, float, Dict[str, int]]]],
      np.ndarray,
      List[np.ndarray],
      List[str],
  ]:
      """
      Detects objects in an image using the unique classes provided
      by COCO.
  
      :param input_image: path to an image file, cv2 image or PIL image
      :param output_image_path: path to save input image with predictions rendered
      :param output_type: type of output for rendered image. Acceptable values are 'file' and 'array` ( a cv2 image )
      :param extract_detected_objects: extract each object based on the output type
      :param minimum_percentage_probability: the minimum confidence a detected object must have
      :param display_percentage_probability: to diplay/not display the confidence on rendered image
      :param display_object_name: to diplay/not display the object name on rendered image
      :param display_box: to diplay/not display the object bounding box on rendered image
      :param custom_objects: a dictionary of detectable objects set to boolean values
  
      :returns: A list of tuples containing the label of detected object and the
      confidence.
      """
  
      self.__model.eval()
      if not self.__model_loaded:
          if self.__model_path:
              warnings.warn(
                  "Model path has changed but pretrained weights in the"
                  " new path is yet to be loaded.",
                  ResourceWarning,
              )
          else:
              raise RuntimeError(
                  "Model path isn't set, pretrained weights aren't used."
              )
      predictions = defaultdict(lambda: [])
  
      if (
          self.__model_type == "yolov3"
          or self.__model_type == "tiny-yolov3"
      ):
          (
              fnames,
              original_imgs,
              input_dims,
              imgs,
          ) = self.__load_image_yolo(input_image)
  
          with torch.no_grad():
              output = self.__model(imgs)
  
          output = get_predictions(
              pred=output.to(self.__device),
              num_classes=len(self.__classes),
              nms_confidence_level=self.__nms_score,
              objectness_confidence=self.__objectness_score,
              device=self.__device,
          )
  
          if output is None:
              if output_type == "array":
                  if extract_detected_objects:
                      return original_imgs[0], [], []
                  else:
                      return original_imgs[0], []
              else:
                  if extract_detected_objects:
                      return original_imgs[0], []
                  else:
                      return []
  
          # scale the output to match the dimension of the original image
          input_dims = torch.index_select(
              input_dims, 0, output[:, 0].long()
          )
          scaling_factor = torch.min(416 / input_dims, 1)[0].view(-1, 1)
          output[:, [1, 3]] -= (
              416 - (scaling_factor * input_dims[:, 0].view(-1, 1))
          ) / 2
          output[:, [2, 4]] -= (
              416 - (scaling_factor * input_dims[:, 1].view(-1, 1))
          ) / 2
          output[:, 1:5] /= scaling_factor
  
          # clip bounding box for those that extended outside the detected image.
          for idx in range(output.shape[0]):
              output[idx, [1, 3]] = torch.clamp(
                  output[idx, [1, 3]], 0.0, input_dims[idx, 0]
              )
              output[idx, [2, 4]] = torch.clamp(
                  output[idx, [2, 4]], 0.0, input_dims[idx, 1]
              )
  
          for pred in output:
              pred_label = self.__classes[int(pred[-1])]
              if custom_objects:
                  if (
                      pred_label.replace(" ", "_")
                      in custom_objects.keys()
                  ):
                      if not custom_objects[
                          pred_label.replace(" ", "_")
                      ]:
                          continue
                  else:
                      continue
              predictions[int(pred[0])].append(
                  (
                      pred_label,
                      float(pred[-2]),
                      {
                          k: v
                          for k, v in zip(
                              ["x1", "y1", "x2", "y2"],
                              map(int, pred[1:5]),
                          )
                      },
                  )
              )
      elif self.__model_type == "retinanet":
          (
              fnames,
              original_imgs,
              scaled_images,
          ) = self.__load_image_retinanet(input_image)
          with torch.no_grad():
              output = self.__model(scaled_images)
  
          if output is None:
              if output_type == "array":
                  if extract_detected_objects:
                      return original_imgs[0], [], []
                  else:
                      return original_imgs[0], []
              else:
                  if extract_detected_objects:
                      return original_imgs[0], []
                  else:
                      return []
  
          for idx, pred in enumerate(output):
              for id in range(pred["labels"].shape[0]):
                  if pred["scores"][id] >= self.__objectness_score:
                      pred_label = self.__classes[pred["labels"][id]]
  
                      if custom_objects:
                          if (
                              pred_label.replace(" ", "_")
                              in custom_objects.keys()
                          ):
                              if not custom_objects[
                                  pred_label.replace(" ", "_")
                              ]:
                                  continue
                          else:
                              continue
  
                      predictions[idx].append(
                          (
                              pred_label,
                              pred["scores"][id].item(),
                              {
                                  k: v
                                  for k, v in zip(
                                      ["x1", "y1", "x2", "y2"],
                                      map(int, pred["boxes"][id]),
                                  )
                              },
                          )
                      )
  
      # Render detection on copy of input image
      original_input_image = None
      output_image_array = None
      extracted_objects = []
  
      if (
          self.__model_type == "yolov3"
          or self.__model_type == "tiny-yolov3"
      ):
          original_input_image = cv2.cvtColor(
              original_imgs[0], cv2.COLOR_RGB2BGR
          )
          if isinstance(output, torch.Tensor):
              for pred in output:
                  percentage_conf = round(float(pred[-2]) * 100, 2)
                  if percentage_conf < minimum_percentage_probability:
                      continue
  
                  displayed_label = ""
                  if display_object_name:
                      displayed_label = (
                          f"{self.__classes[int(pred[-1].item())]} : "
                      )
                  if display_percentage_probability:
                      displayed_label += f" {percentage_conf}%"
  
                  original_imgs[
                      int(pred[0].item())
                  ] = draw_bbox_and_label(
                      pred[1:5].int() if display_box else None,
                      displayed_label,
                      original_imgs[int(pred[0].item())],
                  )
  
              output_image_array = cv2.cvtColor(
                  original_imgs[0], cv2.COLOR_RGB2BGR
              )
  
      elif self.__model_type == "retinanet":
          original_input_image = tensor_to_ndarray(
              original_imgs[0].div(255.0)
          )
          original_input_image = cv2.cvtColor(
              original_input_image, cv2.COLOR_RGB2BGR
          )
          for idx, pred in predictions.items():
              max_dim = max(list(original_imgs[idx].size()))
  
              for label, score, bbox in pred:
                  percentage_conf = round(score * 100, 2)
                  if percentage_conf < minimum_percentage_probability:
                      continue
  
                  displayed_label = ""
                  if display_object_name:
                      displayed_label = f"{label} :"
                  if display_percentage_probability:
                      displayed_label += f" {percentage_conf}%"
  
                  original_imgs[idx] = draw_bounding_boxes_and_labels(
                      image=original_imgs[idx],
                      boxes=torch.Tensor(
                          [
                              [
                                  bbox["x1"],
                                  bbox["y1"],
                                  bbox["x2"],
                                  bbox["y2"],
                              ]
                          ]
                      ),
                      draw_boxes=display_box,
                      labels=[displayed_label],
                      label_color=(0, 0, 255),
                      box_color=(0, 255, 0),
                      width=1,
                      fill=False,
                      font_size=int(max_dim / 30),
                  )
  
          output_image_array = tensor_to_ndarray(
              original_imgs[0].div(255.0)
          )
          output_image_array = cv2.cvtColor(
              output_image_array, cv2.COLOR_RGB2BGR
          )
  
      # Format predictions for function reponse
      predictions_batch = list(predictions.values())
      predictions_list = (
          predictions_batch[0] if len(predictions_batch) > 0 else []
      )
      min_probability = minimum_percentage_probability / 100
  
      if output_type == "file":
          if output_image_path:
              cv2.imwrite(output_image_path, output_image_array)
  
              if extract_detected_objects:
                  extraction_dir = (
                      ".".join(output_image_path.split(".")[:-1])
                      + "-extracted"
                  )
                  os.mkdir(extraction_dir)
                  count = 0
                  for obj_prediction in predictions_list:
                      if obj_prediction[1] >= min_probability:
                          count += 1
                          extracted_path = os.path.join(
                              extraction_dir,
                              ".".join(
                                  os.path.basename(
                                      output_image_path
                                  ).split(".")[:-1]
                              )
                              + f"-{count}.jpg",
                          )
                          obj_bbox = obj_prediction[2]
                          cv2.imwrite(
                              extracted_path,
                              original_input_image[
                                  obj_bbox["y1"] : obj_bbox["y2"],
                                  obj_bbox["x1"] : obj_bbox["x2"],
                              ],
                          )
  
                          extracted_objects.append(extracted_path)
  
      elif output_type == "array":
          if extract_detected_objects:
              for obj_prediction in predictions_list:
                  if obj_prediction[1] >= min_probability:
                      obj_bbox = obj_prediction[2]
  
                      extracted_objects.append(
                          original_input_image[
                              obj_bbox["y1"] : obj_bbox["y2"],
                              obj_bbox["x1"] : obj_bbox["x2"],
                          ]
                      )
      else:
          raise ValueError(
              f"Invalid output_type '{output_type}'. Supported values are 'file' and 'array' "
          )
  
      predictions_list = [
          {
              "name": prediction[0],
              "percentage_probability": round(prediction[1] * 100, 2),
              "box_points": [
                  prediction[2]["x1"],
                  prediction[2]["y1"],
                  prediction[2]["x2"],
                  prediction[2]["y2"],
              ],
          }
          for prediction in predictions_list
          if prediction[1] >= min_probability
      ]
  
      if output_type == "array":
          if extract_detected_objects:
              return (
                  output_image_array,
                  predictions_list,
                  extracted_objects,
              )
          else:
              return output_image_array, predictions_list
      else:
          if extract_detected_objects:
              return predictions_list, extracted_objects
          else:
              return predictions_list
  
imports: import os, warnings
  from tkinter import Image
  from collections import defaultdict
  from typing import List, Tuple, Dict, Union
  from PIL import Image
  import torchvision
  import numpy
  from enum import Enum
  import torch
  import cv2
  from typing import Union, List
  from ..yolov3.yolov3 import YoloV3
  from ..yolov3.tiny_yolov3 import YoloV3Tiny
  from ..yolov3.utils import draw_bbox_and_label, get_predictions, prepare_image
  from ..retinanet.utils import read_image, draw_bounding_boxes_and_labels, tensor_to_ndarray
  import uuid
  from ..backend_check.model_extension import extension_check
label_desc: Use image node_id 13 for calling the ObjectDetection obj's underlying member method code with example usage: obj.detectObjectsFromImage(input_image, output_image_path, output_type, extract_detected_objects, minimum_percentage_probability, display_percentage_probability, display_object_name, display_box, custom_objects) and returns: output_image_array,  predictions_list,  extracted_objects, output_image_array,  predictions_list, predictions_list,  extracted_objects, predictions_list, original_imgs,  list,  list, original_imgs,  list, original_imgs,  list, list, original_imgs,  list,  list, original_imgs,  list, original_imgs,  list, list