AnishaNaik03 commited on
Commit
6b4e6f2
·
verified ·
1 Parent(s): b222e21

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +274 -151
app.py CHANGED
@@ -321,190 +321,313 @@
321
 
322
 
323
 
324
- import gradio as gr
325
- import json
326
- import base64
327
- import requests
328
- from io import BytesIO
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
329
  import torch
330
  import numpy as np
331
  import cv2
 
332
  import os
333
- from PIL import Image
334
  from detectron2.engine import DefaultPredictor
335
  from detectron2.config import get_cfg
336
  from detectron2 import model_zoo
337
 
338
- # === Set up Detectron2 model ===
 
 
 
 
 
339
  cfg = get_cfg()
340
  cfg.merge_from_file(model_zoo.get_config_file("COCO-Keypoints/keypoint_rcnn_R_50_FPN_3x.yaml"))
341
  cfg.MODEL.WEIGHTS = model_zoo.get_checkpoint_url("COCO-Keypoints/keypoint_rcnn_R_50_FPN_3x.yaml")
342
  cfg.MODEL.DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
343
- predictor = DefaultPredictor(cfg)
344
 
345
- # === Utility ===
346
- def get_distance(p1, p2):
347
- return np.linalg.norm(np.array(p1) - np.array(p2))
348
 
349
- # === Keypoint and Measurement Logic ===
350
  def process_image(image, user_height_cm):
351
- image_cv = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
352
- outputs = predictor(image_cv)
 
 
 
 
 
353
  instances = outputs["instances"]
354
  keypoints = instances.pred_keypoints.cpu().numpy().tolist() if instances.has("pred_keypoints") else None
355
 
356
  if not keypoints:
357
- return "No keypoints detected.", None, None
358
 
359
- keypoints = np.array(keypoints[0])[:, :2]
 
 
 
 
 
 
 
 
 
 
 
360
 
361
- # Draw keypoints and skeleton
362
  skeleton = [(5, 6), (5, 11), (6, 12), (11, 12)]
 
 
363
  for x, y in keypoints:
364
- cv2.circle(image_cv, (int(x), int(y)), 5, (0, 255, 0), -1)
 
 
365
  for pt1, pt2 in skeleton:
366
  x1, y1 = map(int, keypoints[pt1])
367
  x2, y2 = map(int, keypoints[pt2])
368
- cv2.line(image_cv, (x1, y1), (x2, y2), (255, 0, 0), 2)
369
 
370
- # Body part indices
371
- NOSE, L_SHOULDER, R_SHOULDER = 0, 5, 6
372
- L_WRIST, L_HIP, R_HIP, L_ANKLE, R_ANKLE = 9, 11, 12, 15, 16
373
 
 
374
  ankle_mid = ((keypoints[L_ANKLE] + keypoints[R_ANKLE]) / 2).tolist()
375
  pixel_height = get_distance(keypoints[NOSE], ankle_mid)
376
- estimated_full_pixel_height = pixel_height / 0.87
377
- pixels_per_cm = estimated_full_pixel_height / user_height_cm
378
-
379
- shoulder_width_cm = get_distance(keypoints[L_SHOULDER], keypoints[R_SHOULDER]) / pixels_per_cm
380
- waist_width_cm = get_distance(keypoints[L_HIP], keypoints[R_HIP]) / pixels_per_cm
381
- pelvis = ((keypoints[L_HIP] + keypoints[R_HIP]) / 2).tolist()
382
- neck = ((keypoints[L_SHOULDER] + keypoints[R_SHOULDER]) / 2).tolist()
383
- torso_length_cm = get_distance(neck, pelvis) / pixels_per_cm
384
- arm_length_cm = get_distance(keypoints[L_SHOULDER], keypoints[L_WRIST]) / pixels_per_cm
385
 
386
- waist_circumference = np.pi * waist_width_cm
387
- hip_circumference = waist_circumference / 0.75
 
388
 
389
- measurements = {
390
- "Waist Circumference (cm)": round(waist_circumference, 2),
391
- "Hip Circumference (cm)": round(hip_circumference, 2),
392
- "Shoulder Width (cm)": round(shoulder_width_cm, 2),
393
- "Torso Length (Neck to Pelvis, cm)": round(torso_length_cm, 2),
394
- "Full Arm Length (Shoulder to Wrist, cm)": round(arm_length_cm, 2),
395
- }
396
 
397
- return measurements, cv2.cvtColor(image_cv, cv2.COLOR_BGR2RGB), keypoints.tolist()
 
 
398
 
399
- # === Save to DB ===
400
- # def save_to_database(measurements, image, user_height_cm, user_id):
401
- # if not user_id:
402
- # return "❌ user_id missing from URL."
403
-
404
- # if measurements is None or image is None:
405
- # return "⚠️ No data to save."
406
-
407
- # buffered = BytesIO()
408
- # pil_image = Image.fromarray(image)
409
- # pil_image.save(buffered, format="JPEG")
410
- # img_str = base64.b64encode(buffered.getvalue()).decode("utf-8")
411
 
412
- # payload = {
413
- # "imageBase64": img_str,
414
- # "heightCm": user_height_cm,
415
- # "measurements": measurements
416
- # }
417
 
418
- # try:
419
- # response = requests.post(
420
- # f"https://9d68-210-212-162-140.ngrok-free.app/upload/{user_id}",
421
- # json=payload
422
- # )
423
- # if response.status_code == 201:
424
- # return "✅ Measurements and image saved to database!"
425
- # else:
426
- # return f"❌ Failed: {response.status_code} - {response.text}"
427
- # except Exception as e:
428
- # return f"⚠️ Error during save: {str(e)}"
429
-
430
-
431
-
432
- def save_to_database(measurements, image, user_height_cm, user_id):
433
- if not user_id:
434
- return "❌ user_id missing from URL."
435
- if measurements is None or image is None:
436
- return "⚠️ No data to save."
437
-
438
- buffered = BytesIO()
439
- pil_image = Image.fromarray(image)
440
- pil_image.save(buffered, format="JPEG")
441
- img_str = base64.b64encode(buffered.getvalue()).decode("utf-8")
442
-
443
- payload = {
444
- "imageBase64": img_str,
445
- "heightCm": user_height_cm,
446
- "waistCircumferenceCm": measurements.get("Waist Circumference (cm)"),
447
- "hipcircumference": measurements.get("Hip Circumference (cm)"),
448
- "shoulderwidth": measurements.get("Shoulder Width (cm)"),
449
- "torsolength": measurements.get("Torso Length (Neck to Pelvis, cm)"),
450
- "fullarmlength": measurements.get("Full Arm Length (Shoulder to Wrist, cm)"),
451
- }
452
-
453
- try:
454
- response = requests.post(
455
- f"https://7da2-2409-4042-6e81-1806-de6-b8e5-836c-6b95.ngrok-free.app/upload/{user_id}",
456
- json=payload
457
- )
458
- if response.status_code == 201:
459
- return "✅ Measurements and image saved to database!"
460
- else:
461
- return f"❌ Failed: {response.status_code} - {response.text}"
462
- except Exception as e:
463
- return f"⚠️ Error during save: {str(e)}"
464
-
465
-
466
- # === Gradio App ===
467
- with gr.Blocks() as demo:
468
- gr.Markdown("# 📏 AI-Powered Body Measurement Tool")
469
- user_id_state = gr.State()
470
-
471
- @demo.load(inputs=None, outputs=[user_id_state])
472
- def load_user_id(request: gr.Request):
473
- return request.query_params.get("user_id", "")
474
-
475
- with gr.Row():
476
- with gr.Column():
477
- image_input = gr.Image(label="Upload Your Full Body Image", type="pil")
478
- height_input = gr.Number(label="Your Real Height (in cm)")
479
- process_button = gr.Button("📐 Extract Measurements")
480
-
481
- with gr.Column():
482
- output_image = gr.Image(label="Detected Keypoints")
483
- measurement_output = gr.JSON(label="Body Measurements")
484
-
485
- with gr.Row():
486
- save_button = gr.Button("💾 Save to Backend")
487
- save_status = gr.Textbox(label="Status", interactive=False)
488
-
489
- # Store results for save
490
- processed_img = gr.State()
491
- processed_data = gr.State()
492
-
493
- process_button.click(
494
- fn=process_image,
495
- inputs=[image_input, height_input],
496
- outputs=[measurement_output, output_image, processed_data]
497
- ).then(
498
- fn=lambda img: img,
499
- inputs=[output_image],
500
- outputs=processed_img
501
- )
502
-
503
- save_button.click(
504
- fn=save_to_database,
505
- inputs=[measurement_output, processed_img, height_input, user_id_state],
506
- outputs=save_status
507
- )
508
-
509
- if __name__ == "__main__":
510
- demo.launch()
 
321
 
322
 
323
 
324
+ # import gradio as gr
325
+ # import json
326
+ # import base64
327
+ # import requests
328
+ # from io import BytesIO
329
+ # import torch
330
+ # import numpy as np
331
+ # import cv2
332
+ # import os
333
+ # from PIL import Image
334
+ # from detectron2.engine import DefaultPredictor
335
+ # from detectron2.config import get_cfg
336
+ # from detectron2 import model_zoo
337
+
338
+ # # === Set up Detectron2 model ===
339
+ # cfg = get_cfg()
340
+ # cfg.merge_from_file(model_zoo.get_config_file("COCO-Keypoints/keypoint_rcnn_R_50_FPN_3x.yaml"))
341
+ # cfg.MODEL.WEIGHTS = model_zoo.get_checkpoint_url("COCO-Keypoints/keypoint_rcnn_R_50_FPN_3x.yaml")
342
+ # cfg.MODEL.DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
343
+ # predictor = DefaultPredictor(cfg)
344
+
345
+ # # === Utility ===
346
+ # def get_distance(p1, p2):
347
+ # return np.linalg.norm(np.array(p1) - np.array(p2))
348
+
349
+ # # === Keypoint and Measurement Logic ===
350
+ # def process_image(image, user_height_cm):
351
+ # image_cv = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
352
+ # outputs = predictor(image_cv)
353
+ # instances = outputs["instances"]
354
+ # keypoints = instances.pred_keypoints.cpu().numpy().tolist() if instances.has("pred_keypoints") else None
355
+
356
+ # if not keypoints:
357
+ # return "No keypoints detected.", None, None
358
+
359
+ # keypoints = np.array(keypoints[0])[:, :2]
360
+
361
+ # # Draw keypoints and skeleton
362
+ # skeleton = [(5, 6), (5, 11), (6, 12), (11, 12)]
363
+ # for x, y in keypoints:
364
+ # cv2.circle(image_cv, (int(x), int(y)), 5, (0, 255, 0), -1)
365
+ # for pt1, pt2 in skeleton:
366
+ # x1, y1 = map(int, keypoints[pt1])
367
+ # x2, y2 = map(int, keypoints[pt2])
368
+ # cv2.line(image_cv, (x1, y1), (x2, y2), (255, 0, 0), 2)
369
+
370
+ # # Body part indices
371
+ # NOSE, L_SHOULDER, R_SHOULDER = 0, 5, 6
372
+ # L_WRIST, L_HIP, R_HIP, L_ANKLE, R_ANKLE = 9, 11, 12, 15, 16
373
+
374
+ # ankle_mid = ((keypoints[L_ANKLE] + keypoints[R_ANKLE]) / 2).tolist()
375
+ # pixel_height = get_distance(keypoints[NOSE], ankle_mid)
376
+ # estimated_full_pixel_height = pixel_height / 0.87
377
+ # pixels_per_cm = estimated_full_pixel_height / user_height_cm
378
+
379
+ # shoulder_width_cm = get_distance(keypoints[L_SHOULDER], keypoints[R_SHOULDER]) / pixels_per_cm
380
+ # waist_width_cm = get_distance(keypoints[L_HIP], keypoints[R_HIP]) / pixels_per_cm
381
+ # pelvis = ((keypoints[L_HIP] + keypoints[R_HIP]) / 2).tolist()
382
+ # neck = ((keypoints[L_SHOULDER] + keypoints[R_SHOULDER]) / 2).tolist()
383
+ # torso_length_cm = get_distance(neck, pelvis) / pixels_per_cm
384
+ # arm_length_cm = get_distance(keypoints[L_SHOULDER], keypoints[L_WRIST]) / pixels_per_cm
385
+
386
+ # waist_circumference = np.pi * waist_width_cm
387
+ # hip_circumference = waist_circumference / 0.75
388
+
389
+ # measurements = {
390
+ # "Waist Circumference (cm)": round(waist_circumference, 2),
391
+ # "Hip Circumference (cm)": round(hip_circumference, 2),
392
+ # "Shoulder Width (cm)": round(shoulder_width_cm, 2),
393
+ # "Torso Length (Neck to Pelvis, cm)": round(torso_length_cm, 2),
394
+ # "Full Arm Length (Shoulder to Wrist, cm)": round(arm_length_cm, 2),
395
+ # }
396
+
397
+ # return measurements, cv2.cvtColor(image_cv, cv2.COLOR_BGR2RGB), keypoints.tolist()
398
+
399
+ # # === Save to DB ===
400
+ # # def save_to_database(measurements, image, user_height_cm, user_id):
401
+ # # if not user_id:
402
+ # # return "❌ user_id missing from URL."
403
+
404
+ # # if measurements is None or image is None:
405
+ # # return "⚠️ No data to save."
406
+
407
+ # # buffered = BytesIO()
408
+ # # pil_image = Image.fromarray(image)
409
+ # # pil_image.save(buffered, format="JPEG")
410
+ # # img_str = base64.b64encode(buffered.getvalue()).decode("utf-8")
411
+
412
+ # # payload = {
413
+ # # "imageBase64": img_str,
414
+ # # "heightCm": user_height_cm,
415
+ # # "measurements": measurements
416
+ # # }
417
+
418
+ # # try:
419
+ # # response = requests.post(
420
+ # # f"https://9d68-210-212-162-140.ngrok-free.app/upload/{user_id}",
421
+ # # json=payload
422
+ # # )
423
+ # # if response.status_code == 201:
424
+ # # return "✅ Measurements and image saved to database!"
425
+ # # else:
426
+ # # return f"❌ Failed: {response.status_code} - {response.text}"
427
+ # # except Exception as e:
428
+ # # return f"⚠️ Error during save: {str(e)}"
429
+
430
+
431
+
432
+ # def save_to_database(measurements, image, user_height_cm, user_id):
433
+ # if not user_id:
434
+ # return "❌ user_id missing from URL."
435
+ # if measurements is None or image is None:
436
+ # return "⚠️ No data to save."
437
+
438
+ # buffered = BytesIO()
439
+ # pil_image = Image.fromarray(image)
440
+ # pil_image.save(buffered, format="JPEG")
441
+ # img_str = base64.b64encode(buffered.getvalue()).decode("utf-8")
442
+
443
+ # payload = {
444
+ # "imageBase64": img_str,
445
+ # "heightCm": user_height_cm,
446
+ # "waistCircumferenceCm": measurements.get("Waist Circumference (cm)"),
447
+ # "hipcircumference": measurements.get("Hip Circumference (cm)"),
448
+ # "shoulderwidth": measurements.get("Shoulder Width (cm)"),
449
+ # "torsolength": measurements.get("Torso Length (Neck to Pelvis, cm)"),
450
+ # "fullarmlength": measurements.get("Full Arm Length (Shoulder to Wrist, cm)"),
451
+ # }
452
+
453
+ # try:
454
+ # response = requests.post(
455
+ # f"https://7da2-2409-4042-6e81-1806-de6-b8e5-836c-6b95.ngrok-free.app/upload/{user_id}",
456
+ # json=payload
457
+ # )
458
+ # if response.status_code == 201:
459
+ # return "✅ Measurements and image saved to database!"
460
+ # else:
461
+ # return f"❌ Failed: {response.status_code} - {response.text}"
462
+ # except Exception as e:
463
+ # return f"⚠️ Error during save: {str(e)}"
464
+
465
+
466
+ # # === Gradio App ===
467
+ # with gr.Blocks() as demo:
468
+ # gr.Markdown("# 📏 AI-Powered Body Measurement Tool")
469
+ # user_id_state = gr.State()
470
+
471
+ # @demo.load(inputs=None, outputs=[user_id_state])
472
+ # def load_user_id(request: gr.Request):
473
+ # return request.query_params.get("user_id", "")
474
+
475
+ # with gr.Row():
476
+ # with gr.Column():
477
+ # image_input = gr.Image(label="Upload Your Full Body Image", type="pil")
478
+ # height_input = gr.Number(label="Your Real Height (in cm)")
479
+ # process_button = gr.Button("📐 Extract Measurements")
480
+
481
+ # with gr.Column():
482
+ # output_image = gr.Image(label="Detected Keypoints")
483
+ # measurement_output = gr.JSON(label="Body Measurements")
484
+
485
+ # with gr.Row():
486
+ # save_button = gr.Button("💾 Save to Backend")
487
+ # save_status = gr.Textbox(label="Status", interactive=False)
488
+
489
+ # # Store results for save
490
+ # processed_img = gr.State()
491
+ # processed_data = gr.State()
492
+
493
+ # process_button.click(
494
+ # fn=process_image,
495
+ # inputs=[image_input, height_input],
496
+ # outputs=[measurement_output, output_image, processed_data]
497
+ # ).then(
498
+ # fn=lambda img: img,
499
+ # inputs=[output_image],
500
+ # outputs=processed_img
501
+ # )
502
+
503
+ # save_button.click(
504
+ # fn=save_to_database,
505
+ # inputs=[measurement_output, processed_img, height_input, user_id_state],
506
+ # outputs=save_status
507
+ # )
508
+
509
+ # if __name__ == "__main__":
510
+ # demo.launch()
511
  import torch
512
  import numpy as np
513
  import cv2
514
+ import json
515
  import os
516
+ import gradio as gr
517
  from detectron2.engine import DefaultPredictor
518
  from detectron2.config import get_cfg
519
  from detectron2 import model_zoo
520
 
521
+ # Create output directory if it doesn't exist
522
+ output_dir = "key/"
523
+ os.makedirs(output_dir, exist_ok=True)
524
+ output_file = os.path.join(output_dir, "keypoints.json")
525
+
526
+ # Load pre-trained Keypoint R-CNN model
527
  cfg = get_cfg()
528
  cfg.merge_from_file(model_zoo.get_config_file("COCO-Keypoints/keypoint_rcnn_R_50_FPN_3x.yaml"))
529
  cfg.MODEL.WEIGHTS = model_zoo.get_checkpoint_url("COCO-Keypoints/keypoint_rcnn_R_50_FPN_3x.yaml")
530
  cfg.MODEL.DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
 
531
 
532
+ # Load the predictor
533
+ predictor = DefaultPredictor(cfg)
 
534
 
 
535
  def process_image(image, user_height_cm):
536
+ # Convert Gradio image input to OpenCV format
537
+ image = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
538
+
539
+ # Run keypoint detection
540
+ outputs = predictor(image)
541
+
542
+ # Extract keypoints
543
  instances = outputs["instances"]
544
  keypoints = instances.pred_keypoints.cpu().numpy().tolist() if instances.has("pred_keypoints") else None
545
 
546
  if not keypoints:
547
+ return "No keypoints detected.", None
548
 
549
+ # Save keypoints to JSON
550
+ with open(output_file, "w") as f:
551
+ json.dump({"keypoints": keypoints}, f, indent=4)
552
+
553
+ keypoints = np.array(keypoints[0])[:, :2] # Extract (x, y) coordinates
554
+
555
+ # COCO format indices
556
+ NOSE, L_SHOULDER, R_SHOULDER = 0, 5, 6
557
+ L_ELBOW, R_ELBOW = 7, 8
558
+ L_WRIST, R_WRIST = 9, 10
559
+ L_HIP, R_HIP = 11, 12
560
+ L_ANKLE, R_ANKLE = 15, 16
561
 
562
+ # Define Keypoint Pairs for Drawing Lines (COCO Format)
563
  skeleton = [(5, 6), (5, 11), (6, 12), (11, 12)]
564
+
565
+ # Draw Keypoints
566
  for x, y in keypoints:
567
+ cv2.circle(image, (int(x), int(y)), 5, (0, 255, 0), -1)
568
+
569
+ # Draw Skeleton
570
  for pt1, pt2 in skeleton:
571
  x1, y1 = map(int, keypoints[pt1])
572
  x2, y2 = map(int, keypoints[pt2])
573
+ cv2.line(image, (x1, y1), (x2, y2), (255, 0, 0), 2)
574
 
575
+ # Function to calculate Euclidean distance
576
+ def get_distance(p1, p2):
577
+ return np.linalg.norm(np.array(p1) - np.array(p2))
578
 
579
+ # Calculate full height (consider head length)
580
  ankle_mid = ((keypoints[L_ANKLE] + keypoints[R_ANKLE]) / 2).tolist()
581
  pixel_height = get_distance(keypoints[NOSE], ankle_mid)
 
 
 
 
 
 
 
 
 
582
 
583
+ # Estimated full body height (add approx head length)
584
+ estimated_full_pixel_height = pixel_height / 0.87 # Since 87% = nose to ankle
585
+ pixels_per_cm = estimated_full_pixel_height / user_height_cm
586
 
587
+ # Waist and shoulder measurements
588
+ shoulder_width_px = get_distance(keypoints[L_SHOULDER], keypoints[R_SHOULDER])
589
+ waist_width_px = get_distance(keypoints[L_HIP], keypoints[R_HIP])
 
 
 
 
590
 
591
+ # Convert to cm
592
+ shoulder_width_cm = shoulder_width_px / pixels_per_cm
593
+ waist_width_cm = waist_width_px / pixels_per_cm
594
 
595
+ # Torso Length (Neck to Pelvis)
596
+ pelvis = ((keypoints[L_HIP] + keypoints[R_HIP]) / 2).tolist()
597
+ neck = ((keypoints[L_SHOULDER] + keypoints[R_SHOULDER]) / 2).tolist()
598
+ torso_length_px = get_distance(neck, pelvis)
599
+ torso_length_cm = torso_length_px / pixels_per_cm
 
 
 
 
 
 
 
600
 
601
+ # Arm Length (Shoulder to Wrist)
602
+ arm_length_px = get_distance(keypoints[L_SHOULDER], keypoints[L_WRIST])
603
+ arm_length_cm = arm_length_px / pixels_per_cm
 
 
604
 
605
+ # Calculate waist and hip circumference (Ellipse approximation)
606
+ # Waist circumference ≈ π × (waist_width / 2) × 2
607
+ waist_circumference = np.pi * waist_width_cm
608
+ hip_circumference = waist_circumference / 0.75 # Assuming hip is slightly bigger than waist
609
+
610
+ # Improved body measurement calculation
611
+ def calculate_body_measurements(waist_circumference, hip_circumference, shoulder_width_cm, torso_length_cm, arm_length_cm):
612
+ return {
613
+ "Waist Circumference (cm)": round(waist_circumference, 2),
614
+ "Hip Circumference (cm)": round(hip_circumference, 2),
615
+ "Shoulder Width (cm)": round(shoulder_width_cm, 2),
616
+ "Torso Length (Neck to Pelvis, cm)": round(torso_length_cm, 2),
617
+ "Full Arm Length (Shoulder to Wrist, cm)": round(arm_length_cm, 2),
618
+ }
619
+
620
+ measurements = calculate_body_measurements(waist_circumference, hip_circumference, shoulder_width_cm, torso_length_cm, arm_length_cm)
621
+
622
+ return measurements, cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
623
+
624
+ # Gradio Interface
625
+ demo = gr.Interface(
626
+ fn=process_image,
627
+ inputs=[gr.Image(type="pil"), gr.Number(label="User Height (cm)")],
628
+ outputs=[gr.JSON(label="Measurements"), gr.Image(type="pil", label="Keypoint Overlay")],
629
+ title="Keypoint Measurement Extractor",
630
+ description="Upload an image, enter your height, and get body measurements based on keypoints.",
631
+ )
632
+
633
+ demo.launch()