From edb832bc6a4f55d91793f8dbb0f3faccda8d5447 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Sun, 15 Feb 2026 18:04:12 +0900 Subject: [PATCH] =?UTF-8?q?fix:=EC=8A=AC=EB=9D=BC=EC=9D=B4=EB=93=9C=20?= =?UTF-8?q?=ED=8F=B0=ED=8A=B8=20=ED=81=AC=EA=B8=B0=202=EB=B0=B0=20?= =?UTF-8?q?=ED=99=95=EB=8C=80=20+=20=EC=A2=8C=ED=91=9C=20=EC=A0=95?= =?UTF-8?q?=ED=99=95=EB=8F=84=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SlideAnnotationService: 아웃트로 메인텍스트 36→72, 서브텍스트 24→48 - ScreenAnalysisService: 그리드 오버레이 레터박스 제거 (전체 채움) → Gemini 좌표가 이미지 비율과 직접 매핑되어 스포트라이트 위치 정확도 향상 Co-Authored-By: Claude Opus 4.6 --- app/Services/Video/ScreenAnalysisService.php | 18 ++----- app/Services/Video/SlideAnnotationService.php | 54 +++++++++---------- 2 files changed, 31 insertions(+), 41 deletions(-) diff --git a/app/Services/Video/ScreenAnalysisService.php b/app/Services/Video/ScreenAnalysisService.php index db8d6f78..81ba3623 100644 --- a/app/Services/Video/ScreenAnalysisService.php +++ b/app/Services/Video/ScreenAnalysisService.php @@ -276,24 +276,16 @@ private function createGridOverlay(string $imagePath): ?string $srcW = imagesx($source); $srcH = imagesy($source); - // 1920x1080 캔버스에 리사이즈 (비율 유지, 여백은 검정) + // 1920x1080 캔버스에 리사이즈 (전체 채움, 레터박스 없음) + // 이미지를 캔버스 전체에 채워서 Gemini 좌표가 이미지 비율과 직접 매핑되도록 함 $canvas = imagecreatetruecolor(self::ANALYSIS_WIDTH, self::ANALYSIS_HEIGHT); - $black = imagecolorallocate($canvas, 0, 0, 0); - imagefill($canvas, 0, 0, $black); - $scale = min(self::ANALYSIS_WIDTH / $srcW, self::ANALYSIS_HEIGHT / $srcH); - $newW = (int) ($srcW * $scale); - $newH = (int) ($srcH * $scale); - $offsetX = (int) ((self::ANALYSIS_WIDTH - $newW) / 2); - $offsetY = (int) ((self::ANALYSIS_HEIGHT - $newH) / 2); - - imagecopyresampled($canvas, $source, $offsetX, $offsetY, 0, 0, $newW, $newH, $srcW, $srcH); + imagecopyresampled($canvas, $source, 0, 0, 0, 0, self::ANALYSIS_WIDTH, self::ANALYSIS_HEIGHT, $srcW, $srcH); imagedestroy($source); - Log::debug("ScreenAnalysis: 이미지 정규화", [ + Log::debug("ScreenAnalysis: 이미지 정규화 (전체 채움)", [ 'original' => "{$srcW}x{$srcH}", - 'normalized' => self::ANALYSIS_WIDTH . 'x' . self::ANALYSIS_HEIGHT, - 'content_area' => "{$offsetX},{$offsetY} {$newW}x{$newH}", + 'canvas' => self::ANALYSIS_WIDTH . 'x' . self::ANALYSIS_HEIGHT, ]); // 그리드 오버레이 (정규화된 캔버스 위에) diff --git a/app/Services/Video/SlideAnnotationService.php b/app/Services/Video/SlideAnnotationService.php index 7e51dd24..941300dc 100644 --- a/app/Services/Video/SlideAnnotationService.php +++ b/app/Services/Video/SlideAnnotationService.php @@ -8,8 +8,8 @@ class SlideAnnotationService { private const TARGET_WIDTH = 1920; private const TARGET_HEIGHT = 1080; - private const CAPTION_HEIGHT = 100; - private const MARKER_RADIUS = 20; + private const CAPTION_HEIGHT = 180; + private const MARKER_RADIUS = 35; private string $fontPath; @@ -166,25 +166,23 @@ private function drawCaptionBar(\GdImage $canvas, string $caption, int $stepNumb // 상단 구분선 (인디고) $accent = imagecolorallocate($canvas, 79, 70, 229); - imagefilledrectangle($canvas, 0, $barY, self::TARGET_WIDTH, $barY + 3, $accent); + imagefilledrectangle($canvas, 0, $barY, self::TARGET_WIDTH, $barY + 4, $accent); // 캡션 텍스트 $white = imagecolorallocate($canvas, 255, 255, 255); - $fontSize = 20; - $textY = $barY + 55; + $fontSize = 38; if (file_exists($this->fontPath)) { - // 텍스트 줄바꿈 처리 - $wrappedText = $this->wrapText($caption, 60); + $wrappedText = $this->wrapText($caption, 40); $lines = explode("\n", $wrappedText); - $lineHeight = 30; - $startY = $barY + 20 + $lineHeight; + $lineHeight = 52; + $startY = $barY + 30 + $lineHeight; if (count($lines) > 1) { - $fontSize = 17; - $lineHeight = 26; - $startY = $barY + 15 + $lineHeight; + $fontSize = 32; + $lineHeight = 46; + $startY = $barY + 22 + $lineHeight; } foreach ($lines as $li => $line) { @@ -194,7 +192,7 @@ private function drawCaptionBar(\GdImage $canvas, string $caption, int $stepNumb imagettftext($canvas, $fontSize, 0, $tx, $startY + ($li * $lineHeight), $white, $this->fontPath, $line); } } else { - imagestring($canvas, 5, 40, $textY, $caption, $white); + imagestring($canvas, 5, 40, $barY + 80, $caption, $white); } } @@ -380,23 +378,23 @@ public function createIntroSlide(string $title, string $outputPath): ?string if (file_exists($this->fontPath)) { // 타이틀 - $titleSize = 42; + $titleSize = 72; $bbox = imagettfbbox($titleSize, 0, $this->fontPath, $title); $tw = $bbox[2] - $bbox[0]; $tx = (int) ((self::TARGET_WIDTH - $tw) / 2); - imagettftext($canvas, $titleSize, 0, $tx, 460, $white, $this->fontPath, $title); + imagettftext($canvas, $titleSize, 0, $tx, 440, $white, $this->fontPath, $title); // 서브타이틀 $sub = 'SAM 사용법을 안내합니다.'; - $subSize = 24; + $subSize = 40; $bbox2 = imagettfbbox($subSize, 0, $this->fontPath, $sub); $tw2 = $bbox2[2] - $bbox2[0]; $tx2 = (int) ((self::TARGET_WIDTH - $tw2) / 2); - imagettftext($canvas, $subSize, 0, $tx2, 530, $lightWhite, $this->fontPath, $sub); + imagettftext($canvas, $subSize, 0, $tx2, 550, $lightWhite, $this->fontPath, $sub); // 구분선 $lineColor = imagecolorallocatealpha($canvas, 255, 255, 255, 80); - imagefilledrectangle($canvas, (self::TARGET_WIDTH / 2) - 60, 560, (self::TARGET_WIDTH / 2) + 60, 563, $lineColor); + imagefilledrectangle($canvas, (self::TARGET_WIDTH / 2) - 80, 590, (self::TARGET_WIDTH / 2) + 80, 594, $lineColor); } else { imagestring($canvas, 5, self::TARGET_WIDTH / 2 - 100, 450, $title, $white); } @@ -441,18 +439,18 @@ public function createOutroSlide(string $title, string $outputPath): ?string if (file_exists($this->fontPath)) { $mainText = '이상으로 안내를 마칩니다.'; - $mainSize = 36; + $mainSize = 72; $bbox = imagettfbbox($mainSize, 0, $this->fontPath, $mainText); $tw = $bbox[2] - $bbox[0]; $tx = (int) ((self::TARGET_WIDTH - $tw) / 2); - imagettftext($canvas, $mainSize, 0, $tx, 470, $white, $this->fontPath, $mainText); + imagettftext($canvas, $mainSize, 0, $tx, 440, $white, $this->fontPath, $mainText); $subText = '감사합니다.'; - $subSize = 24; + $subSize = 48; $bbox2 = imagettfbbox($subSize, 0, $this->fontPath, $subText); $tw2 = $bbox2[2] - $bbox2[0]; $tx2 = (int) ((self::TARGET_WIDTH - $tw2) / 2); - imagettftext($canvas, $subSize, 0, $tx2, 530, $lightWhite, $this->fontPath, $subText); + imagettftext($canvas, $subSize, 0, $tx2, 560, $lightWhite, $this->fontPath, $subText); } else { imagestring($canvas, 5, self::TARGET_WIDTH / 2 - 100, 470, 'Thank you', $white); } @@ -480,7 +478,7 @@ private function drawNumberMarker(\GdImage $canvas, int $number, int $cx, int $c imageellipse($canvas, $cx, $cy, $r * 2, $r * 2, $white); $num = (string) $number; - $fontSize = 14; + $fontSize = 26; if (file_exists($this->fontPath)) { $bbox = imagettfbbox($fontSize, 0, $this->fontPath, $num); @@ -502,21 +500,21 @@ private function drawStepProgressBadge(\GdImage $canvas, int $stepNumber, int $t $bx = 30; $by = 20; - $bw = 170; - $bh = 44; + $bw = 300; + $bh = 76; imagefilledrectangle($canvas, $bx, $by, $bx + $bw, $by + $bh, $badgeBg); $text = "STEP {$stepNumber}/{$totalSteps}"; if (file_exists($this->fontPath)) { - $fontSize = 18; + $fontSize = 34; $bbox = imagettfbbox($fontSize, 0, $this->fontPath, $text); $tw = $bbox[2] - $bbox[0]; $tx = $bx + (int) (($bw - $tw) / 2); - imagettftext($canvas, $fontSize, 0, $tx, $by + 32, $white, $this->fontPath, $text); + imagettftext($canvas, $fontSize, 0, $tx, $by + 54, $white, $this->fontPath, $text); } else { - imagestring($canvas, 5, $bx + 20, $by + 12, $text, $white); + imagestring($canvas, 5, $bx + 20, $by + 20, $text, $white); } }