extractCandidateBoxes($context, $sourceWidth, $sourceHeight); if ($boxes === []) { return null; } usort($boxes, static function (array $left, array $right): int { return $right['score'] <=> $left['score']; }); $best = $boxes[0]; return new SubjectDetectionResultData( cropBox: $best['box'], strategy: 'subject', reason: 'vision_subject_box', confidence: (float) $best['confidence'], meta: [ 'label' => $best['label'], 'score' => $best['score'], ], ); } /** * @return array */ private function extractCandidateBoxes(array $context, int $sourceWidth, int $sourceHeight): array { $boxes = []; $preferredLabels = collect((array) config('uploads.square_thumbnails.subject_detector.preferred_labels', [])) ->map(static fn ($label): string => mb_strtolower((string) $label)) ->filter() ->values() ->all(); $candidates = $context['subject_boxes'] ?? $context['vision_boxes'] ?? null; if ($candidates === null && ($context['artwork'] ?? null) instanceof Artwork) { $candidates = $this->boxesFromArtwork($context['artwork']); } foreach ((array) $candidates as $row) { if (! is_array($row)) { continue; } $box = $this->normalizeBox($row, $sourceWidth, $sourceHeight); if ($box === null) { continue; } $label = mb_strtolower((string) ($row['label'] ?? $row['tag'] ?? $row['name'] ?? 'subject')); $confidence = max(0.0, min(1.0, (float) ($row['confidence'] ?? $row['score'] ?? 0.75))); $areaWeight = ($box->width * $box->height) / max(1, $sourceWidth * $sourceHeight); $preferredBoost = in_array($label, $preferredLabels, true) ? 1.25 : 1.0; $boxes[] = [ 'box' => $box, 'label' => $label, 'confidence' => $confidence, 'score' => ($confidence * 0.8 + $areaWeight * 0.2) * $preferredBoost, ]; } return $boxes; } /** * @return array> */ private function boxesFromArtwork(Artwork $artwork): array { return collect((array) ($artwork->yolo_objects_json ?? [])) ->filter(static fn ($row): bool => is_array($row)) ->values() ->all(); } /** * @param array $row */ private function normalizeBox(array $row, int $sourceWidth, int $sourceHeight): ?CropBoxData { $payload = is_array($row['box'] ?? null) ? $row['box'] : $row; $left = $payload['x'] ?? $payload['left'] ?? $payload['x1'] ?? null; $top = $payload['y'] ?? $payload['top'] ?? $payload['y1'] ?? null; $width = $payload['width'] ?? null; $height = $payload['height'] ?? null; if ($width === null && isset($payload['x2'], $payload['x1'])) { $width = (float) $payload['x2'] - (float) $payload['x1']; } if ($height === null && isset($payload['y2'], $payload['y1'])) { $height = (float) $payload['y2'] - (float) $payload['y1']; } if (! is_numeric($left) || ! is_numeric($top) || ! is_numeric($width) || ! is_numeric($height)) { return null; } $left = (float) $left; $top = (float) $top; $width = (float) $width; $height = (float) $height; $normalized = max(abs($left), abs($top), abs($width), abs($height)) <= 1.0; if ($normalized) { $left *= $sourceWidth; $top *= $sourceHeight; $width *= $sourceWidth; $height *= $sourceHeight; } if ($width <= 1 || $height <= 1) { return null; } return (new CropBoxData( x: (int) floor($left), y: (int) floor($top), width: (int) round($width), height: (int) round($height), ))->clampToImage($sourceWidth, $sourceHeight); } }