733 lines
29 KiB
Python
733 lines
29 KiB
Python
"""Classes to be used by the different processing functions.
|
|
|
|
These classes are wrappers around their corresponding structs defined in
|
|
c_interface/include/model_res.h. They will hold results after the appropriate C processing
|
|
function is called.
|
|
"""
|
|
import ctypes
|
|
import string
|
|
from typing import List, Mapping, NewType, Optional, TypeVar, Union
|
|
|
|
import numpy as np
|
|
import numpy.typing as npt
|
|
|
|
BOXES_MAX_NUM = 80
|
|
CLASSIFIER_MAX_NUM = 1000
|
|
FR_FEATURE_MAP_SIZE = 256
|
|
KEYPOINT_POINTS = 11
|
|
LANDMARK_POINTS = 5
|
|
MAX_KEYPOINTS = 100
|
|
MAX_ONET_POINTS = 100
|
|
MAX_YOLO_FACE_LANDMARK_CNT = 8
|
|
OCR_MAX_NUM = 20
|
|
SEG_WIDTH = 80
|
|
SEG_HEIGHT = 60
|
|
|
|
class AgeGenderResult(ctypes.Structure):
|
|
"""Age and gender result.
|
|
|
|
Attributes:
|
|
age: An integer indicating the age of the detected person.
|
|
gender: An integer indicating the gender of detected person (0: female, 1: male).
|
|
"""
|
|
_fields_ = [("age", ctypes.c_uint32),
|
|
("ismale", ctypes.c_uint32)]
|
|
|
|
def __init__(self, age: int = 0, ismale: int = 0):
|
|
self.age = age
|
|
self.ismale = ismale
|
|
super().__init__()
|
|
|
|
def __repr__(self):
|
|
gender = "male" if self.ismale else "female"
|
|
return ("---------------------------- Age Gender Result ----------------------------\n"
|
|
f"Age: {self.age}\nGender: {gender}\n")
|
|
|
|
class BoundingBox(ctypes.Structure):
|
|
"""Box that outlines the detected object in the image.
|
|
|
|
Attributes:
|
|
x1: A float x coordinate of the top left corner.
|
|
y1: A float y coordinate of the top left corner.
|
|
x2: A float x coordinate of the bottom right corner.
|
|
y2: A float y coordinate of the bottom right corner.
|
|
score: A float indicating the probability score of the box.
|
|
class_num: An integer indicating the class with the highest score.
|
|
"""
|
|
_fields_ = [("x1", ctypes.c_float),
|
|
("y1", ctypes.c_float),
|
|
("x2", ctypes.c_float),
|
|
("y2", ctypes.c_float),
|
|
("score", ctypes.c_float),
|
|
("class_num", ctypes.c_int32)]
|
|
|
|
def __init__(self, x1: float = 0, y1: float = 0, x2: float = 0, y2: float = 0,
|
|
score: float = 0, class_num: int = 0):
|
|
self.x1 = x1
|
|
self.y1 = y1
|
|
self.x2 = x2
|
|
self.y2 = y2
|
|
self.score = score
|
|
self.class_num = class_num
|
|
super().__init__()
|
|
|
|
def __repr__(self):
|
|
box = f"Box (x1, y1, x2, y2): ({self.x1}, {self.y1}, {self.x2}, {self.y2})"
|
|
return ("------------------------------ Bounding Box -------------------------------\n"
|
|
f"{box}\nScore: {self.score}\nClass number: {self.class_num}\n")
|
|
|
|
def to_np(self, box_type: str = "xyxy", to_round: bool = False) -> npt.NDArray:
|
|
"""Returns a NumPy array of the 6 data values.
|
|
|
|
Args:
|
|
box_type: String indicating the format to return the box coordinates. "xyxy" or "xywh".
|
|
to_round: If True, box coordinate values will be rounded to the nearest integer.
|
|
If box_type is "xywh", rounding will be done first before subtraction.
|
|
|
|
Returns:
|
|
A NumPy array of length 6 with either of the following formats, depending on the
|
|
provided box_type:
|
|
"xyxy": [x1, y1, x2, y2, score, class_num]
|
|
"xywh": [x1, y1, w, h, score, class_num]
|
|
|
|
Raises:
|
|
ValueError: If box_type is not supported
|
|
"""
|
|
box = np.array([self.x1, self.y1, self.x2, self.y2, self.score, self.class_num])
|
|
if to_round:
|
|
box[:4] = np.round(box[:4])
|
|
if box_type == "xyxy":
|
|
return box
|
|
elif box_type == "xywh":
|
|
box[2] -= box[0]
|
|
box[3] -= box[1]
|
|
return box
|
|
else:
|
|
raise ValueError(f"to_np (BoundingBox): box_type must be one of "
|
|
f"['xyxy', 'xywh'], not {box_type}")
|
|
|
|
class BoundingBoxLandmark(ctypes.Structure):
|
|
"""Box that outlines the detected object in the image, including landmark data.
|
|
|
|
Attributes:
|
|
x1: A float x coordinate of the top left corner.
|
|
y1: A float y coordinate of the top left corner.
|
|
x2: A float x coordinate of the bottom right corner.
|
|
y2: A float y coordinate of the bottom right corner.
|
|
score: A float indicating the probability score of the box.
|
|
class_num: An integer indicating the class with the highest score.
|
|
lm: A list of floats indicating the landmark coordinates.
|
|
"""
|
|
_fields_ = [("x1", ctypes.c_float),
|
|
("y1", ctypes.c_float),
|
|
("x2", ctypes.c_float),
|
|
("y2", ctypes.c_float),
|
|
("score", ctypes.c_float),
|
|
("class_num", ctypes.c_int32),
|
|
("lm", ctypes.c_float * MAX_YOLO_FACE_LANDMARK_CNT)]
|
|
|
|
def __init__(self, x1: float = 0, y1: float = 0, x2: float = 0, y2: float = 0,
|
|
score: float = 0, class_num: int = 0, lm: Optional[float] = None):
|
|
self.x1 = x1
|
|
self.y1 = y1
|
|
self.x2 = x2
|
|
self.y2 = y2
|
|
self.score = score
|
|
self.class_num = class_num
|
|
if lm is not None:
|
|
self.lm = (ctypes.c_float * MAX_YOLO_FACE_LANDMARK_CNT)(*lm)
|
|
super().__init__()
|
|
|
|
def __repr__(self):
|
|
box = f"Box (x1, y1, x2, y2): ({self.x1}, {self.y1}, {self.x2}, {self.y2})"
|
|
lm_repr = "Landmarks:\n"
|
|
for (index, landmark) in enumerate(self.lm):
|
|
if index % 2: # y
|
|
lm_repr += f"{landmark})\n"
|
|
else: # x
|
|
lm_repr += f"LM[{index // 2}] (x, y): ({landmark}, "
|
|
return ("------------------------- Bounding Box + Landmark -------------------------\n"
|
|
f"{box}\nScore: {self.score}\nClass number: {self.class_num}\n{lm_repr}\n")
|
|
|
|
def box_to_np(self, box_type: str = "xyxy", to_round: bool = False) -> npt.NDArray:
|
|
"""Returns a NumPy array of the bounding box data.
|
|
|
|
Args:
|
|
box_type: String indicating the format to return the box coordinates. "xyxy" or "xywh".
|
|
to_round: If True, box coordinate values will be rounded to the nearest integer.
|
|
If box_type is "xywh", rounding will be done first before subtraction.
|
|
|
|
Returns:
|
|
A NumPy array of length 6 with either of the following formats, depending on the
|
|
provided box_type:
|
|
"xyxy": [x1, y1, x2, y2, score, class_num]
|
|
"xywh": [x1, y1, w, h, score, class_num]
|
|
|
|
Raises:
|
|
ValueError: If box_type is not supported
|
|
"""
|
|
box = np.array([self.x1, self.y1, self.x2, self.y2, self.score, self.class_num])
|
|
if to_round:
|
|
box[:4] = np.round(box[:4])
|
|
if box_type == "xyxy":
|
|
return box
|
|
elif box_type == "xywh":
|
|
box[2] -= box[0]
|
|
box[3] -= box[1]
|
|
return box
|
|
else:
|
|
raise ValueError(f"to_np (BoundingBox): box_type must be one of "
|
|
f"['xyxy', 'xywh'], not {box_type}")
|
|
|
|
def lm_to_np(self) -> npt.NDArray:
|
|
"""Returns a NumPy array of the landmark data."""
|
|
return np.array(self.lm)
|
|
|
|
class BoundingBoxLandmarkPlus(ctypes.Structure):
|
|
"""Box that outlines the detected object in the image, including landmarks and top 2 scores.
|
|
|
|
Attributes:
|
|
x1: A float x coordinate of the top left corner.
|
|
y1: A float y coordinate of the top left corner.
|
|
x2: A float x coordinate of the bottom right corner.
|
|
y2: A float y coordinate of the bottom right corner.
|
|
score: A float indicating the probability score of the box.
|
|
class_num: An integer indicating the class with the highest score.
|
|
score_next: A float indicating the probability score of the box.
|
|
class_num_next: An integer indicating the class with the highest score.
|
|
lm: A list of floats indicating the landmark coordinates.
|
|
"""
|
|
_fields_ = [("x1", ctypes.c_float),
|
|
("y1", ctypes.c_float),
|
|
("x2", ctypes.c_float),
|
|
("y2", ctypes.c_float),
|
|
("score", ctypes.c_float),
|
|
("class_num", ctypes.c_int32),
|
|
("score_next", ctypes.c_float),
|
|
("class_num_next", ctypes.c_int32),
|
|
("lm", ctypes.c_float * MAX_YOLO_FACE_LANDMARK_CNT)]
|
|
|
|
def __init__(self, x1: float = 0, y1: float = 0, x2: float = 0, y2: float = 0,
|
|
score: float = 0, class_num: int = 0, score_next: float = 0,
|
|
class_num_next: int = 0, lm: Optional[float] = None):
|
|
self.x1 = x1
|
|
self.y1 = y1
|
|
self.x2 = x2
|
|
self.y2 = y2
|
|
self.score = score
|
|
self.class_num = class_num
|
|
self.score_next = score_next
|
|
self.class_num_next = class_num_next
|
|
if lm is not None:
|
|
self.lm = (ctypes.c_float * MAX_YOLO_FACE_LANDMARK_CNT)(*lm)
|
|
super().__init__()
|
|
|
|
def __repr__(self):
|
|
box = f"Box (x1, y1, x2, y2): ({self.x1}, {self.y1}, {self.x2}, {self.y2})"
|
|
lm_repr = "Landmarks:\n"
|
|
for (index, landmark) in enumerate(self.lm):
|
|
if index % 2: # y
|
|
lm_repr += f"{landmark})\n"
|
|
else: # x
|
|
lm_repr += f"LM[{index // 2}] (x, y): ({landmark}, "
|
|
return ("------------------------- Bounding Box + Landmark -------------------------\n"
|
|
f"{box}\nScore: {self.score}\nClass number: {self.class_num}\n2nd score: "
|
|
f"{self.score_next}\n2nd class number: {self.class_num_next}\n{lm_repr}\n")
|
|
|
|
def box_to_np(self, box_type: str = "xyxy", to_round: bool = False,
|
|
second_best: bool = False) -> npt.NDArray:
|
|
"""Returns a NumPy array of the bounding box data.
|
|
|
|
Args:
|
|
box_type: String indicating the format to return the box coordinates. "xyxy" or "xywh".
|
|
to_round: If True, box coordinate values will be rounded to the nearest integer.
|
|
If box_type is "xywh", rounding will be done first before subtraction.
|
|
second_best: If True, box will also have the second highest score and class number.
|
|
|
|
Returns:
|
|
A NumPy array of length 6 with either of the following formats, depending on the
|
|
provided box_type:
|
|
"xyxy": [x1, y1, x2, y2, score, class_num]
|
|
"xywh": [x1, y1, w, h, score, class_num]
|
|
If second_best is set, each box will have two additional values appended to the end:
|
|
second highest score and corresponding class number.
|
|
|
|
Raises:
|
|
ValueError: If box_type is not supported
|
|
"""
|
|
if second_best:
|
|
box = np.array([self.x1, self.y1, self.x2, self.y2, self.score, self.class_num,
|
|
self.score_next, self.class_num_next])
|
|
else:
|
|
box = np.array([self.x1, self.y1, self.x2, self.y2, self.score, self.class_num])
|
|
if to_round:
|
|
box[:4] = np.round(box[:4])
|
|
if box_type == "xyxy":
|
|
return box
|
|
elif box_type == "xywh":
|
|
box[2] -= box[0]
|
|
box[3] -= box[1]
|
|
return box
|
|
else:
|
|
raise ValueError(f"to_np (BoundingBox): box_type must be one of "
|
|
f"['xyxy', 'xywh'], not {box_type}")
|
|
|
|
def lm_to_np(self) -> npt.NDArray:
|
|
"""Returns a NumPy array of the landmark data."""
|
|
return np.array(self.lm)
|
|
|
|
class ClassifierResult(ctypes.Structure):
|
|
"""Classifier result.
|
|
|
|
Attributes:
|
|
score: A float array holding scores for each class index
|
|
"""
|
|
_fields_ = [("score", ctypes.c_float * CLASSIFIER_MAX_NUM)]
|
|
|
|
def __init__(self, score: Optional[List[float]] = None):
|
|
if score is not None:
|
|
self.score = (ctypes.c_float * CLASSIFIER_MAX_NUM)(*score)
|
|
super().__init__()
|
|
|
|
def __repr__(self):
|
|
first_ten = "Displaying first 10 scores:\n"
|
|
for index, score in enumerate(self.score[:10]):
|
|
first_ten += f"Score[{index}]: {score}\n"
|
|
return ("---------------------------- Classifier Result ----------------------------\n"
|
|
f"{first_ten}\n")
|
|
|
|
class FaceOccludeResult(ctypes.Structure):
|
|
"""Face pose result.
|
|
|
|
Attributes:
|
|
yaw: A float indicating yaw value.
|
|
pitch: A float indicating pitch value.
|
|
roll: A float indicating roll value.
|
|
occ: A float indicating occlusion value.
|
|
"""
|
|
_fields_ = [("yaw", ctypes.c_float),
|
|
("pitch", ctypes.c_float),
|
|
("roll", ctypes.c_float),
|
|
("occ", ctypes.c_float)]
|
|
|
|
def __init__(self, yaw: float = 0, pitch: float = 0, roll: float = 0, occ: float = 0):
|
|
self.yaw = yaw
|
|
self.pitch = pitch
|
|
self.roll = roll
|
|
self.occ = occ
|
|
super().__init__()
|
|
|
|
def __repr__(self):
|
|
return ("--------------------------- Face Occlude Result ---------------------------\n"
|
|
f"Yaw: {self.yaw}\nPitch: {self.pitch}\nRoll: {self.roll}\nOcc: {self.occ}")
|
|
|
|
class FRResult(ctypes.Structure):
|
|
"""Face recognition result.
|
|
|
|
Attributes:
|
|
feature_map: A list of floats indicating the feature map.
|
|
"""
|
|
_fields_ = [("feature_map", ctypes.c_float * FR_FEATURE_MAP_SIZE)]
|
|
|
|
def __init__(self, feature_map: Optional[List[float]] = None):
|
|
if feature_map is not None:
|
|
self.feature_map = (ctypes.c_float * FR_FEATURE_MAP_SIZE)(*feature_map)
|
|
super().__init__()
|
|
|
|
def __repr__(self):
|
|
first_ten = "Displaying first 10 feature map values:\n"
|
|
for index, value in enumerate(self.feature_map[:10]):
|
|
first_ten += f"FR[{index}]: {value}\n"
|
|
return ("-------------------------------- FR Result --------------------------------\n"
|
|
f"{first_ten}\n")
|
|
|
|
class LandmarkPoint(ctypes.Structure):
|
|
"""Landmark point in integer format.
|
|
|
|
Attributes:
|
|
x: An integer indicating the x coordinate.
|
|
y: An integer indicating the y coordinate.
|
|
"""
|
|
_fields_ = [("x", ctypes.c_uint32),
|
|
("y", ctypes.c_uint32)]
|
|
|
|
def __init__(self, x: int = 0, y: int = 0):
|
|
self.x = x
|
|
self.y = y
|
|
super().__init__()
|
|
|
|
def __repr__(self):
|
|
return f"Landmark point (x, y): ({self.x}, {self.y})"
|
|
|
|
class LandmarkPointFloat(ctypes.Structure):
|
|
"""Landmark point in floating point format.
|
|
|
|
Attributes:
|
|
x: A float indicating the x coordinate.
|
|
y: A float indicating the y coordinate.
|
|
"""
|
|
_fields_ = [("x", ctypes.c_float),
|
|
("y", ctypes.c_float)]
|
|
|
|
def __init__(self, x: float = 0, y: float = 0):
|
|
self.x = x
|
|
self.y = y
|
|
super().__init__()
|
|
|
|
def __repr__(self):
|
|
return f"Landmark point (x, y): ({self.x}, {self.y})"
|
|
|
|
class LandmarkResult(ctypes.Structure):
|
|
"""Landmark result for variable number of landmarks.
|
|
|
|
Attributes:
|
|
marks: List of LandmarkPoint instances.
|
|
score: A float indicating the probability score.
|
|
blur: A float indicating the blur.
|
|
"""
|
|
_fields_ = [("marks", LandmarkPoint * MAX_ONET_POINTS),
|
|
("score", ctypes.c_float),
|
|
("blur", ctypes.c_float)]
|
|
|
|
def __init__(self, marks: Optional[List[LandmarkPoint]] = None,
|
|
score: float = 0, blur: float = 0):
|
|
if marks is not None:
|
|
self.marks = (LandmarkPoint * MAX_ONET_POINTS)(*marks)
|
|
self.score = score
|
|
self.blur = blur
|
|
super().__init__()
|
|
|
|
def __repr__(self):
|
|
landmarks = "Landmarks:\n"
|
|
for index, point in enumerate(self.marks):
|
|
landmarks += f"#{index} {point}\n"
|
|
return ("----------------------------- Landmark Result -----------------------------\n"
|
|
f"{landmarks}\nScore: {self.score}\nBlur: {self.blur}")
|
|
|
|
class LandmarkResult5p(ctypes.Structure):
|
|
"""Landmark result for 5 landmark points.
|
|
|
|
Attributes:
|
|
marks: List of LandmarkPoint instances.
|
|
score: A float indicating the probability score.
|
|
blur: A float indicating the blur.
|
|
class_num: An integer indicating the class number with the highest score.
|
|
"""
|
|
_fields_ = [("marks", LandmarkPoint * LANDMARK_POINTS),
|
|
("score", ctypes.c_float),
|
|
("blur", ctypes.c_float),
|
|
("class_num", ctypes.c_int32)]
|
|
|
|
def __init__(self, marks: Optional[List[LandmarkPoint]] = None,
|
|
score: float = 0, blur: float = 0, class_num: int = 0):
|
|
if marks is not None:
|
|
self.marks = (LandmarkPoint * LANDMARK_POINTS)(*marks)
|
|
self.score = score
|
|
self.blur = blur
|
|
self.class_num = class_num
|
|
super().__init__()
|
|
|
|
def __repr__(self):
|
|
landmarks = "Landmarks:\n"
|
|
for index, point in enumerate(self.marks):
|
|
landmarks += f"#{index} {point}\n"
|
|
return ("----------------------------- Landmark Result -----------------------------\n"
|
|
f"{landmarks}\nScore: {self.score}\nBlur: {self.blur}\nClass number: "
|
|
f"{self.class_num}")
|
|
|
|
class Keypoint(ctypes.Structure):
|
|
"""Keypoint.
|
|
|
|
Attributes:
|
|
x: A float indicating the x coordinate.
|
|
y: A float indicating the y coordinate.
|
|
"""
|
|
_fields_ = [("x", ctypes.c_float),
|
|
("y", ctypes.c_float)]
|
|
|
|
def __init__(self, x: float = 0, y: float = 0):
|
|
self.x = x
|
|
self.y = y
|
|
super().__init__()
|
|
|
|
def __repr__(self):
|
|
return f"Keypoint point (x, y): ({self.x}, {self.y})"
|
|
|
|
class KeyPointResult(ctypes.Structure):
|
|
"""Keypoint result.
|
|
|
|
Attributes:
|
|
point_count: An integer indicating the number of points found.
|
|
points: List of Keypoint instances.
|
|
scores: A list of floats indicating the probability scores for each Keypoint.
|
|
"""
|
|
_fields_ = [("point_count", ctypes.c_uint32),
|
|
("points", Keypoint * MAX_KEYPOINTS),
|
|
("scores", ctypes.c_float * MAX_KEYPOINTS)]
|
|
|
|
def __init__(self, point_count: int = 0, points: Optional[List[Keypoint]] = None,
|
|
scores: Optional[List[float]] = None):
|
|
self.point_count = point_count
|
|
if points is not None:
|
|
self.points = (Keypoint * MAX_KEYPOINTS)(*points)
|
|
if scores is not None:
|
|
self.scores = (ctypes.c_float * MAX_KEYPOINTS)(*scores)
|
|
super().__init__()
|
|
|
|
def __repr__(self):
|
|
keypoint = "Keypoints:\n"
|
|
for index, (point, score) in enumerate(zip(self.points[:self.point_count],
|
|
self.scores[:self.point_count])):
|
|
keypoint += f"#{index} {point}, Score: {score}\n"
|
|
return ("----------------------------- Keypoint Result -----------------------------\n"
|
|
f"{keypoint}")
|
|
|
|
class OnetPlusResult(ctypes.Structure):
|
|
"""ONET plus result.
|
|
|
|
Attributes:
|
|
marks: List of LandmarkPointFloat instances.
|
|
scores: A list of floats indicating the probability scores for each landmark.
|
|
"""
|
|
_fields_ = [("marks", LandmarkPointFloat * LANDMARK_POINTS),
|
|
("scores", ctypes.c_float * LANDMARK_POINTS)]
|
|
|
|
def __init__(self, marks: Optional[List[LandmarkPointFloat]] = None,
|
|
scores: Optional[List[float]] = None):
|
|
if marks is not None:
|
|
self.marks = (LandmarkPointFloat * LANDMARK_POINTS)(*marks)
|
|
if scores is not None:
|
|
self.scores = (ctypes.c_float * LANDMARK_POINTS)(*scores)
|
|
super().__init__()
|
|
|
|
def __repr__(self):
|
|
landmarks = "Landmarks:\n"
|
|
for index, point in enumerate(self.marks):
|
|
landmarks += (f"#{index} {point}; score: {self.scores[index]}\n")
|
|
return ("----------------------------- ONET Plus Result ----------------------------\n"
|
|
f"{landmarks}")
|
|
|
|
class OCRResult(ctypes.Structure):
|
|
"""Licenseplate OCR result.
|
|
|
|
Attributes:
|
|
char_count: An integer indicating the number of characters found.
|
|
char_boxes: Sequence of BoundingBox instances found in an image.
|
|
valid: An integer indicating if the plate is valid.
|
|
hyphen: An integer indicating the position the hyphen comes after.
|
|
|
|
"""
|
|
_fields_ = [("char_count", ctypes.c_uint32),
|
|
("char_boxes", BoundingBox * OCR_MAX_NUM),
|
|
("valid", ctypes.c_uint8),
|
|
("hyphen", ctypes.c_uint8)]
|
|
|
|
char_map = string.digits + string.ascii_uppercase
|
|
|
|
def __init__(self, char_count: int = 0, char_boxes: Optional[BoundingBox] = None,
|
|
valid: int = 0, hyphen: int = 0):
|
|
self.char_count = char_count
|
|
if char_boxes is not None:
|
|
self.char_boxes = (BoundingBox * OCR_MAX_NUM)(*char_boxes)
|
|
self.valid = valid
|
|
self.hyphen = hyphen
|
|
super().__init__()
|
|
|
|
def __repr__(self):
|
|
box_repr = ""
|
|
for index, box in enumerate(self.char_boxes[:self.char_count]):
|
|
box_repr += f"\nBox #{index}:\n{box}\n"
|
|
return ("------------------------------- OCR Result --------------------------------\n"
|
|
f"Plate: {self.get_plate()}\nBox_count: {self.char_count}\n{box_repr}\n")
|
|
|
|
def get_plate(self, input_map: Optional[Mapping[str, str]] = None):
|
|
"""Returns the plate value from the detected boxes.
|
|
|
|
Args:
|
|
input_map: Mapping from string representing the class number to a string
|
|
representing the actual character value.
|
|
|
|
Returns:
|
|
A string representing the values on the licenseplate.
|
|
"""
|
|
if not self.valid:
|
|
return ""
|
|
|
|
if input_map is None:
|
|
char_map = self.char_map
|
|
else:
|
|
char_map = list(input_map.values())
|
|
|
|
chars = [char_map[box.class_num] for box in self.char_boxes[:self.char_count]]
|
|
|
|
if self.hyphen:
|
|
# hyphen is the character after which it should be placed
|
|
chars = [*chars[:self.hyphen + 1], "-", *chars[self.hyphen + 1:]]
|
|
|
|
return "".join(chars)
|
|
|
|
class PersonHeadResult(ctypes.Structure):
|
|
"""Results of person bounding boxes mapped to head bounding boxes.
|
|
|
|
Attributes:
|
|
num_persons: An integer indicating number of total people.
|
|
num_heads: An integer indicating number of matching heads.
|
|
person_boxes: List of all person BoundingBox instances.
|
|
head_boxes: List of all matching head BoundingBox instances.
|
|
"""
|
|
_fields_ = [("num_persons", ctypes.c_uint32),
|
|
("num_heads", ctypes.c_uint32),
|
|
("person_boxes", BoundingBox * BOXES_MAX_NUM),
|
|
("head_boxes", BoundingBox * BOXES_MAX_NUM)]
|
|
|
|
def __init__(self, num_persons: int = 0, num_heads: int = 0,
|
|
person_boxes: Optional[List[BoundingBox]] = None,
|
|
head_boxes: Optional[List[BoundingBox]] = None):
|
|
self.num_persons = num_persons
|
|
self.num_heads = num_heads
|
|
if person_boxes is not None:
|
|
self.person_boxes = (BoundingBox * BOXES_MAX_NUM)(*person_boxes)
|
|
if head_boxes is not None:
|
|
self.head_boxes = (BoundingBox * BOXES_MAX_NUM)(*head_boxes)
|
|
super().__init__()
|
|
|
|
def __repr__(self):
|
|
box_repr = ""
|
|
for index, (person_box, head_box) in enumerate(zip(self.person_boxes[:self.num_persons],
|
|
self.head_boxes[:self.num_heads])):
|
|
box_repr += f"\nPerson #{index}:\n{person_box}\nCorresponding head:\n{head_box}\n"
|
|
return ("--------------------------- Person Head Result ----------------------------\n"
|
|
f"Person count: {self.num_persons}\nHead count: {self.num_heads}\n{box_repr}\n")
|
|
|
|
class SegResult(ctypes.Structure):
|
|
"""Semantic segmentation result.
|
|
|
|
Attributes:
|
|
seg_class_result: List of integers indicating the class number for each image pixel.
|
|
"""
|
|
_fields_ = [("seg_class_result", ctypes.c_uint32 * (SEG_WIDTH * SEG_HEIGHT))]
|
|
|
|
def __init__(self, seg_class_result: Optional[List[int]] = None):
|
|
if seg_class_result is not None:
|
|
self.seg_class_result = (ctypes.c_uint32 * (SEG_WIDTH * SEG_HEIGHT))(*seg_class_result)
|
|
super().__init__()
|
|
|
|
def __repr__(self):
|
|
first_ten = "Displaying first 10 pixels:\n"
|
|
for index, value in enumerate(self.seg_class_result[:10]):
|
|
first_ten += f"seg[{index}]: {value}\n"
|
|
return ("--------------------------- Segmentation Result ---------------------------\n"
|
|
f"{first_ten}")
|
|
|
|
def to_np(self) -> npt.NDArray[np.uint32]:
|
|
"""Returns a NumPy array of the class numbers for each pixel."""
|
|
classes = np.asarray(self.seg_class_result, dtype=np.uint32)
|
|
return np.reshape(classes, (SEG_HEIGHT, SEG_WIDTH))
|
|
|
|
class UpperbodyKeypointResult(ctypes.Structure):
|
|
"""Upperbody keypoint result.
|
|
|
|
Attributes:
|
|
marks: List of Keypoint instances.
|
|
"""
|
|
_fields_ = [("marks", Keypoint * KEYPOINT_POINTS)]
|
|
|
|
def __init__(self, marks: Optional[List[Keypoint]] = None):
|
|
if marks is not None:
|
|
self.marks = (Keypoint * KEYPOINT_POINTS)(*marks)
|
|
super().__init__()
|
|
|
|
def __repr__(self):
|
|
keypoint = "Keypoints:\n"
|
|
for index, point in enumerate(self.marks):
|
|
keypoint += f"#{index} {point}\n"
|
|
return ("----------------------------- Keypoint Result -----------------------------\n"
|
|
f"{keypoint}")
|
|
|
|
class YoloResult(ctypes.Structure):
|
|
"""Results of all bounding boxes found in an image.
|
|
|
|
Attributes:
|
|
class_count: An integer indicating total possible classes.
|
|
box_count: An integer indicating number of found boxes.
|
|
boxes: List of BoundingBox instances found in an image.
|
|
"""
|
|
_fields_ = [("class_count", ctypes.c_uint32),
|
|
("box_count", ctypes.c_uint32),
|
|
("boxes", BoundingBox * BOXES_MAX_NUM)]
|
|
|
|
def __init__(self, class_count: int = 0, box_count: int = 0,
|
|
boxes: Optional[List[BoundingBox]] = None):
|
|
self.class_count = class_count
|
|
self.box_count = box_count
|
|
if boxes is not None:
|
|
self.boxes = (BoundingBox * BOXES_MAX_NUM)(*boxes)
|
|
super().__init__()
|
|
|
|
def __repr__(self):
|
|
box_repr = ""
|
|
for index, box in enumerate(self.boxes[:self.box_count]):
|
|
box_repr += f"\nBox #{index}:\n{box}\n"
|
|
return ("------------------------------- Yolo Result -------------------------------\n"
|
|
f"Class count: {self.class_count}\nBox_count: {self.box_count}\n{box_repr}\n")
|
|
|
|
class YoloLandmarkResult(ctypes.Structure):
|
|
"""Results of all bounding boxes (including landmark data) found in an image.
|
|
|
|
Attributes:
|
|
class_count: An integer indicating total possible classes.
|
|
box_count: An integer indicating number of found boxes.
|
|
boxes: List of BoundingBoxLandmark instances found in an image.
|
|
"""
|
|
_fields_ = [("class_count", ctypes.c_uint32),
|
|
("box_count", ctypes.c_uint32),
|
|
("boxes", BoundingBoxLandmark * BOXES_MAX_NUM)]
|
|
|
|
def __init__(self, class_count: int = 0, box_count: int = 0,
|
|
boxes: Optional[List[BoundingBoxLandmark]] = None):
|
|
self.class_count = class_count
|
|
self.box_count = box_count
|
|
if boxes is not None:
|
|
self.boxes = (BoundingBoxLandmark * BOXES_MAX_NUM)(*boxes)
|
|
super().__init__()
|
|
|
|
def __repr__(self):
|
|
box_repr = ""
|
|
for index, box in enumerate(self.boxes[:self.box_count]):
|
|
box_repr += f"\nBox #{index}:\n{box}\n"
|
|
return ("------------------------------- Yolo Result -------------------------------\n"
|
|
f"Class count: {self.class_count}\nBox_count: {self.box_count}\n{box_repr}\n")
|
|
|
|
class YoloxLandmarkResult(ctypes.Structure):
|
|
"""Results of all bounding boxes (including landmark data) found in an image.
|
|
|
|
Attributes:
|
|
class_count: An integer indicating total possible classes.
|
|
box_count: An integer indicating number of found boxes.
|
|
boxes: List of BoundingBoxLandmarkPlus instances found in an image.
|
|
"""
|
|
_fields_ = [("class_count", ctypes.c_uint32),
|
|
("box_count", ctypes.c_uint32),
|
|
("boxes", BoundingBoxLandmarkPlus * BOXES_MAX_NUM)]
|
|
|
|
def __init__(self, class_count: int = 0, box_count: int = 0,
|
|
boxes: Optional[List[BoundingBoxLandmarkPlus]] = None):
|
|
self.class_count = class_count
|
|
self.box_count = box_count
|
|
if boxes is not None:
|
|
self.boxes = (BoundingBoxLandmarkPlus * BOXES_MAX_NUM)(*boxes)
|
|
super().__init__()
|
|
|
|
def __repr__(self):
|
|
box_repr = ""
|
|
for index, box in enumerate(self.boxes[:self.box_count]):
|
|
box_repr += f"\nBox #{index}:\n{box}\n"
|
|
return ("------------------------------- Yolo Result -------------------------------\n"
|
|
f"Class count: {self.class_count}\nBox_count: {self.box_count}\n{box_repr}\n")
|
|
|
|
Det = NewType("Det", List[List[Union[float, int]]])
|
|
ResultClass = TypeVar("ResultClass", AgeGenderResult, ClassifierResult, FaceOccludeResult,
|
|
FRResult, LandmarkResult, LandmarkResult5p, KeyPointResult, OnetPlusResult,
|
|
OCRResult, PersonHeadResult, SegResult, UpperbodyKeypointResult,
|
|
YoloResult, YoloLandmarkResult, YoloxLandmarkResult)
|