"""Pure geometry helpers for deriving face candidate regions.""" from .config_loader import FaceSettings from .models import FaceROI, TrackedPerson def create_face_roi(person: TrackedPerson, frame_width: int, frame_height: int, config: FaceSettings) -> FaceROI | None: """Create a clipped upper-body ROI or return None when the track is ineligible.""" if config.require_confirmed_track and not person.is_confirmed: return None if config.process_only_inside_region and not person.inside_evaluation_region: return None bbox = person.bbox candidate_height = bbox.height * config.roi_height_ratio expand_x = bbox.width * config.roi_expand_ratio expand_y = candidate_height * config.roi_expand_ratio x1 = max(0, round(bbox.x1 - expand_x)) y1 = max(0, round(bbox.y1 - expand_y)) x2 = min(frame_width, round(bbox.x2 + expand_x)) y2 = min(frame_height, round(bbox.y1 + candidate_height + expand_y)) if x2 - x1 < config.min_roi_width or y2 - y1 < config.min_roi_height: return None return FaceROI(person.track_id, x1, y1, x2, y2, bbox, person.confidence)