ishworrsubedii commited on
Commit
db9ae22
·
1 Parent(s): 69c4c90

add: async image upload, nto optimize

Browse files
Files changed (2) hide show
  1. app.py +6 -1
  2. src/api/nto_api.py +86 -54
app.py CHANGED
@@ -22,4 +22,9 @@ app.add_middleware(
22
  allow_credentials=True,
23
  allow_methods=["*"],
24
  allow_headers=["*"],
25
- )
 
 
 
 
 
 
22
  allow_credentials=True,
23
  allow_methods=["*"],
24
  allow_headers=["*"],
25
+ )
26
+
27
+ if __name__ == '__main__':
28
+ import uvicorn
29
+
30
+ uvicorn.run(app)
src/api/nto_api.py CHANGED
@@ -26,6 +26,9 @@ import replicate
26
  import requests
27
  from src.utils.logger import logger
28
  import secrets
 
 
 
29
 
30
  pipeline = Pipeline()
31
 
@@ -360,31 +363,64 @@ async def parse_necklace_try_on_id(necklaceImageId: str = Form(...),
360
  )
361
 
362
 
363
- def supabase_upload_and_return_url(prefix: str, image: Image.Image,quality: int = 85) :
364
-
365
  try:
366
- filename = f"{prefix}_{secrets.token_hex(16)}.webp"
367
-
368
-
 
 
 
 
 
 
369
 
 
 
 
 
 
370
 
371
- buffer = BytesIO()
372
- image.save(buffer, format='WEBP', quality=quality, optimize=True)
373
- image_bytes = buffer.getvalue()
374
 
375
- bucket.upload(
376
- path=filename,
377
- file=image_bytes,
378
- )
 
 
 
379
 
380
  return bucket.get_public_url(filename)
381
 
382
  except Exception as e:
383
- print(f"Failed to upload image: {str(e)}")
384
  return None
385
- finally:
386
- if 'buffer' in locals():
387
- buffer.close()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
388
 
389
 
390
  @nto_cto_router.post("/necklaceTryOnID")
@@ -394,18 +430,6 @@ async def necklace_try_on_id(necklace_try_on_id: NecklaceTryOnIDEntity = Depends
394
  logger.info(f">>> NECKLACE TRY ON ID STARTED :: {necklace_try_on_id.storename} <<<")
395
  start_time = time.time()
396
 
397
- try:
398
- data, _ = supabase.table("APIKeyList").select("*").filter("API_KEY", "eq",
399
- necklace_try_on_id.api_token).execute()
400
- api_key_actual = data[1][0]['API_KEY']
401
- if api_key_actual != necklace_try_on_id.api_token:
402
- logger.error(">>> API KEY VALIDATION FAILED <<<")
403
- return JSONResponse(content={"error": "Invalid API Key"}, status_code=401)
404
- logger.info(">>> API KEY VALIDATION SUCCESSFUL <<<")
405
- except Exception as e:
406
- logger.error(f">>> API KEY VALIDATION ERROR: {str(e)} <<<")
407
- return JSONResponse(content={"error": f"Error validating API key", "code": 500}, status_code=500)
408
-
409
  try:
410
  imageBytes = await image.read()
411
  jewellery_url = f"https://lvuhhlrkcuexzqtsbqyu.supabase.co/storage/v1/object/public/Stores/{necklace_try_on_id.storename}/{necklace_try_on_id.necklaceCategory}/image/{necklace_try_on_id.necklaceImageId}.png"
@@ -436,39 +460,47 @@ async def necklace_try_on_id(necklace_try_on_id: NecklaceTryOnIDEntity = Depends
436
  logger.info(">>> SAVING RESULT IMAGES <<<")
437
  start_time_saving = time.time()
438
 
439
- result_url = supabase_upload_and_return_url(prefix="necklace_try_on", image=result)
440
- mask_url = supabase_upload_and_return_url(prefix="necklace_try_on_mask", image=mask)
 
 
 
 
441
 
442
- logger.info(f">>> RESULT IMAGES SAVED IN {round((time.time() - start_time_saving), 2)}s <<<")
 
443
 
 
444
  logger.info(">>> RESULT IMAGES SAVED <<<")
445
  except Exception as e:
446
  logger.error(f">>> RESULT SAVING ERROR: {str(e)} <<<")
447
  return JSONResponse(content={"error": f"Error saving result images", "code": 500}, status_code=500)
448
-
449
  try:
450
- creditResponse = deductAndTrackCredit(storename=necklace_try_on_id.storename, endpoint="/necklaceTryOnID")
451
- total_backend_time = round((time.time() - start_time), 2)
452
- response = {
453
- "code": 200,
454
- "output": f"{result_url}",
455
- "mask": f"{mask_url}",
456
- "inference_time": total_backend_time
457
- }
458
- if creditResponse == "No Credits Available":
459
- logger.error(">>> NO CREDITS REMAINING <<<")
460
- response = {"error": "No Credits Remaining"}
461
- return JSONResponse(content=response, status_code=402)
462
- logger.info(">>> CREDITS DEDUCTED SUCCESSFULLY <<<")
463
- except Exception as e:
464
- logger.error(f">>> CREDIT DEDUCTION ERROR: {str(e)} <<<")
465
- return JSONResponse(content={"error": f"Error deducting credits", "code": 500}, status_code=500)
466
-
467
- logger.info(f">>> TOTAL INFERENCE TIME: {total_backend_time}s <<<")
468
- logger.info(f">>> NECKLACE TRY ON COMPLETED :: {necklace_try_on_id.storename} <<<")
469
- logger.info("-" * 50)
470
-
471
- return JSONResponse(content=response, status_code=200)
 
 
 
472
 
473
 
474
  @nto_cto_router.post("/canvasPoints")
 
26
  import requests
27
  from src.utils.logger import logger
28
  import secrets
29
+ import aiohttp
30
+ import asyncio
31
+ import gc
32
 
33
  pipeline = Pipeline()
34
 
 
363
  )
364
 
365
 
366
+ async def supabase_upload_and_return_url(prefix: str, image: Image.Image, quality: int = 85):
 
367
  try:
368
+ filename = f"{prefix}_{secrets.token_hex(8)}.webp"
369
+
370
+ loop = asyncio.get_event_loop()
371
+ image_bytes = await loop.run_in_executor(
372
+ None,
373
+ process_image,
374
+ image,
375
+ quality
376
+ )
377
 
378
+ async with aiohttp.ClientSession() as session:
379
+ headers = {
380
+ "Authorization": f"Bearer {key}",
381
+ "Content-Type": "image/webp"
382
+ }
383
 
384
+ upload_url = f"{url}/storage/v1/object/JewelMirrorOutputs/{filename}"
 
 
385
 
386
+ async with session.post(
387
+ upload_url,
388
+ data=image_bytes,
389
+ headers=headers
390
+ ) as response:
391
+ if response.status != 200:
392
+ raise Exception(f"Upload failed with status {response.status}")
393
 
394
  return bucket.get_public_url(filename)
395
 
396
  except Exception as e:
397
+ logger.error(f"Failed to upload image: {str(e)}")
398
  return None
399
+
400
+
401
+ def process_image(image: Image.Image, quality: int) -> bytes:
402
+ try:
403
+ if image.mode in ['RGBA', 'P']:
404
+ image = image.convert('RGB')
405
+
406
+ max_size = 3000
407
+ if image.width > max_size or image.height > max_size:
408
+ ratio = min(max_size / image.width, max_size / image.height)
409
+ new_size = (int(image.width * ratio), int(image.height * ratio))
410
+ image = image.resize(new_size, Image.Resampling.LANCZOS)
411
+
412
+ with BytesIO() as buffer:
413
+ image.save(
414
+ buffer,
415
+ format='WEBP',
416
+ quality=quality,
417
+ optimize=True,
418
+ method=6
419
+ )
420
+ return buffer.getvalue()
421
+ except Exception as e:
422
+ logger.error(f"Image processing failed: {str(e)}")
423
+ raise
424
 
425
 
426
  @nto_cto_router.post("/necklaceTryOnID")
 
430
  logger.info(f">>> NECKLACE TRY ON ID STARTED :: {necklace_try_on_id.storename} <<<")
431
  start_time = time.time()
432
 
 
 
 
 
 
 
 
 
 
 
 
 
433
  try:
434
  imageBytes = await image.read()
435
  jewellery_url = f"https://lvuhhlrkcuexzqtsbqyu.supabase.co/storage/v1/object/public/Stores/{necklace_try_on_id.storename}/{necklace_try_on_id.necklaceCategory}/image/{necklace_try_on_id.necklaceImageId}.png"
 
460
  logger.info(">>> SAVING RESULT IMAGES <<<")
461
  start_time_saving = time.time()
462
 
463
+ # Upload both images concurrently
464
+ upload_tasks = [
465
+ supabase_upload_and_return_url(prefix="necklace_try_on", image=result),
466
+ supabase_upload_and_return_url(prefix="necklace_try_on_mask", image=mask)
467
+ ]
468
+ result_url, mask_url = await asyncio.gather(*upload_tasks)
469
 
470
+ if not result_url or not mask_url:
471
+ raise Exception("Failed to upload one or both images")
472
 
473
+ logger.info(f">>> RESULT IMAGES SAVED IN {round((time.time() - start_time_saving), 2)}s <<<")
474
  logger.info(">>> RESULT IMAGES SAVED <<<")
475
  except Exception as e:
476
  logger.error(f">>> RESULT SAVING ERROR: {str(e)} <<<")
477
  return JSONResponse(content={"error": f"Error saving result images", "code": 500}, status_code=500)
 
478
  try:
479
+ try:
480
+ total_backend_time = round((time.time() - start_time), 2)
481
+ response = {
482
+ "code": 200,
483
+ "output": f"{result_url}",
484
+ "mask": f"{mask_url}",
485
+ "inference_time": total_backend_time
486
+ }
487
+
488
+
489
+
490
+ except Exception as e:
491
+ logger.error(f">>> RESPONSE GENERATION ERROR: {str(e)} <<<")
492
+ return JSONResponse(content={"error": f"Error generating response", "code": 500}, status_code=500)
493
+
494
+ logger.info(f">>> TOTAL INFERENCE TIME: {total_backend_time}s <<<")
495
+ logger.info(f">>> NECKLACE TRY ON COMPLETED :: {necklace_try_on_id.storename} <<<")
496
+ logger.info("-" * 50)
497
+
498
+ return JSONResponse(content=response, status_code=200)
499
+
500
+ finally:
501
+ if 'result' in locals(): del result
502
+ if 'mask' in locals(): del mask
503
+ gc.collect()
504
 
505
 
506
  @nto_cto_router.post("/canvasPoints")