From 4046bafaab570f951f260f92604b1e222985f6ed Mon Sep 17 00:00:00 2001 From: sebastjanartic <45803536-sebastjanartic@users.noreply.replit.com> Date: Sat, 28 Feb 2026 21:16:53 +0000 Subject: [PATCH] Improve image focus by centering on people and faces Update AI prompts for more accurate focal point detection and adjust image rendering in various components (MediumCard, SideCard, SingleImageCarousel) to utilize these focal points, enhancing visual composition. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 517dfa7b-26ac-463d-a6e1-a58c6df97188 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: 4e108b3d-fbcd-43ec-8d2f-0a6ed15b2c47 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/f209e72a-0939-48fa-84fc-57854de71967/517dfa7b-26ac-463d-a6e1-a58c6df97188/drGbo1a Replit-Helium-Checkpoint-Created: true --- client/src/components/photo-gallery.tsx | 2 +- client/src/pages/home.tsx | 43 +++++++++++------ server/focal-point.ts | 61 ++++++++----------------- 3 files changed, 49 insertions(+), 57 deletions(-) diff --git a/client/src/components/photo-gallery.tsx b/client/src/components/photo-gallery.tsx index 516d2c0..793fef8 100644 --- a/client/src/components/photo-gallery.tsx +++ b/client/src/components/photo-gallery.tsx @@ -110,7 +110,7 @@ function SingleImageCarousel({ onMouseEnter={() => setPaused(true)} onMouseLeave={() => setPaused(false)} > -
+
{images[index].fileName}): string { + if (!coverImage || !focalPoints) return "center 20%"; + const fp = focalPoints[coverImage]; + if (!fp) return "center 20%"; + return `${fp.x}% ${fp.y}%`; +} + +function MediumCard({ article, focalPoints }: { article: Article; focalPoints?: Record }) { const isVideo = article.category === "Video"; + const objPos = getObjectPosition(article.coverImage, focalPoints); return (
- {article.title} + {article.title}
{isVideo && (
@@ -171,14 +179,15 @@ function MediumCard({ article }: { article: Article }) { ); } -function SideCard({ article }: { article: Article }) { +function SideCard({ article, focalPoints }: { article: Article; focalPoints?: Record }) { const isVideo = article.category === "Video"; + const objPos = getObjectPosition(article.coverImage, focalPoints); return (
- {article.title} + {article.title}
{isVideo && (
@@ -289,7 +298,7 @@ function FeaturedCarousel({ articles, popular, galleryImages, focalPoints }: { a
{side.map((a) => ( - + ))}
@@ -360,26 +369,30 @@ export default function Home() { -
+
{row2Left.map((a) => ( - +
+ +
))} -
+
-
+
-
-
+
+
{row3Middle.map((a) => ( - +
+ +
))} -
+
@@ -387,7 +400,7 @@ export default function Home() { {row4Articles.length > 0 && (
{row4Articles.map((a) => ( - + ))}
@@ -396,7 +409,7 @@ export default function Home() { {row5Articles.length > 0 && (
{row5Articles.map((a) => ( - + ))}
diff --git a/server/focal-point.ts b/server/focal-point.ts index fbdb5d1..00839a1 100644 --- a/server/focal-point.ts +++ b/server/focal-point.ts @@ -9,6 +9,17 @@ const openai = new OpenAI({ const cache = new Map(); +const SYSTEM_PROMPT = `You are an image analysis tool that detects where faces and people are located in photographs. +Analyze the image and find the PRIMARY person or group of people. Report the CENTER of their face(s) as x,y percentages. +- x=0 means far left edge, x=100 means far right edge +- y=0 means very top edge, y=100 means very bottom edge +- For a person's face in the upper third, y should be around 15-35 +- For a person standing centered, x should be around 40-60 +- For a group photo, find the center of the group's faces +- Be PRECISE, do NOT default to 50,50. Actually look at where faces are. +- If there are multiple people, find the most prominent face or group center. +Return ONLY a JSON object like {"x":42,"y":28} with no other text.`; + export async function analyzeFocalPoint(imagePath: string): Promise<{ x: number; y: number }> { const originalPath = imagePath; if (cache.has(originalPath)) { @@ -16,8 +27,7 @@ export async function analyzeFocalPoint(imagePath: string): Promise<{ x: number; } try { - let imageData: string; - let mimeType = "image/webp"; + let imageContent: { type: "image_url"; image_url: { url: string; detail: "auto" | "low" | "high" } }; if (imagePath.startsWith("/uploads/")) { const localPath = path.join(process.cwd(), "client/public", imagePath); @@ -25,61 +35,30 @@ export async function analyzeFocalPoint(imagePath: string): Promise<{ x: number; throw new Error(`File not found: ${localPath}`); } const buffer = fs.readFileSync(localPath); - imageData = buffer.toString("base64"); + const imageData = buffer.toString("base64"); + let mimeType = "image/webp"; if (localPath.endsWith(".jpg") || localPath.endsWith(".jpeg")) mimeType = "image/jpeg"; else if (localPath.endsWith(".png")) mimeType = "image/png"; + imageContent = { type: "image_url", image_url: { url: `data:${mimeType};base64,${imageData}`, detail: "auto" } }; } else if (imagePath.startsWith("http")) { - const response = await openai.chat.completions.create({ - model: "gpt-4o-mini", - messages: [ - { - role: "system", - content: "You analyze images to find the main subject (person's face or group center). Return ONLY a JSON object with x and y as percentages (0-100). x=50 means horizontal center, y=20 means near top. Example: {\"x\":50,\"y\":30}" - }, - { - role: "user", - content: [ - { type: "text", text: "Where is the main subject (face/person) in this image? Return only JSON." }, - { type: "image_url", image_url: { url: imagePath, detail: "low" } } - ] - } - ], - max_tokens: 50, - }); - const text = response.choices[0]?.message?.content?.trim() || ""; - const match = text.match(/\{[^}]+\}/); - if (match) { - const parsed = JSON.parse(match[0]); - const point = { - x: Math.max(0, Math.min(100, Number(parsed.x) || 50)), - y: Math.max(0, Math.min(100, Number(parsed.y) || 30)), - }; - cache.set(originalPath, point); - return point; - } - throw new Error("Could not parse response"); + imageContent = { type: "image_url", image_url: { url: imagePath, detail: "auto" } }; } else { throw new Error(`Unsupported path: ${imagePath}`); } - const dataUrl = `data:${mimeType};base64,${imageData}`; - const response = await openai.chat.completions.create({ model: "gpt-4o-mini", messages: [ - { - role: "system", - content: "You analyze images to find the main subject (person's face or group center). Return ONLY a JSON object with x and y as percentages (0-100) indicating where the main subject/face is located. x=50 means horizontal center, y=20 means near the top. Example: {\"x\":45,\"y\":30}" - }, + { role: "system", content: SYSTEM_PROMPT }, { role: "user", content: [ - { type: "text", text: "Where is the main subject (face/person) in this image? Return only JSON with x,y percentages." }, - { type: "image_url", image_url: { url: dataUrl, detail: "low" } } + { type: "text", text: "Where exactly are the faces/people in this image? Be precise with coordinates. Return only JSON." }, + imageContent ] } ], - max_tokens: 50, + max_tokens: 60, }); const text = response.choices[0]?.message?.content?.trim() || "";