| | import glob |
| | import os |
| | import random |
| | import shutil |
| | import time |
| | from pathlib import Path |
| | from threading import Thread |
| |
|
| | import cv2 |
| | import math |
| | import numpy as np |
| | import torch |
| | from PIL import Image, ExifTags |
| | from torch.utils.data import Dataset |
| | from tqdm import tqdm |
| |
|
| | from ..utils import letterbox_for_img, clean_str |
| |
|
| | img_formats = ['.bmp', '.jpg', '.jpeg', '.png', '.tif', '.tiff', '.dng'] |
| | vid_formats = ['.mov', '.avi', '.mp4', '.mpg', '.mpeg', '.m4v', '.wmv', '.mkv'] |
| |
|
| | class LoadImages: |
| | def __init__(self, path, img_size=640): |
| | p = str(Path(path)) |
| | p = os.path.abspath(p) |
| | if '*' in p: |
| | files = sorted(glob.glob(p, recursive=True)) |
| | elif os.path.isdir(p): |
| | files = sorted(glob.glob(os.path.join(p, '*.*'))) |
| | elif os.path.isfile(p): |
| | files = [p] |
| | else: |
| | raise Exception('ERROR: %s does not exist' % p) |
| |
|
| | images = [x for x in files if os.path.splitext(x)[-1].lower() in img_formats] |
| | videos = [x for x in files if os.path.splitext(x)[-1].lower() in vid_formats] |
| | ni, nv = len(images), len(videos) |
| |
|
| | self.img_size = img_size |
| | self.files = images + videos |
| | self.nf = ni + nv |
| | self.video_flag = [False] * ni + [True] * nv |
| | self.mode = 'images' |
| | if any(videos): |
| | self.new_video(videos[0]) |
| | else: |
| | self.cap = None |
| | assert self.nf > 0, 'No images or videos found in %s. Supported formats are:\nimages: %s\nvideos: %s' % \ |
| | (p, img_formats, vid_formats) |
| |
|
| | def __iter__(self): |
| | self.count = 0 |
| | return self |
| |
|
| | def __next__(self): |
| | if self.count == self.nf: |
| | raise StopIteration |
| | path = self.files[self.count] |
| |
|
| | if self.video_flag[self.count]: |
| | |
| | self.mode = 'video' |
| | ret_val, img0 = self.cap.read() |
| | if not ret_val: |
| | self.count += 1 |
| | self.cap.release() |
| | if self.count == self.nf: |
| | raise StopIteration |
| | else: |
| | path = self.files[self.count] |
| | self.new_video(path) |
| | ret_val, img0 = self.cap.read() |
| | h0, w0 = img0.shape[:2] |
| |
|
| | self.frame += 1 |
| | print('\n video %g/%g (%g/%g) %s: ' % (self.count + 1, self.nf, self.frame, self.nframes, path), end='') |
| |
|
| | else: |
| | |
| | self.count += 1 |
| | img0 = cv2.imread(path, cv2.IMREAD_COLOR | cv2.IMREAD_IGNORE_ORIENTATION) |
| | |
| | assert img0 is not None, 'Image Not Found ' + path |
| | print('image %g/%g %s: \n' % (self.count, self.nf, path), end='') |
| | h0, w0 = img0.shape[:2] |
| |
|
| | |
| | img, ratio, pad = letterbox_for_img(img0, new_shape=self.img_size, auto=True) |
| | h, w = img.shape[:2] |
| | shapes = (h0, w0), ((h / h0, w / w0), pad) |
| |
|
| | |
| | |
| | img = np.ascontiguousarray(img) |
| |
|
| |
|
| | |
| | return path, img, img0, self.cap, shapes |
| |
|
| | def new_video(self, path): |
| | self.frame = 0 |
| | self.cap = cv2.VideoCapture(path) |
| | self.nframes = int(self.cap.get(cv2.CAP_PROP_FRAME_COUNT)) |
| |
|
| | def __len__(self): |
| | return self.nf |
| |
|
| |
|
| |
|
| | class LoadStreams: |
| | def __init__(self, sources='streams.txt', img_size=640, auto=True): |
| | self.mode = 'stream' |
| | self.img_size = img_size |
| |
|
| | if os.path.isfile(sources): |
| | with open(sources, 'r') as f: |
| | sources = [x.strip() for x in f.read().strip().splitlines() if len(x.strip())] |
| | else: |
| | sources = [sources] |
| |
|
| | n = len(sources) |
| | self.imgs, self.fps, self.frames, self.threads = [None] * n, [0] * n, [0] * n, [None] * n |
| | self.sources = [clean_str(x) for x in sources] |
| | self.auto = auto |
| | for i, s in enumerate(sources): |
| | |
| | print(f'{i + 1}/{n}: {s}... ', end='') |
| | s = eval(s) if s.isnumeric() else s |
| | cap = cv2.VideoCapture(s) |
| | assert cap.isOpened(), f'Failed to open {s}' |
| | w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) |
| | h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) |
| | self.fps[i] = max(cap.get(cv2.CAP_PROP_FPS) % 100, 0) or 30.0 |
| | self.frames[i] = max(int(cap.get(cv2.CAP_PROP_FRAME_COUNT)), 0) or float('inf') |
| |
|
| | _, self.imgs[i] = cap.read() |
| | self.threads[i] = Thread(target=self.update, args=([i, cap]), daemon=True) |
| | print(f" success ({self.frames[i]} frames {w}x{h} at {self.fps[i]:.2f} FPS)") |
| | self.threads[i].start() |
| | print('') |
| |
|
| | |
| |
|
| | s = np.stack([letterbox_for_img(x, self.img_size, auto=self.auto)[0].shape for x in self.imgs], 0) |
| | self.rect = np.unique(s, axis=0).shape[0] == 1 |
| | if not self.rect: |
| | print('WARNING: Different stream shapes detected. For optimal performance supply similarly-shaped streams.') |
| |
|
| | def update(self, i, cap): |
| | |
| | n, f, read = 0, self.frames[i], 1 |
| | while cap.isOpened() and n < f: |
| | n += 1 |
| | |
| | cap.grab() |
| | if n % read == 0: |
| | success, im = cap.retrieve() |
| | self.imgs[i] = im if success else self.imgs[i] * 0 |
| | time.sleep(1 / self.fps[i]) |
| |
|
| | def __iter__(self): |
| | self.count = -1 |
| | return self |
| |
|
| | def __next__(self): |
| | self.count += 1 |
| | if not all(x.is_alive() for x in self.threads) or cv2.waitKey(1) == ord('q'): |
| | cv2.destroyAllWindows() |
| | raise StopIteration |
| |
|
| | |
| | img0 = self.imgs.copy() |
| |
|
| | h0, w0 = img0[0].shape[:2] |
| | img, _, pad = letterbox_for_img(img0[0], self.img_size, auto=self.rect and self.auto) |
| |
|
| | |
| | h, w = img.shape[:2] |
| | shapes = (h0, w0), ((h / h0, w / w0), pad) |
| |
|
| | |
| | |
| | img = np.ascontiguousarray(img) |
| |
|
| | return self.sources, img, img0[0], None, shapes |
| |
|
| | def __len__(self): |
| | return len(self.sources) |