+
{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() || "";