Harsha1845 commited on
Commit
4b7f4e3
·
verified ·
1 Parent(s): 6625a55

Update src/services/geminiService.ts

Browse files
Files changed (1) hide show
  1. src/services/geminiService.ts +30 -69
src/services/geminiService.ts CHANGED
@@ -3,7 +3,8 @@ import { Product, ProductCategory } from "../types";
3
 
4
  // Helper to clean base64 string
5
  const cleanBase64 = (data: string) => {
6
- return data.replace(/^data:image\/(png|jpeg|jpg|webp);base64,/, '');
 
7
  };
8
 
9
  const getMimeType = (data: string) => {
@@ -55,7 +56,7 @@ const urlToBase64 = async (url: string): Promise<string> => {
55
  export const generateTryOn = async (userImageBase64: string, product: Product): Promise<string> => {
56
  const apiKey = process.env.API_KEY;
57
  if (!apiKey) {
58
- throw new Error("API Key is missing. Please set the API_KEY environment variable.");
59
  }
60
 
61
  const ai = new GoogleGenAI({ apiKey });
@@ -69,81 +70,28 @@ export const generateTryOn = async (userImageBase64: string, product: Product):
69
  throw new Error("Could not load product image. Please try another item.");
70
  }
71
 
72
- // --- PROMPT ENGINEERING FOR VIRTUAL TRY-ON ---
73
- // We use Gemini 2.5 Flash Image ('nano banana') capabilities.
74
- // The key is to instruct it to act as a segmentation and warping engine.
75
-
76
- const basePrompt = `Act as an advanced Virtual Try-On (VTON) AI model.
77
-
78
- TASK:
79
- Synthesize a photorealistic image of the USER (Image A) wearing the GARMENT (Image B).
80
-
81
- STRICT PROCESSING STEPS:
82
- 1. SEGMENTATION: Identify the user's body parts relevant to the garment (e.g., torso for shirts, legs for pants, face for glasses).
83
- 2. EXTRACTION: Extract the garment from Image B, completely ignoring any white background, hangers, or mannequins.
84
- 3. WARPING: Warp the extracted garment to match the user's body pose, rotation, and shape in Image A.
85
- - If the user is turning, the garment must turn.
86
- - If the user has arms crossed, the garment must fold realistically.
87
- 4. COMPOSITING: Blend the warped garment onto the user.
88
- - Apply lighting from the user's environment to the garment.
89
- - Cast shadows from the garment onto the user's skin/body to create depth.
90
 
91
- CONSTRAINTS:
92
- - PRESERVE FACE IDENTITY: Do not modify the user's facial features.
93
- - PRESERVE BACKGROUND: Do not change the background behind the user.
94
- - REALISM: The output must look like a photograph, not a photoshop cut-paste.
95
 
96
- CATEGORY SPECIFIC INSTRUCTIONS:
 
 
 
 
97
  `;
98
 
99
- let categoryInstruction = "";
100
- switch (product.category) {
101
- case ProductCategory.EYEWEAR:
102
- categoryInstruction = `Category: EYEWEAR.
103
- - Fit: Align perfectly with the eyes and nose bridge.
104
- - Scale: Ensure the glasses width matches the face width.
105
- - Reality: If the glasses are sunglasses, add reflections. If clear, show the eyes through the lenses.`;
106
- break;
107
- case ProductCategory.NECKLACE:
108
- categoryInstruction = `Category: JEWELRY.
109
- - Fit: Drape the necklace around the neck curve. Gravity is important.
110
- - Layering: It must sit ON TOP of skin or the shirt the user is already wearing (unless it's a shirt try-on, then on the new shirt).`;
111
- break;
112
- case ProductCategory.HEADWEAR:
113
- categoryInstruction = `Category: HAT.
114
- - Fit: Sit naturally on the crown of the head.
115
- - Occlusion: If the user has hair volume, the hat should compress it slightly or sit behind fringe.`;
116
- break;
117
- case ProductCategory.SHIRT:
118
- categoryInstruction = `Category: UPPER BODY GARMENT.
119
- - Action: Completely replace the user's current top.
120
- - Sleeves: Match the sleeve length of the target garment. If the user's arm pose is complex, warp the sleeves accordingly.
121
- - Tucking: Determine if a tuck is natural based on the user's pose.`;
122
- break;
123
- case ProductCategory.PANTS:
124
- categoryInstruction = `Category: LOWER BODY GARMENT.
125
- - Action: Replace the user's pants/skirt.
126
- - Waist: Align with the natural waistline.`;
127
- break;
128
- default:
129
- categoryInstruction = `Category: FASHION ITEM. Wear it naturally.`;
130
- break;
131
- }
132
-
133
- const fullPrompt = `${basePrompt}\n${categoryInstruction}`;
134
-
135
  const userMime = getMimeType(userImageBase64);
136
  const productMime = getMimeType(productBase64);
137
 
138
  try {
139
- // gemini-2.5-flash-image is the recommended model for high-fidelity image manipulation tasks
140
  const response = await ai.models.generateContent({
141
  model: 'gemini-2.5-flash-image',
142
  contents: {
143
  parts: [
144
- {
145
- text: fullPrompt
146
- },
147
  {
148
  inlineData: {
149
  mimeType: userMime,
@@ -160,19 +108,32 @@ export const generateTryOn = async (userImageBase64: string, product: Product):
160
  }
161
  });
162
 
163
- const parts = response.candidates?.[0]?.content?.parts;
 
 
 
 
 
 
 
164
  if (parts) {
165
  for (const part of parts) {
166
  if (part.inlineData && part.inlineData.data) {
167
  return `data:${part.inlineData.mimeType || 'image/png'};base64,${part.inlineData.data}`;
168
  }
 
 
 
 
 
169
  }
170
  }
171
 
172
- throw new Error("No image generated by the AI.");
173
 
174
- } catch (error) {
175
  console.error("Gemini API Error:", error);
176
- throw error;
 
177
  }
178
  };
 
3
 
4
  // Helper to clean base64 string
5
  const cleanBase64 = (data: string) => {
6
+ // Use regex with case-insensitive flag
7
+ return data.replace(/^data:image\/\w+;base64,/i, '');
8
  };
9
 
10
  const getMimeType = (data: string) => {
 
56
  export const generateTryOn = async (userImageBase64: string, product: Product): Promise<string> => {
57
  const apiKey = process.env.API_KEY;
58
  if (!apiKey) {
59
+ throw new Error("API Key is missing. Please check your settings.");
60
  }
61
 
62
  const ai = new GoogleGenAI({ apiKey });
 
70
  throw new Error("Could not load product image. Please try another item.");
71
  }
72
 
73
+ // Simplified prompt focused on the visual task to avoid safety filter triggers
74
+ // and improve adherence to the "editing" capability of the model.
75
+ const prompt = `Update the first image to show the person wearing the ${product.name} shown in the second image.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
 
77
+ Product Category: ${product.category}
 
 
 
78
 
79
+ Instructions:
80
+ - The item should fit the person naturally based on their pose.
81
+ - Maintain the original lighting, shadows, and background.
82
+ - Do not change the person's identity or facial features (other than adding eyewear if applicable).
83
+ - High photorealism.
84
  `;
85
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
  const userMime = getMimeType(userImageBase64);
87
  const productMime = getMimeType(productBase64);
88
 
89
  try {
 
90
  const response = await ai.models.generateContent({
91
  model: 'gemini-2.5-flash-image',
92
  contents: {
93
  parts: [
94
+ { text: prompt },
 
 
95
  {
96
  inlineData: {
97
  mimeType: userMime,
 
108
  }
109
  });
110
 
111
+ const candidate = response.candidates?.[0];
112
+
113
+ // Check for safety blocks or other finish reasons
114
+ if (candidate?.finishReason && candidate.finishReason !== 'STOP') {
115
+ throw new Error(`Generation blocked. Reason: ${candidate.finishReason}`);
116
+ }
117
+
118
+ const parts = candidate?.content?.parts;
119
  if (parts) {
120
  for (const part of parts) {
121
  if (part.inlineData && part.inlineData.data) {
122
  return `data:${part.inlineData.mimeType || 'image/png'};base64,${part.inlineData.data}`;
123
  }
124
+ // If we got text back instead of an image, it might be an explanation of refusal
125
+ if (part.text) {
126
+ console.warn("Model returned text:", part.text);
127
+ // Don't throw immediately, look for other parts, but if only text remains...
128
+ }
129
  }
130
  }
131
 
132
+ throw new Error("The AI did not generate an image. Please try a different photo.");
133
 
134
+ } catch (error: any) {
135
  console.error("Gemini API Error:", error);
136
+ // Propagate the specific error message to the UI
137
+ throw new Error(error.message || "Failed to connect to AI service");
138
  }
139
  };