|
import os |
|
from collections import defaultdict |
|
from dataclasses import dataclass, field |
|
from enum import Enum |
|
from typing import Dict, List, Optional, Tuple |
|
|
|
import pandas as pd |
|
|
|
IMAGES_EXT: Tuple = (".jpeg", ".jpg", ".png", ".webp", ".bmp", ".gif") |
|
VIDEO_EXT: Tuple = (".mp4", ".avi", ".mov", ".mkv", ".webm") |
|
|
|
|
|
@dataclass |
|
class PictureInfo: |
|
image_path: str |
|
age: Optional[str] |
|
gender: Optional[str] |
|
bbox: List[int] = field(default_factory=lambda: [-1, -1, -1, -1]) |
|
person_bbox: List[int] = field(default_factory=lambda: [-1, -1, -1, -1]) |
|
|
|
@property |
|
def has_person_bbox(self) -> bool: |
|
return any(coord != -1 for coord in self.person_bbox) |
|
|
|
@property |
|
def has_face_bbox(self) -> bool: |
|
return any(coord != -1 for coord in self.bbox) |
|
|
|
def has_gt(self, only_age: bool = False) -> bool: |
|
if only_age: |
|
return self.age != "-1" |
|
else: |
|
return not (self.age == "-1" and self.gender == "-1") |
|
|
|
def clear_person_bbox(self): |
|
self.person_bbox = [-1, -1, -1, -1] |
|
|
|
def clear_face_bbox(self): |
|
self.bbox = [-1, -1, -1, -1] |
|
|
|
|
|
class AnnotType(Enum): |
|
ORIGINAL = "original" |
|
PERSONS = "persons" |
|
NONE = "none" |
|
|
|
@classmethod |
|
def _missing_(cls, value): |
|
print(f"WARN: Unknown annotation type {value}.") |
|
return AnnotType.NONE |
|
|
|
|
|
def get_all_files(path: str, extensions: Tuple = IMAGES_EXT): |
|
files_all = [] |
|
for root, subFolders, files in os.walk(path): |
|
for name in files: |
|
|
|
if "directory" not in name and sum([ext.lower() in name.lower() for ext in extensions]) > 0: |
|
files_all.append(os.path.join(root, name)) |
|
return files_all |
|
|
|
|
|
class InputType(Enum): |
|
Image = 0 |
|
Video = 1 |
|
VideoStream = 2 |
|
|
|
|
|
def get_input_type(input_path: str) -> InputType: |
|
if os.path.isdir(input_path): |
|
print("Input is a folder, only images will be processed") |
|
return InputType.Image |
|
elif os.path.isfile(input_path): |
|
if input_path.endswith(VIDEO_EXT): |
|
return InputType.Video |
|
if input_path.endswith(IMAGES_EXT): |
|
return InputType.Image |
|
else: |
|
raise ValueError( |
|
f"Unknown or unsupported input file format {input_path}, \ |
|
supported video formats: {VIDEO_EXT}, \ |
|
supported image formats: {IMAGES_EXT}" |
|
) |
|
elif input_path.startswith("http") and not input_path.endswith(IMAGES_EXT): |
|
return InputType.VideoStream |
|
else: |
|
raise ValueError(f"Unknown input {input_path}") |
|
|
|
|
|
def read_csv_annotation_file(annotation_file: str, images_dir: str, ignore_without_gt=False): |
|
bboxes_per_image: Dict[str, List[PictureInfo]] = defaultdict(list) |
|
|
|
df = pd.read_csv(annotation_file, sep=",") |
|
|
|
annot_type = AnnotType("persons") if "person_x0" in df.columns else AnnotType("original") |
|
print(f"Reading {annotation_file} (type: {annot_type})...") |
|
|
|
missing_images = 0 |
|
for index, row in df.iterrows(): |
|
img_path = os.path.join(images_dir, row["img_name"]) |
|
if not os.path.exists(img_path): |
|
missing_images += 1 |
|
continue |
|
|
|
face_x1, face_y1, face_x2, face_y2 = row["face_x0"], row["face_y0"], row["face_x1"], row["face_y1"] |
|
age, gender = str(row["age"]), str(row["gender"]) |
|
|
|
if ignore_without_gt and (age == "-1" or gender == "-1"): |
|
continue |
|
|
|
if annot_type == AnnotType.PERSONS: |
|
p_x1, p_y1, p_x2, p_y2 = row["person_x0"], row["person_y0"], row["person_x1"], row["person_y1"] |
|
person_bbox = list(map(int, [p_x1, p_y1, p_x2, p_y2])) |
|
else: |
|
person_bbox = [-1, -1, -1, -1] |
|
|
|
bbox = list(map(int, [face_x1, face_y1, face_x2, face_y2])) |
|
pic_info = PictureInfo(img_path, age, gender, bbox, person_bbox) |
|
assert isinstance(pic_info.person_bbox, list) |
|
|
|
bboxes_per_image[img_path].append(pic_info) |
|
|
|
if missing_images > 0: |
|
print(f"WARNING: Missing images: {missing_images}/{len(df)}") |
|
return bboxes_per_image, annot_type |
|
|