[image detection]

# image detection

## od_inference 파일의 43라인
batch_size=1,  # batch size

## od_inference 파일의 85라인
model.warmup(imgsz=(1 if pt else batch_size, 3, imgsz, imgsz))  # warmup

## common.py 파일에서 batch size 1에 대해 추론을 한번 실행하여 모델 warmup
def warmup(self, imgsz=(1, 3, 640, 640)):
        # Warmup model by running inference once
        warmup_types = self.pt, self.jit, self.onnx, self.engine, self.saved_model, self.pb
        if any(warmup_types) and self.device.type != 'cpu':
            im = torch.zeros(*imgsz, dtype=torch.half if self.fp16 else torch.float, device=self.device)  # input
            for _ in range(2 if self.jit else 1):  #
                self.forward(im)  # warmup

## od_inference 파일의 114라인
for batch_i, (im, targets, paths, shapes) in enumerate(pbar):

## od_inference 파일의 127라인
out, train_out = model(im, augment=augment, val=True)  # inference, loss outputs

[video detection]

# video detection

## video_detection.py 파일의 47라인에서 모델 로드 (DetectMultiBackend 함수 사용)
model = DetectMultiBackend(weights, device=device, dnn=dnn, data=data, fp16=half)

## common.py 파일의 308라인에서 DetectMultiBackend 함수를 실행시키며,
## 이때 TensorFlow SavedModel, edgetpu 모델을 불러오기 위해 사용되는 device는 cpu사용 
def __init__(self, weights='yolov5s.pt', device=torch.device('cpu'), dnn=False, data=None, fp16=False):
YOLOv5 \\U0001f680 2022-5-19 torch 1.10.2 CPU (추론 실행시 출력되는 내용)

## common.py 파일의 279, 369, 387라인에서 DetectMultiBackend 함수를 실행시키며,
## TensorFlow SavedModel, edgetpu 모델을 불러와 로드시킴
## 그리고 edgetpu의 경우 바로 tpu device 라이브러리를 할당하여 추론에 사용하도록 환경 설정
## TensroFlow SavedModel의 경우 추론시 사용하는 device 설정은 아래에서 진행
else:  # TensorFlow (SavedModel, GraphDef, Lite, Edge TPU)
    if saved_model:  # SavedModel
      LOGGER.info(f'Loading {w} for TensorFlow SavedModel inference...')
      import tensorflow as tf
      keras = False  # assume TF1 saved_model
      model = tf.keras.models.load_model(w) if keras else tf.saved_model.load(w)

    if edgetpu:  # Edge TPU <https://coral.ai/software/#edgetpu-runtime>
      LOGGER.info(f'Loading {w} for TensorFlow Lite Edge TPU inference...')
      delegate = {
      'Linux': 'libedgetpu.so.1',
      'Darwin': 'libedgetpu.1.dylib',
      'Windows': 'edgetpu.dll'}[platform.system()]
      interpreter = Interpreter(model_path=w, experimental_delegates=[load_delegate(delegate)])

## video_detection.py 파일의 55라인에서 bs 변수에 batch size 지정
bs = 1  # batch_size

## video_detection.py 파일의 60라인에서 model warmup 함수 실행을 위해 
## batch size, channel,image size 전달
model.warmup(imgsz=(1 if pt else bs, 3, *imgsz))  # warmup

## common.py 파일의 472라인에서 batch size 1에 대해 추론을 한번 실행하여 모델 warmup
## 장비에서 처음 추론 시간이 길기 때문에 coldstart를 줄이기 위해 사용되는 함수로 확인
## warmup 함수가 각 모델의 device에서 forward 함수를 실행시켜 추론 진행
def warmup(self, imgsz=(1, 3, 640, 640)):
        # Warmup model by running inference once
        warmup_types = self.pt, self.jit, self.onnx, self.engine, self.saved_model, self.pb
        if any(warmup_types) and self.device.type != 'cpu':
            im = torch.zeros(*imgsz, dtype=torch.half if self.fp16 else torch.float, device=self.device)  # input
            for _ in range(2 if self.jit else 1):  
                self.forward(im)  # warmup

## device는 TensroFlow SavedModel의 경우 추론을 위해 torch_utils.py 파일의 52, 59라인에서 
## select_device 함수를 실행시켜 cuda device 사용
device = select_device(device)

def select_device(device='', batch_size=0, newline=True):
    # device = 'cpu' or '0' or '0,1,2,3'
    s = f'YOLOv5 🚀 {git_describe() or file_update_date()} torch {torch.__version__} '  # string
    device = str(device).strip().lower().replace('cuda:', '')  # to string, 'cuda:0' to '0'
    cpu = device == 'cpu'
    if cpu:
        os.environ['CUDA_VISIBLE_DEVICES'] = '-1'  # force torch.cuda.is_available() = False
    elif device:  # non-cpu device requested
        os.environ['CUDA_VISIBLE_DEVICES'] = device  # set environment variable - must be before assert is_available()
        assert torch.cuda.is_available() and torch.cuda.device_count() >= len(device.replace(',', '')), \\
            f"Invalid CUDA '--device {device}' requested, use '--device cpu' or pass valid CUDA device(s)"

    cuda = not cpu and torch.cuda.is_available()
    if cuda:
        devices = device.split(',') if device else '0'  # range(torch.cuda.device_count())  # i.e. 0,1,6,7
        n = len(devices)  # device count
        if n > 1 and batch_size > 0:  # check batch_size is divisible by device_count
            assert batch_size % n == 0, f'batch-size {batch_size} not multiple of GPU count {n}'
        space = ' ' * (len(s) + 1)
        for i, d in enumerate(devices):
            p = torch.cuda.get_device_properties(i)
            s += f"{'' if i == 0 else space}CUDA:{d} ({p.name}, {p.total_memory / (1 << 20):.0f}MiB)\\n"  # bytes to MB
    else:
        s += 'CPU\\n'

    if not newline:
        s = s.rstrip()
    LOGGER.info(s.encode().decode('ascii', 'ignore') if platform.system() == 'Windows' else s)  # emoji-safe
    return torch.device('cuda:0' if cuda else 'cpu')

## common.py 파일의 410, 448, 454라인에서 forward 함수를 실행하여
## 입력 데이터의 형식을 모델에 맞게 맞추고 추론 실행
if self.saved_model:  # SavedModel
  y = (self.model(im, training=False) if self.keras else self.model(im)).numpy()
else:  # Lite or Edge TPU
  input, output = self.input_details[0], self.output_details[0]
  int8 = input['dtype'] == np.uint8  # is TFLite quantized uint8 model
  if int8:
		scale, zero_point = input['quantization']
	  im = (im / scale + zero_point).astype(np.uint8)  # de-scale
	  self.interpreter.set_tensor(input['index'], im)
	  self.interpreter.invoke() # TPU에서 추론
	  y = self.interpreter.get_tensor(output['index'])
  if int8:
    scale, zero_point = output['quantization']
    y = (y.astype(np.float32) - zero_point) * scale  # re-scale
 y[..., :4] *= [w, h, w, h]  # xywh normalized to pixels

## video_detection.py 파일의 45, 64라인에서 batch size 1로 한번 추론한 모델을 사용하여
## dataset 만큼 추론 진행하며 입력으로 dataset image shape, box shape, video를
## 입력으로으로 받아 추론 시작
device = select_device(device)
for path, im, im0s, vid_cap, s in dataset:
	im = torch.from_numpy(im).to(device)

## TensroFlow SavedModel의 경우 추론을 위해 torch_utils.py 파일의 52, 59라인에서 
## select_device 함수를 실행시켜 cuda device 사용
def select_device(device='', batch_size=0, newline=True):
    # device = 'cpu' or '0' or '0,1,2,3'
    s = f'YOLOv5 🚀 {git_describe() or file_update_date()} torch {torch.__version__} '  # string
    device = str(device).strip().lower().replace('cuda:', '')  # to string, 'cuda:0' to '0'
    cpu = device == 'cpu'
    if cpu:
        os.environ['CUDA_VISIBLE_DEVICES'] = '-1'  # force torch.cuda.is_available() = False
    elif device:  # non-cpu device requested
        os.environ['CUDA_VISIBLE_DEVICES'] = device  # set environment variable - must be before assert is_available()
        assert torch.cuda.is_available() and torch.cuda.device_count() >= len(device.replace(',', '')), \\
            f"Invalid CUDA '--device {device}' requested, use '--device cpu' or pass valid CUDA device(s)"

    cuda = not cpu and torch.cuda.is_available()
    if cuda:
        devices = device.split(',') if device else '0'  # range(torch.cuda.device_count())  # i.e. 0,1,6,7
        n = len(devices)  # device count
        if n > 1 and batch_size > 0:  # check batch_size is divisible by device_count
            assert batch_size % n == 0, f'batch-size {batch_size} not multiple of GPU count {n}'
        space = ' ' * (len(s) + 1)
        for i, d in enumerate(devices):
            p = torch.cuda.get_device_properties(i)
            s += f"{'' if i == 0 else space}CUDA:{d} ({p.name}, {p.total_memory / (1 << 20):.0f}MiB)\\n"  # bytes to MB
    else:
        s += 'CPU\\n'

    if not newline:
        s = s.rstrip()
    LOGGER.info(s.encode().decode('ascii', 'ignore') if platform.system() == 'Windows' else s)  # emoji-safe
    return torch.device('cuda:0' if cuda else 'cpu')

## video_detection.py 파일의 73라인부터 video frame 하나당 추론 진행
## 위에서 batch size 1로 한 번 추론된 모델에 데이터셋을 입력으로 넣고 추론 후
## bounding box 처리 및 결과 계산
pred = model(im, augment=augment, visualize=False)

## 추론에 들어가는 데이터셋은 video_detection.py 파일의 40-42라인에서
## source에 비디오 데이터셋을 입력으로 받음
source = str(source)
is_file = Path(source).suffix[1:] in (IMG_FORMATS + VID_FORMATS)
source = check_file(source)  # download

## 그리고 video_detection.py 파일의 54라인에서 LoadImages 함수를 실행하여 frame을 읽어옴
dataset = LoadImages(source, img_size=imgsz, stride=stride, auto=pt)

## datasets.py 파일의 178, 186, 219라인에서 LoadIamges 클래스를 실행하여
## 입력 받은 비디오 파일을 frame으로 읽어옴
class LoadImages:
    # YOLOv5 image/video dataloader, i.e. `python detect.py --source image.jpg/vid.mp4`
    def __init__(self, path, img_size=640, stride=32, auto=True):
			elif os.path.isfile(p):
            files = [p]  # files
			
			...

			if self.video_flag[self.count]:
            # Read video
            self.mode = 'video'
            ret_val, img0 = self.cap.read()
            while not ret_val:
                self.count += 1
                self.cap.release()
                if self.count == self.nf:  # last video
                    raise StopIteration
                else:
                    path = self.files[self.count]
                    self.new_video(path)
                    ret_val, img0 = self.cap.read()

            self.frame += 1
            s = f'video {self.count + 1}/{self.nf} ({self.frame}/{self.frames}) {path}: '

## 그렇게 받아온 dataset을 기준으로 video_detection.py 파일의 45, 60, 64, 73라인에서 추론 시작
## pt는 torch 기반 모델이므로, TensorFlow SavedModel과 edgetpu 모델의 경우 
## batch size에 따라 warmup 모델을 사용하여 추론
model.warmup(imgsz=(1 if pt else bs, 3, *imgsz))  # warmup
for path, im, im0s, vid_cap, s in dataset:
        im = torch.from_numpy(im).to(device)
        im = im.half() if model.fp16 else im.float()  # uint8 to fp16/32
        im /= 255  # 0 - 255 to 0.0 - 1.0
        if len(im.shape) == 3:
            im = im[None]  # expand for batch dim

        # Inference
        iftime_avg_start = time.time()
        pred = model(im, augment=augment, visualize=False)