ishworrsubedii commited on
Commit
71bc000
·
1 Parent(s): aded6c4

add: new endpoint product page-->batch nto,cto,mto generation

Browse files
Files changed (2) hide show
  1. app.py +76 -1
  2. src/components/product_page_nto_cto_gen.py +272 -0
app.py CHANGED
@@ -2,6 +2,7 @@ import mimetypes
2
  import os
3
  import tempfile
4
  import time
 
5
  from typing import List
6
 
7
  import requests
@@ -11,15 +12,27 @@ from starlette.responses import JSONResponse
11
  from supabase import create_client
12
 
13
  from src.components.each_necklace_video_gen import EachVideoCreator
 
14
  from src.components.vidgen import VideoCreator
15
  from src.utils.logs import logger
16
 
17
  supabase_url = os.getenv('SUPABASE_URL')
18
  supabase_key = os.getenv('SUPABASE_KEY')
19
  supabase = create_client(supabase_url, supabase_key)
 
 
 
 
 
 
 
 
 
 
 
20
  app = FastAPI()
21
 
22
- RESOURCES_DIR = "/app/resources/"
23
 
24
  os.makedirs(RESOURCES_DIR, exist_ok=True)
25
  TEMP_VIDEO_DIR = f"{RESOURCES_DIR}/temp_video"
@@ -337,6 +350,68 @@ async def get_information():
337
  return JSONResponse(content=json, status_code=200)
338
 
339
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
340
  if __name__ == "__main__":
341
  import uvicorn
342
 
 
2
  import os
3
  import tempfile
4
  import time
5
+ from dataclasses import dataclass
6
  from typing import List
7
 
8
  import requests
 
12
  from supabase import create_client
13
 
14
  from src.components.each_necklace_video_gen import EachVideoCreator
15
+ from src.components.product_page_nto_cto_gen import ProductPageImageGeneration
16
  from src.components.vidgen import VideoCreator
17
  from src.utils.logs import logger
18
 
19
  supabase_url = os.getenv('SUPABASE_URL')
20
  supabase_key = os.getenv('SUPABASE_KEY')
21
  supabase = create_client(supabase_url, supabase_key)
22
+
23
+ prod_page = ProductPageImageGeneration()
24
+
25
+
26
+ @dataclass
27
+ class MakeupColors:
28
+ lipstick: str
29
+ eyeliner: str
30
+ eyeshadow: str
31
+
32
+
33
  app = FastAPI()
34
 
35
+ RESOURCES_DIR = "resources"
36
 
37
  os.makedirs(RESOURCES_DIR, exist_ok=True)
38
  TEMP_VIDEO_DIR = f"{RESOURCES_DIR}/temp_video"
 
350
  return JSONResponse(content=json, status_code=200)
351
 
352
 
353
+ def ensure_json_serializable(data):
354
+ if isinstance(data, list):
355
+ return [ensure_json_serializable(item) for item in data]
356
+ elif isinstance(data, dict):
357
+ return {key: ensure_json_serializable(value) for key, value in data.items()}
358
+ elif hasattr(data, "__dict__"):
359
+ return ensure_json_serializable(data.__dict__)
360
+ else:
361
+ return data
362
+
363
+
364
+ @app.post("/product_page_image_generation")
365
+ async def product_page_image_generation(model_image: str, necklace_id: str, necklace_category: str, storename: str,
366
+ x_offset: str,
367
+ y_offset: str,
368
+ clothing_list: List[str],
369
+ makeup_colors: MakeupColors):
370
+ try:
371
+ model_name = model_image.split("/")[-1].split(".")[0]
372
+
373
+ model_image_path = download_image(model_image)
374
+
375
+ if model_image_path is None:
376
+ return JSONResponse(content={"status": "error", "message": "Failed to download model image"},
377
+ status_code=400)
378
+
379
+ response = prod_page.process_full_tryon_sequence(
380
+ model_image_path,
381
+ necklace_id,
382
+ {
383
+ "id": necklace_id,
384
+ "category": necklace_category,
385
+ "store_name": storename,
386
+ "offset_x": x_offset,
387
+ "offset_y": y_offset
388
+ },
389
+
390
+ clothing_list,
391
+ makeup_colors,
392
+ model_name=model_name
393
+ )
394
+
395
+ nto_results = response["nto_results"]
396
+ cto_results = response["cto_results"]
397
+ mto_results = response["mto_results"]
398
+
399
+ json_response = {
400
+ "status": "success",
401
+ "message": "Product page images generated successfully.",
402
+ "nto_results": ensure_json_serializable(nto_results),
403
+ "cto_results": ensure_json_serializable(cto_results),
404
+ "mto_results": ensure_json_serializable(mto_results)
405
+ }
406
+
407
+ return JSONResponse(content=json_response, status_code=200)
408
+
409
+
410
+
411
+ except Exception as e:
412
+ return JSONResponse(content={"status": "error", "message": str(e)}, status_code=500)
413
+
414
+
415
  if __name__ == "__main__":
416
  import uvicorn
417
 
src/components/product_page_nto_cto_gen.py ADDED
@@ -0,0 +1,272 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from dataclasses import dataclass
3
+ from typing import List, Dict, Optional
4
+
5
+ import requests
6
+ from supabase import create_client
7
+
8
+ from src.utils.logs import logger
9
+
10
+ supabase_url = os.getenv('SUPABASE_URL')
11
+ supabase_key = os.getenv('SUPABASE_KEY')
12
+ supabase = create_client(supabase_url, supabase_key)
13
+
14
+
15
+ @dataclass
16
+ class MakeupColors:
17
+ lipstick: str
18
+ eyeliner: str
19
+ eyeshadow: str
20
+
21
+
22
+ class ProductPageImageGeneration:
23
+ def __init__(self):
24
+ self.nto_endpoint = "https://techconspartners-cfgvigrclbw4urtwcjficydv0edz7x.hf.space/necklaceTryOnID"
25
+ self.cto_endpoint = "https://techconspartners-cfgvigrclbw4urtwcjficydv0edz7x.hf.space/clothingTryOnV2"
26
+ self.mto_endpoint = "https://techconspartners-cfgvigrclbw4urtwcjficydv0edz7x.hf.space/makeup_tryon/tryon"
27
+ self.destination_bucket = "ProductPageOutputs"
28
+
29
+ def generate_filename(self,
30
+ base_id: str,
31
+ model_name: str,
32
+ try_on_type: str,
33
+ item_type: Optional[str] = None,
34
+ makeup_colors: Optional[MakeupColors] = None) -> str:
35
+ """Generate a consistent filename for storage"""
36
+ parts = [base_id, model_name, try_on_type]
37
+
38
+ if item_type and not isinstance(item_type, MakeupColors):
39
+ logger.info(f"Item type: {item_type}")
40
+ parts.append(item_type.replace(' ', '_'))
41
+
42
+ if makeup_colors:
43
+ colors_str = f"lip_{makeup_colors.lipstick}_eye_{makeup_colors.eyeliner}_shadow_{makeup_colors.eyeshadow}"
44
+ parts.append(colors_str.replace(' ', '_'))
45
+
46
+ return f"{'-'.join(parts)}.png"
47
+
48
+ def check_file_exists(self, filename: str) -> bool:
49
+ """Check if file already exists in Supabase storage"""
50
+ try:
51
+ supabase.storage.from_(self.destination_bucket).get_public_url(filename)
52
+ return True
53
+ except:
54
+ return False
55
+
56
+ def upload_to_supabase(self, image_content: bytes, filename: str) -> str:
57
+ """Upload or replace file in Supabase storage"""
58
+ try:
59
+ # Delete existing file if it exists
60
+ if self.check_file_exists(filename):
61
+ try:
62
+ supabase.storage.from_(self.destination_bucket).remove([filename])
63
+ except Exception as e:
64
+ logger.error(f"Error deleting existing file {filename}: {str(e)}")
65
+
66
+ # Upload new file
67
+ result = supabase.storage.from_(self.destination_bucket).upload(
68
+ filename,
69
+ image_content,
70
+ {"content-type": "image/png"}
71
+ )
72
+
73
+ return supabase.storage.from_(self.destination_bucket).get_public_url(filename)
74
+ except Exception as e:
75
+ print(f"Error uploading {filename}: {str(e)}")
76
+ return ""
77
+
78
+ def process_necklace_tryon(self,
79
+ image_path: str,
80
+ necklace_id: str,
81
+ category: str,
82
+ store_name: str,
83
+ offset_x: float,
84
+ offset_y: float) -> dict:
85
+ """Process a necklace try-on request"""
86
+ try:
87
+ with open(image_path, 'rb') as img_file:
88
+ files = {
89
+ 'image': (os.path.basename(image_path), img_file, 'image/jpeg')
90
+ }
91
+ data = {
92
+ 'necklaceImageId': necklace_id,
93
+ 'necklaceCategory': category,
94
+ 'storename': store_name,
95
+ 'offset_x': str(offset_x),
96
+ 'offset_y': str(offset_y)
97
+ }
98
+ response = requests.post(self.nto_endpoint, files=files, data=data)
99
+ response.raise_for_status()
100
+ logger.info(f"Necklace Try on Completed for {necklace_id}")
101
+ return response.json()
102
+ except Exception as e:
103
+ print(f"Error in necklace try-on: {str(e)}")
104
+ return None
105
+
106
+ def process_clothing_tryon(self, image_path: str, clothing_type: str) -> dict:
107
+ """Process a clothing try-on request"""
108
+ try:
109
+ with open(image_path, 'rb') as img_file:
110
+ files = {
111
+ 'image': (os.path.basename(image_path), img_file, 'image/jpeg')
112
+ }
113
+ data = {
114
+ 'clothing_type': clothing_type
115
+ }
116
+ response = requests.post(self.cto_endpoint, files=files, data=data)
117
+ response.raise_for_status()
118
+ logger.info(f"Clothing Try on Completed for {clothing_type}")
119
+ return response.json()
120
+
121
+ except Exception as e:
122
+ logger.info(f"Error in clothing try-on: {str(e)}")
123
+ return None
124
+
125
+ def process_makeup_tryon(self,
126
+ image_path: str,
127
+ makeup_colors: MakeupColors) -> dict:
128
+ """Process a makeup try-on request"""
129
+ try:
130
+ with open(image_path, 'rb') as img_file:
131
+ files = {
132
+ 'image': (os.path.basename(image_path), img_file, 'image/jpeg')
133
+ }
134
+ data = {
135
+ 'lipstick_color': makeup_colors.lipstick,
136
+ 'eyeliner_color': makeup_colors.eyeliner,
137
+ 'eyeshadow_color': makeup_colors.eyeshadow
138
+ }
139
+ response = requests.post(self.mto_endpoint, files=files, data=data)
140
+ response.raise_for_status()
141
+ logger.info(f"Makeup Try on Completed for {makeup_colors}")
142
+ return response.json()
143
+ except Exception as e:
144
+ print(f"Error in makeup try-on: {str(e)}")
145
+ return None
146
+
147
+ def process_full_tryon_sequence(self,
148
+ model_image_path: str,
149
+ model_id: str,
150
+ necklace_config: Dict,
151
+ clothing_list: List[str],
152
+ makeup_colors: MakeupColors,
153
+ model_name) -> dict:
154
+ """Process a complete sequence of try-ons"""
155
+ results = {
156
+ "nto_results": [],
157
+ "cto_results": [],
158
+ "mto_results": []
159
+ }
160
+
161
+ # 1. First, process necklace try-on
162
+ nto_response = self.process_necklace_tryon(
163
+ model_image_path,
164
+ necklace_config['id'],
165
+ necklace_config['category'],
166
+ necklace_config['store_name'],
167
+ necklace_config['offset_x'],
168
+ necklace_config['offset_y']
169
+ )
170
+
171
+ if nto_response and 'output' in nto_response:
172
+ try:
173
+ image_response = requests.get(nto_response['output'])
174
+ filename = self.generate_filename(model_id, model_name, "nto", necklace_config['category'])
175
+ url = self.upload_to_supabase(image_response.content, filename)
176
+ if url:
177
+ results["nto_results"].append({
178
+ "necklace_id": necklace_config['id'],
179
+ "url": url
180
+ })
181
+ except Exception as e:
182
+ print(f"Error processing necklace result: {str(e)}")
183
+
184
+ # 2. Process each clothing type
185
+ for clothing_type in clothing_list:
186
+ cto_response = self.process_clothing_tryon(model_image_path, clothing_type)
187
+
188
+ if cto_response and cto_response.get("code") == 200:
189
+ try:
190
+ # Get the clothing try-on result
191
+ cto_image = requests.get(cto_response['output'])
192
+
193
+ # Save clothing result
194
+ filename = self.generate_filename(model_id, "cto", clothing_type)
195
+ cto_url = self.upload_to_supabase(cto_image.content, filename)
196
+
197
+ if cto_url:
198
+ results["cto_results"].append({
199
+ "clothing_type": clothing_type,
200
+ "url": cto_url
201
+ })
202
+
203
+ # 3. Apply makeup to clothing result
204
+ temp_path = f"temp_{model_id}_{clothing_type}.png"
205
+ with open(temp_path, 'wb') as f:
206
+ f.write(cto_image.content)
207
+
208
+ mto_response = self.process_makeup_tryon(temp_path, makeup_colors)
209
+ os.remove(temp_path) # Clean up temp file
210
+
211
+ if mto_response and mto_response.get("code") == 200:
212
+ mto_image = requests.get(mto_response['output'])
213
+ filename = self.generate_filename(
214
+ model_id,
215
+ model_name,
216
+ "mto",
217
+ item_type=clothing_type,
218
+ makeup_colors=makeup_colors
219
+ )
220
+ mto_url = self.upload_to_supabase(mto_image.content, filename)
221
+
222
+ if mto_url:
223
+ results["mto_results"].append({
224
+ "clothing_type": clothing_type,
225
+ "makeup_colors": makeup_colors,
226
+ "url": mto_url
227
+ })
228
+
229
+ logger.info(f"Makeup Try on Completed for {clothing_type}")
230
+
231
+ except Exception as e:
232
+ logger.error(f"Error processing {clothing_type}: {str(e)}")
233
+ print(f"Error processing {clothing_type}: {str(e)}")
234
+
235
+ return results
236
+
237
+
238
+ # Example usage:
239
+ """
240
+ processor = FashionTryOnProcessor()
241
+
242
+ # Configuration
243
+ necklace_config = {
244
+ "id": "necklace123",
245
+ "category": "traditional",
246
+ "store_name": "jewelry_store",
247
+ "offset_x": 0,
248
+ "offset_y": 0
249
+ }
250
+
251
+ clothing_list = ["silk saree", "kurti", "casual dress"]
252
+
253
+ makeup_colors = MakeupColors(
254
+ lipstick="Carmine Red",
255
+ eyeliner="Black",
256
+ eyeshadow="Maroon"
257
+ )
258
+
259
+ # Process everything
260
+ results = processor.process_full_tryon_sequence(
261
+ model_image_path="path/to/model/image.jpg",
262
+ model_id="model123",
263
+ necklace_config=necklace_config,
264
+ clothing_list=clothing_list,
265
+ makeup_colors=makeup_colors
266
+ )
267
+
268
+ # Print results
269
+ print("Necklace try-ons:", len(results["nto_results"]))
270
+ print("Clothing try-ons:", len(results["cto_results"]))
271
+ print("Makeup try-ons:", len(results["mto_results"]))
272
+ """