File size: 11,290 Bytes
bf4c03b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b760957
 
bf4c03b
 
b760957
f87d698
 
 
5038a10
b760957
f87d698
 
 
e212ae5
b760957
e212ae5
 
 
 
6b4e6f2
098fb44
b760957
 
 
6b4e6f2
b760957
 
 
 
098fb44
 
 
 
b760957
6b4e6f2
d7b15c7
6b4e6f2
b760957
 
f87d698
b760957
f87d698
 
 
b760957
f87d698
5038a10
b760957
5038a10
098fb44
 
 
5038a10
6b4e6f2
098fb44
d7b15c7
 
6b4e6f2
5038a10
 
d7b15c7
f5684a0
5038a10
 
6b4e6f2
d7b15c7
5038a10
 
bf4c03b
 
b760957
 
 
 
 
5038a10
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b760957
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5038a10
 
 
 
d7b15c7
6b4e6f2
b760957
 
04f023a
b760957
 
 
 
 
 
 
 
 
6b4e6f2
b760957
d7b15c7
b760957
 
 
 
 
 
d7b15c7
 
 
b760957
 
 
 
eb3dde8
b760957
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bf4c03b
6b4e6f2
b760957
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
# import torch
# import numpy as np
# import cv2
# import json
# import os
# import gradio as gr
# from detectron2.detectron2.engine import DefaultPredictor
# from detectron2.detectron2.config import get_cfg
# from detectron2 import model_zoo
# import torch_utils
# import dnnlib
# import torch
# import numpy as np
# import cv2
# import json
# import os
# import gradio as gr
# from detectron2.engine import DefaultPredictor
# from detectron2.config import get_cfg
# from detectron2 import model_zoo

# # Output directory
# output_dir = "key/"
# os.makedirs(output_dir, exist_ok=True)
# output_file = os.path.join(output_dir, "keypoints.json")

# # Load pre-trained Keypoint R-CNN model
# cfg = get_cfg()
# cfg.merge_from_file(model_zoo.get_config_file("COCO-Keypoints/keypoint_rcnn_R_50_FPN_3x.yaml"))
# cfg.MODEL.WEIGHTS = model_zoo.get_checkpoint_url("COCO-Keypoints/keypoint_rcnn_R_50_FPN_3x.yaml")
# cfg.MODEL.DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
# predictor = DefaultPredictor(cfg)

# def process_image(image, user_height_cm):
#     image = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
#     outputs = predictor(image)

#     instances = outputs["instances"]
#     keypoints = instances.pred_keypoints.cpu().numpy().tolist() if instances.has("pred_keypoints") else None

#     if not keypoints:
#         return "No keypoints detected.", None

#     with open(output_file, "w") as f:
#         json.dump({"keypoints": keypoints}, f, indent=4)

#     keypoints = np.array(keypoints[0])[:, :2]

#     NOSE, L_SHOULDER, R_SHOULDER = 0, 5, 6
#     L_ELBOW, R_ELBOW = 7, 8
#     L_WRIST, R_WRIST = 9, 10
#     L_HIP, R_HIP = 11, 12
#     L_KNEE, R_KNEE = 13, 14
#     L_ANKLE, R_ANKLE = 15, 16

#     skeleton = [(5, 6), (5, 11), (6, 12), (11, 12)]

#     for x, y in keypoints:
#         cv2.circle(image, (int(x), int(y)), 5, (0, 255, 0), -1)
#     for pt1, pt2 in skeleton:
#         x1, y1 = map(int, keypoints[pt1])
#         x2, y2 = map(int, keypoints[pt2])
#         cv2.line(image, (x1, y1), (x2, y2), (255, 0, 0), 2)

#     def get_distance(p1, p2):
#         return np.linalg.norm(np.array(p1) - np.array(p2))

#     ankle_mid = ((keypoints[L_ANKLE] + keypoints[R_ANKLE]) / 2).tolist()
#     pixel_height = get_distance(keypoints[NOSE], ankle_mid)
#     estimated_full_pixel_height = pixel_height / 0.87
#     pixels_per_cm = estimated_full_pixel_height / user_height_cm

#     shoulder_width_cm = get_distance(keypoints[L_SHOULDER], keypoints[R_SHOULDER]) / pixels_per_cm
#     waist_width_cm = get_distance(keypoints[L_HIP], keypoints[R_HIP]) / pixels_per_cm

#     pelvis = ((keypoints[L_HIP] + keypoints[R_HIP]) / 2).tolist()
#     neck = ((keypoints[L_SHOULDER] + keypoints[R_SHOULDER]) / 2).tolist()
#     torso_length_cm = get_distance(neck, pelvis) / pixels_per_cm

#     arm_length_cm = get_distance(keypoints[L_SHOULDER], keypoints[L_WRIST]) / pixels_per_cm

#     knee_mid = ((keypoints[L_KNEE] + keypoints[R_KNEE]) / 2).tolist()
#     neck_to_knee_cm = get_distance(neck, knee_mid) / pixels_per_cm

#     waist_circumference = np.pi * waist_width_cm
#     hip_circumference = waist_circumference / 0.75

#     measurements = {
#         " Shoulder Width (cm)": round(shoulder_width_cm, 2),
#         " Waist Circumference (cm)": round(waist_circumference, 2),
#         " Hip Circumference (cm)": round(hip_circumference, 2),
#         " Torso Length (Neck to Pelvis, cm)": round(torso_length_cm, 2),
#         " Arm Length (Shoulder to Wrist, cm)": round(arm_length_cm, 2),
#         " Neck to Knee Length (cm)": round(neck_to_knee_cm, 2)
#     }

#     return measurements

# # Gradio Interface
# with gr.Blocks() as demo:
#     gr.Markdown("## 🧍 Keypoint-Based Body Measurement Tool")
#     gr.Markdown("Upload a **full-body image** and enter your **height (in cm)** to estimate body measurements using AI-powered keypoint detection.")

#     with gr.Row():
#         with gr.Column():
#             image_input = gr.Image(type="pil", label="πŸ“Έ Upload Image")
#             # height_input = gr.Number(label="πŸ“ Your Height (cm)", value=170)
#             submit_btn = gr.Button("πŸ” Generate Measurements")
#         with gr.Column():
#             height_input = gr.Number(label="πŸ“ Your Height (cm)", value=170)
#             measurement_output = gr.JSON(label="πŸ“ Estimated Measurements")
#             #image_output = gr.Image(type="pil", label="πŸ“Œ Keypoint Overlay")

#     submit_btn.click(fn=process_image, inputs=[image_input, height_input], outputs=measurement_output)

# demo.launch()




import gradio as gr
import json
import base64
import requests
from io import BytesIO
import torch
import numpy as np
import cv2
import os
from PIL import Image
from detectron2.engine import DefaultPredictor
from detectron2.config import get_cfg
from detectron2 import model_zoo

# === Set up Detectron2 model ===
cfg = get_cfg()
cfg.merge_from_file(model_zoo.get_config_file("COCO-Keypoints/keypoint_rcnn_R_50_FPN_3x.yaml"))
cfg.MODEL.WEIGHTS = model_zoo.get_checkpoint_url("COCO-Keypoints/keypoint_rcnn_R_50_FPN_3x.yaml")
cfg.MODEL.DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
predictor = DefaultPredictor(cfg)

# === Utility ===
def get_distance(p1, p2):
    return np.linalg.norm(np.array(p1) - np.array(p2))

# === Keypoint and Measurement Logic ===
def process_image(image, user_height_cm):
    image_cv = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
    outputs = predictor(image_cv)
    instances = outputs["instances"]
    keypoints = instances.pred_keypoints.cpu().numpy().tolist() if instances.has("pred_keypoints") else None

    if not keypoints:
        return "No keypoints detected.", None, None

    keypoints = np.array(keypoints[0])[:, :2]

    # Draw keypoints and skeleton
    skeleton = [(5, 6), (5, 11), (6, 12), (11, 12)]
    for x, y in keypoints:
        cv2.circle(image_cv, (int(x), int(y)), 5, (0, 255, 0), -1)
    for pt1, pt2 in skeleton:
        x1, y1 = map(int, keypoints[pt1])
        x2, y2 = map(int, keypoints[pt2])
        cv2.line(image_cv, (x1, y1), (x2, y2), (255, 0, 0), 2)

    # Body part indices
    NOSE, L_SHOULDER, R_SHOULDER = 0, 5, 6
    L_WRIST, L_HIP, R_HIP, L_ANKLE, R_ANKLE = 9, 11, 12, 15, 16

    ankle_mid = ((keypoints[L_ANKLE] + keypoints[R_ANKLE]) / 2).tolist()
    pixel_height = get_distance(keypoints[NOSE], ankle_mid)
    estimated_full_pixel_height = pixel_height / 0.87
    pixels_per_cm = estimated_full_pixel_height / user_height_cm

    shoulder_width_cm = get_distance(keypoints[L_SHOULDER], keypoints[R_SHOULDER]) / pixels_per_cm
    waist_width_cm = get_distance(keypoints[L_HIP], keypoints[R_HIP]) / pixels_per_cm
    pelvis = ((keypoints[L_HIP] + keypoints[R_HIP]) / 2).tolist()
    neck = ((keypoints[L_SHOULDER] + keypoints[R_SHOULDER]) / 2).tolist()
    torso_length_cm = get_distance(neck, pelvis) / pixels_per_cm
    arm_length_cm = get_distance(keypoints[L_SHOULDER], keypoints[L_WRIST]) / pixels_per_cm

    waist_circumference = np.pi * waist_width_cm
    hip_circumference = waist_circumference / 0.75

    measurements = {
        "Waist Circumference (cm)": round(waist_circumference, 2),
        "Hip Circumference (cm)": round(hip_circumference, 2),
        "Shoulder Width (cm)": round(shoulder_width_cm, 2),
        "Torso Length (Neck to Pelvis, cm)": round(torso_length_cm, 2),
        "Full Arm Length (Shoulder to Wrist, cm)": round(arm_length_cm, 2),
    }

    return measurements, cv2.cvtColor(image_cv, cv2.COLOR_BGR2RGB), keypoints.tolist()

# === Save to DB ===
# def save_to_database(measurements, image, user_height_cm, user_id):
#     if not user_id:
#         return "❌ user_id missing from URL."

#     if measurements is None or image is None:
#         return "⚠️ No data to save."

#     buffered = BytesIO()
#     pil_image = Image.fromarray(image)
#     pil_image.save(buffered, format="JPEG")
#     img_str = base64.b64encode(buffered.getvalue()).decode("utf-8")

#     payload = {
#         "imageBase64": img_str,
#         "heightCm": user_height_cm,
#         "measurements": measurements
#     }

    # try:
    #     response = requests.post(
    #         f"https://9d68-210-212-162-140.ngrok-free.app/upload/{user_id}",
    #         json=payload
    #     )
    #     if response.status_code == 201:
    #         return "βœ… Measurements and image saved to database!"
    #     else:
    #         return f"❌ Failed: {response.status_code} - {response.text}"
    # except Exception as e:
    #     return f"⚠️ Error during save: {str(e)}"



def save_to_database(measurements, image, user_height_cm, user_id):
    if not user_id:
        return "❌ user_id missing from URL."
    if measurements is None or image is None:
        return "⚠️ No data to save."

    buffered = BytesIO()
    pil_image = Image.fromarray(image)
    pil_image.save(buffered, format="JPEG")
    img_str = base64.b64encode(buffered.getvalue()).decode("utf-8")

    payload = {
        "imageBase64": img_str,
        "heightCm": user_height_cm,
        "waistCircumferenceCm": measurements.get("Waist Circumference (cm)"),
        "hipcircumference": measurements.get("Hip Circumference (cm)"),
        "shoulderwidth": measurements.get("Shoulder Width (cm)"),
        "torsolength": measurements.get("Torso Length (Neck to Pelvis, cm)"),
        "fullarmlength": measurements.get("Full Arm Length (Shoulder to Wrist, cm)"),
    }

    try:
        response = requests.post(
            f"  https://68be601de1e4.ngrok-free.app/upload/{user_id}",
            json=payload
        )
        if response.status_code == 201:
            return "βœ… Measurements and image saved to database!"
        else:
            return f"❌ Failed: {response.status_code} - {response.text}"
    except Exception as e:
        return f"⚠️ Error during save: {str(e)}"


# === Gradio App ===
with gr.Blocks() as demo:
    gr.Markdown("# πŸ“ AI-Powered Body Measurement Tool")
    user_id_state = gr.State()

    @demo.load(inputs=None, outputs=[user_id_state])
    def load_user_id(request: gr.Request):
        return request.query_params.get("user_id", "")

    with gr.Row():
        with gr.Column():
            image_input = gr.Image(label="Upload Your Full Body Image", type="pil")
            height_input = gr.Number(label="Your Real Height (in cm)")
            process_button = gr.Button("πŸ“ Extract Measurements")

        with gr.Column():
            output_image = gr.Image(label="Detected Keypoints")
            measurement_output = gr.JSON(label="Body Measurements")

    with gr.Row():
        save_button = gr.Button("πŸ’Ύ Save to Backend")
        save_status = gr.Textbox(label="Status", interactive=False)

    # Store results for save
    processed_img = gr.State()
    processed_data = gr.State()

    process_button.click(
        fn=process_image,
        inputs=[image_input, height_input],
        outputs=[measurement_output, output_image, processed_data]
    ).then(
        fn=lambda img: img,
        inputs=[output_image],
        outputs=processed_img
    )

    save_button.click(
        fn=save_to_database,
        inputs=[measurement_output, processed_img, height_input, user_id_state],
        outputs=save_status
    )

if __name__ == "__main__":
    demo.launch()