Spaces:
Sleeping
Sleeping
Commit
·
130fb6c
1
Parent(s):
5a186b4
add: necklace try on points
Browse files- app.py +50 -1
- src/components/necklaceTryOn.py +61 -0
- src/pipelines/completePipeline.py +8 -0
app.py
CHANGED
@@ -31,7 +31,7 @@ app.add_middleware(
|
|
31 |
)
|
32 |
url: str = os.getenv("SUPABASE_URL")
|
33 |
key: str = os.getenv("SUPABASE_KEY")
|
34 |
-
supabase: Client = create_client(
|
35 |
bucket = supabase.storage.from_("JewelMirrorOutputs")
|
36 |
|
37 |
|
@@ -411,3 +411,52 @@ async def canvas_points(necklace_try_on_id: NecklaceTryOnIDEntity = Depends(pars
|
|
411 |
|
412 |
else:
|
413 |
return JSONResponse(content=response)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
31 |
)
|
32 |
url: str = os.getenv("SUPABASE_URL")
|
33 |
key: str = os.getenv("SUPABASE_KEY")
|
34 |
+
supabase: Client = create_client(supabase_key=key, supabase_url=url)
|
35 |
bucket = supabase.storage.from_("JewelMirrorOutputs")
|
36 |
|
37 |
|
|
|
411 |
|
412 |
else:
|
413 |
return JSONResponse(content=response)
|
414 |
+
|
415 |
+
|
416 |
+
@app.post("/necklaceTryOnWithPoints")
|
417 |
+
async def necklace_try_on_with_points(necklace_try_on_id: NecklaceTryOnIDEntity = Depends(parse_necklace_try_on_id),
|
418 |
+
image: UploadFile = File(...),
|
419 |
+
left_x: int = Form(...),
|
420 |
+
left_y: int = Form(...),
|
421 |
+
right_x: int = Form(...),
|
422 |
+
right_y: int = Form(...)):
|
423 |
+
imageBytes = await image.read()
|
424 |
+
|
425 |
+
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"
|
426 |
+
|
427 |
+
try:
|
428 |
+
image, jewellery = Image.open(BytesIO(imageBytes)), Image.open(returnBytesData(url=jewellery_url))
|
429 |
+
|
430 |
+
except:
|
431 |
+
error_message = {
|
432 |
+
"error": "The requested resource (Image, necklace category, or store) is not available. Please verify the availability and try again."
|
433 |
+
}
|
434 |
+
|
435 |
+
return JSONResponse(content=error_message, status_code=404)
|
436 |
+
|
437 |
+
result, headerText, mask = await pipeline.necklaceTryOnWithPoints_(
|
438 |
+
image=image, jewellery=jewellery, left_shoulder=(left_x, left_y), right_shoulder=(right_x, right_y),
|
439 |
+
storename=necklace_try_on_id.storename
|
440 |
+
)
|
441 |
+
|
442 |
+
inMemFile = BytesIO()
|
443 |
+
inMemFileMask = BytesIO()
|
444 |
+
result.save(inMemFile, format="WEBP", quality=85)
|
445 |
+
mask.save(inMemFileMask, format="WEBP", quality=85)
|
446 |
+
outputBytes = inMemFile.getvalue()
|
447 |
+
maskBytes = inMemFileMask.getvalue()
|
448 |
+
response = {
|
449 |
+
"output": f"data:image/WEBP;base64,{base64.b64encode(outputBytes).decode('utf-8')}",
|
450 |
+
"mask": f"data:image/WEBP;base64,{base64.b64encode(maskBytes).decode('utf-8')}"
|
451 |
+
}
|
452 |
+
|
453 |
+
creditResponse = deductAndTrackCredit(storename=necklace_try_on_id.storename, endpoint="/necklaceTryOnID")
|
454 |
+
if creditResponse == "No Credits Available":
|
455 |
+
response = {
|
456 |
+
"error": "No Credits Remaining"
|
457 |
+
}
|
458 |
+
|
459 |
+
return JSONResponse(content=response)
|
460 |
+
|
461 |
+
else:
|
462 |
+
return JSONResponse(content=response)
|
src/components/necklaceTryOn.py
CHANGED
@@ -343,3 +343,64 @@ class NecklaceTryOn:
|
|
343 |
except Exception as e:
|
344 |
logger.error(f"{CustomException(e)}:: {storename}")
|
345 |
raise CustomException(e)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
343 |
except Exception as e:
|
344 |
logger.error(f"{CustomException(e)}:: {storename}")
|
345 |
raise CustomException(e)
|
346 |
+
|
347 |
+
def necklaceTryOnWithPoints(self, image: Image.Image, jewellery: Image.Image, storename: str,
|
348 |
+
left_point: tuple[int, int], right_point: tuple[int, int]) -> list[
|
349 |
+
Union[Image.Image, str]]:
|
350 |
+
try:
|
351 |
+
logger.info(f">>> NECKLACE TRY ON WITH POINTS STARTED :: {storename} <<<")
|
352 |
+
|
353 |
+
image = np.array(image.convert("RGB"))
|
354 |
+
# .resize((3000, 3000)))
|
355 |
+
copy_image = image.copy()
|
356 |
+
jewellery = np.array(jewellery.convert("RGBA"))
|
357 |
+
|
358 |
+
logger.info(f"NECKLACE TRY ON :: scaling the necklace image based on given points :: {storename}")
|
359 |
+
|
360 |
+
avg_x1 = left_point[0]
|
361 |
+
avg_x2 = right_point[0]
|
362 |
+
avg_y1 = left_point[1]
|
363 |
+
avg_y2 = right_point[1]
|
364 |
+
|
365 |
+
angle = math.ceil(self.detector.findAngle((avg_x2, avg_y2), (avg_x1, avg_y1), (avg_x2, avg_y1))[0])
|
366 |
+
if avg_y2 >= avg_y1:
|
367 |
+
angle *= -1
|
368 |
+
|
369 |
+
xdist = avg_x2 - avg_x1
|
370 |
+
origImgRatio = xdist / jewellery.shape[1]
|
371 |
+
ydist = jewellery.shape[0] * origImgRatio
|
372 |
+
|
373 |
+
logger.info(f"NECKLACE TRY ON :: adding offset based on the necklace shape :: {storename}")
|
374 |
+
image_gray = cv2.cvtColor(jewellery, cv2.COLOR_BGRA2GRAY)
|
375 |
+
offset = int(0.8 * xdist * (np.argmax(image_gray[0, :] != 255) / jewellery.shape[1]))
|
376 |
+
|
377 |
+
jewellery = cv2.resize(jewellery, (int(xdist), int(ydist)), interpolation=cv2.INTER_AREA)
|
378 |
+
jewellery = cvzone.rotateImage(jewellery, angle)
|
379 |
+
y_coordinate = avg_y1 - offset
|
380 |
+
available_space = copy_image.shape[0] - y_coordinate
|
381 |
+
extra = jewellery.shape[0] - available_space
|
382 |
+
|
383 |
+
headerText = "To see more of the necklace, please step back slightly." if extra > 0 else "success"
|
384 |
+
|
385 |
+
logger.info(f"NECKLACE TRY ON :: generating output with given points :: {storename}")
|
386 |
+
result = cvzone.overlayPNG(copy_image, jewellery, (avg_x1, y_coordinate))
|
387 |
+
image = Image.fromarray(result.astype(np.uint8))
|
388 |
+
|
389 |
+
if storename not in self.logo_cache:
|
390 |
+
self.logo_cache[storename] = Image.open(
|
391 |
+
returnBytesData(url=self.necklaceTryOnConfig.logoURL.format(storename)))
|
392 |
+
result = addWatermark(background=image, logo=self.logo_cache[storename])
|
393 |
+
|
394 |
+
# Create binary mask
|
395 |
+
blackedNecklace = np.zeros_like(copy_image)
|
396 |
+
cvzone.overlayPNG(blackedNecklace, jewellery, (avg_x1, y_coordinate))
|
397 |
+
binaryMask = cv2.cvtColor(blackedNecklace.astype(np.uint8), cv2.COLOR_BGR2GRAY)
|
398 |
+
binaryMask = (binaryMask > 5).astype(np.uint8) * 255
|
399 |
+
mask = Image.fromarray(binaryMask).convert("RGB")
|
400 |
+
|
401 |
+
gc.collect()
|
402 |
+
return [result, headerText, mask]
|
403 |
+
|
404 |
+
except Exception as e:
|
405 |
+
logger.error(f"{CustomException(e)}:: {storename}")
|
406 |
+
raise CustomException(e)
|
src/pipelines/completePipeline.py
CHANGED
@@ -20,3 +20,11 @@ class Pipeline:
|
|
20 |
async def canvasPoint(self, image: Image.Image, jewellery: Image.Image, storename: str) -> dict:
|
21 |
points = self.necklaceTryOnObj.canvasPoints(image=image, jewellery=jewellery, storename=storename)
|
22 |
return points
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
20 |
async def canvasPoint(self, image: Image.Image, jewellery: Image.Image, storename: str) -> dict:
|
21 |
points = self.necklaceTryOnObj.canvasPoints(image=image, jewellery=jewellery, storename=storename)
|
22 |
return points
|
23 |
+
|
24 |
+
async def necklaceTryOnWithPoints_(self, image: Image.Image, jewellery: Image.Image, left_shoulder: tuple[int, int],
|
25 |
+
right_shoulder: tuple[int, int], storename: str) -> list[Union[Image.Image, str]]:
|
26 |
+
result, headerText, mask = self.necklaceTryOnObj.necklaceTryOnWithPoints(image=image, jewellery=jewellery,
|
27 |
+
left_point=left_shoulder,
|
28 |
+
right_point=right_shoulder,
|
29 |
+
storename=storename)
|
30 |
+
return [result, headerText, mask]
|