Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -1,231 +1,639 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import pandas as pd
|
2 |
import numpy as np
|
3 |
-
from sklearn.
|
4 |
-
from sklearn.
|
5 |
-
from sklearn.
|
6 |
-
|
7 |
-
from sklearn.multioutput import MultiOutputRegressor
|
8 |
-
import joblib
|
9 |
import logging
|
10 |
-
|
11 |
-
from typing import Tuple, Dict, Any
|
12 |
|
13 |
-
#
|
14 |
-
|
15 |
-
|
|
|
|
|
|
|
|
|
16 |
|
17 |
-
#
|
18 |
-
logging.basicConfig(
|
|
|
|
|
|
|
19 |
logger = logging.getLogger(__name__)
|
20 |
|
21 |
-
|
|
|
|
|
22 |
def __init__(self):
|
23 |
-
self.
|
24 |
-
self.
|
25 |
-
self.
|
26 |
-
self.
|
27 |
-
self.
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
def create_polynomial_features(self, X: pd.DataFrame) -> np.ndarray:
|
32 |
-
"""Create polynomial features up to degree 2 for better prediction."""
|
33 |
-
if self.poly_features is None:
|
34 |
-
self.poly_features = PolynomialFeatures(degree=2, include_bias=False)
|
35 |
-
return self.poly_features.fit_transform(X)
|
36 |
-
return self.poly_features.transform(X)
|
37 |
-
|
38 |
-
def preprocess_data(self, data: pd.DataFrame) -> Tuple[np.ndarray, pd.DataFrame]:
|
39 |
-
"""Preprocess the data with enhanced feature engineering."""
|
40 |
-
# Add BMI as a derived feature
|
41 |
-
data['BMI'] = data['Weight'] / ((data['TotalHeight'] / 100) ** 2)
|
42 |
-
|
43 |
-
# Create feature ratios
|
44 |
-
data['Chest_Height_Ratio'] = data['ChestWidth'] / data['TotalHeight']
|
45 |
-
data['Waist_Height_Ratio'] = data['Waist'] / data['TotalHeight']
|
46 |
-
|
47 |
-
# Define features for prediction
|
48 |
-
self.feature_columns = ['TotalHeight', 'BMI', 'Chest_Height_Ratio', 'Waist_Height_Ratio']
|
49 |
-
X = data[self.feature_columns]
|
50 |
-
|
51 |
-
# Create polynomial features
|
52 |
-
X_poly = self.create_polynomial_features(X)
|
53 |
-
|
54 |
-
# Scale features
|
55 |
-
if self.scaler is None:
|
56 |
-
self.scaler = StandardScaler()
|
57 |
-
X_scaled = self.scaler.fit_transform(X_poly)
|
58 |
-
else:
|
59 |
-
X_scaled = self.scaler.transform(X_poly)
|
60 |
-
|
61 |
-
# Prepare target variables
|
62 |
-
y = data.drop(columns=self.feature_columns + ['BMI'])
|
63 |
-
|
64 |
-
return X_scaled, y
|
65 |
-
|
66 |
-
def train_model(self, data: pd.DataFrame) -> None:
|
67 |
-
"""Train the model with enhanced validation and ensemble methods."""
|
68 |
-
logger.info("Starting model training...")
|
69 |
-
|
70 |
-
# Preprocess data
|
71 |
-
X_scaled, y = self.preprocess_data(data)
|
72 |
-
self.y_columns = y.columns
|
73 |
-
|
74 |
-
# Encode categorical variables
|
75 |
-
self.label_encoder = LabelEncoder()
|
76 |
-
y['Size'] = self.label_encoder.fit_transform(y['Size'])
|
77 |
-
|
78 |
-
# Split data
|
79 |
-
X_train, X_test, y_train, y_test = train_test_split(
|
80 |
-
X_scaled, y, test_size=0.2, random_state=42
|
81 |
-
)
|
82 |
-
|
83 |
-
# Create ensemble of models
|
84 |
-
base_models = [
|
85 |
-
GradientBoostingRegressor(
|
86 |
-
n_estimators=100,
|
87 |
-
learning_rate=0.1,
|
88 |
-
max_depth=5,
|
89 |
-
random_state=42
|
90 |
-
),
|
91 |
-
RandomForestRegressor(
|
92 |
-
n_estimators=100,
|
93 |
-
max_depth=10,
|
94 |
-
random_state=42
|
95 |
-
)
|
96 |
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
97 |
|
98 |
-
#
|
99 |
-
|
100 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
101 |
|
102 |
-
|
103 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
104 |
|
105 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
106 |
|
107 |
-
|
108 |
-
|
109 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
110 |
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
'mse': mean_squared_error(y_test.iloc[:, i], y_pred[:, i]),
|
116 |
-
'mae': mean_absolute_error(y_test.iloc[:, i], y_pred[:, i])
|
117 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
118 |
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
123 |
|
124 |
-
|
125 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
126 |
# Prepare input features
|
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 |
-
return
|
156 |
|
157 |
-
# Initialize predictor as a global variable
|
158 |
-
predictor = EnhancedBodyMeasurementPredictor()
|
159 |
|
160 |
-
def gradio_predict(total_height: float, weight: float = None):
|
161 |
-
result = predictor.predict(total_height, weight)
|
162 |
-
return result
|
163 |
|
164 |
-
|
165 |
-
|
166 |
-
|
|
|
|
|
|
|
|
|
167 |
try:
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
)
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
"
|
184 |
-
"RecommendedSize": recommended_size,
|
185 |
-
"SizeDetails": size_details,
|
186 |
-
"Fit": computed_fit,
|
187 |
-
"PredictedMeasurements": prediction
|
188 |
}
|
189 |
-
|
190 |
-
return
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
data = pd.read_csv("./data/bdm.csv")
|
198 |
-
data = data.dropna()
|
199 |
-
predictor.train_model(data)
|
200 |
-
logger.info("Model initialization completed successfully")
|
201 |
-
except Exception as e:
|
202 |
-
logger.error(f"Error during model initialization: {str(e)}")
|
203 |
-
raise
|
204 |
-
|
205 |
-
# Create Gradio interfaces
|
206 |
-
predict_interface = gr.Interface(
|
207 |
-
fn=gradio_predict,
|
208 |
-
inputs=[
|
209 |
-
gr.Number(label="Total Height (cm)"),
|
210 |
-
gr.Number(label="Weight (kg)")
|
211 |
-
],
|
212 |
-
outputs="json",
|
213 |
-
title="Body Measurement Prediction"
|
214 |
-
)
|
215 |
|
216 |
-
predict_important_interface = gr.Interface(
|
217 |
-
fn=gradio_predict_important,
|
218 |
-
inputs=[
|
219 |
-
gr.Number(label="Total Height (cm)"),
|
220 |
-
gr.Number(label="Weight (kg)"),
|
221 |
-
gr.Textbox(label="Fit Type")
|
222 |
-
],
|
223 |
-
outputs="json",
|
224 |
-
title="Important Body Measurement Prediction"
|
225 |
-
)
|
226 |
|
227 |
-
#
|
228 |
-
gr.
|
229 |
-
|
230 |
-
|
231 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
Comprehensive Body Measurements Predictor with Brand Comparisons
|
3 |
+
Created by: RohanVashisht1234
|
4 |
+
Created on: 2025-02-22 21:26:00 UTC
|
5 |
+
|
6 |
+
This application provides:
|
7 |
+
1. Body measurements predictions based on height
|
8 |
+
2. Brand-specific size recommendations
|
9 |
+
"""
|
10 |
+
|
11 |
+
import gradio as gr
|
12 |
import pandas as pd
|
13 |
import numpy as np
|
14 |
+
from sklearn.preprocessing import LabelEncoder, StandardScaler
|
15 |
+
from sklearn.ensemble import RandomForestRegressor, RandomForestClassifier
|
16 |
+
from sklearn.metrics import r2_score, accuracy_score
|
17 |
+
import json
|
|
|
|
|
18 |
import logging
|
19 |
+
from datetime import datetime
|
|
|
20 |
|
21 |
+
# Constants
|
22 |
+
# Constants
|
23 |
+
CURRENT_TIME = "2025-02-22 21:39:09"
|
24 |
+
CURRENT_USER = "RohanVashisht1234"
|
25 |
+
CSV_PATH = 'bdm.csv'
|
26 |
+
SHIRT_SIZE_CHARTS_PATH = 'shirt_size_charts.json'
|
27 |
+
PANTS_SIZE_CHARTS_PATH = 'pants_size_charts.json'
|
28 |
|
29 |
+
# Set up logging
|
30 |
+
logging.basicConfig(
|
31 |
+
level=logging.INFO,
|
32 |
+
format='%(asctime)s - %(levelname)s - %(message)s'
|
33 |
+
)
|
34 |
logger = logging.getLogger(__name__)
|
35 |
|
36 |
+
|
37 |
+
|
38 |
+
class ShirtPredictor:
|
39 |
def __init__(self):
|
40 |
+
self.models = {}
|
41 |
+
self.encoders = {}
|
42 |
+
self.scaler = StandardScaler()
|
43 |
+
self.categorical_columns = ['Size', 'Fit']
|
44 |
+
self.numerical_columns = [
|
45 |
+
'ChestWidth', 'ShoulderWidth', 'ArmLength',
|
46 |
+
'ShoulderToWaist', 'Belly', 'Waist'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
47 |
]
|
48 |
+
self.accuracies = {}
|
49 |
+
self.units = {
|
50 |
+
'ChestWidth': 'cm',
|
51 |
+
'ShoulderWidth': 'cm',
|
52 |
+
'ArmLength': 'cm',
|
53 |
+
'ShoulderToWaist': 'cm',
|
54 |
+
'Belly': 'cm',
|
55 |
+
'Waist': 'cm',
|
56 |
+
'Weight': 'kg',
|
57 |
+
'TotalHeight': 'cm'
|
58 |
+
}
|
59 |
+
|
60 |
+
def train(self):
|
61 |
+
logger.info("Loading and preparing data...")
|
62 |
+
df = pd.read_csv(CSV_PATH)
|
63 |
|
64 |
+
# Prepare input features
|
65 |
+
base_features = ['TotalHeight']
|
66 |
+
X = df[base_features].values
|
67 |
+
self.scaler.fit(X)
|
68 |
+
X_scaled = self.scaler.transform(X)
|
69 |
+
|
70 |
+
# Train models for categorical columns
|
71 |
+
for col in self.categorical_columns:
|
72 |
+
self.encoders[col] = LabelEncoder()
|
73 |
+
encoded_values = self.encoders[col].fit_transform(df[col])
|
74 |
+
self.models[col] = RandomForestClassifier(n_estimators=100, random_state=42)
|
75 |
+
self.models[col].fit(X_scaled, encoded_values)
|
76 |
+
predictions = self.models[col].predict(X_scaled)
|
77 |
+
self.accuracies[col] = round(accuracy_score(encoded_values, predictions), 4)
|
78 |
+
|
79 |
+
# Train models for numerical columns
|
80 |
+
for col in self.numerical_columns:
|
81 |
+
self.models[col] = RandomForestRegressor(n_estimators=100, random_state=42)
|
82 |
+
self.models[col].fit(X_scaled, df[col])
|
83 |
+
predictions = self.models[col].predict(X_scaled)
|
84 |
+
self.accuracies[col] = round(r2_score(df[col], predictions), 4)
|
85 |
+
|
86 |
+
logger.info("Training completed successfully")
|
87 |
+
|
88 |
+
def predict(self, height, weight=None, body_type=None):
|
89 |
+
features = np.array([[height]])
|
90 |
+
features_scaled = self.scaler.transform(features)
|
91 |
|
92 |
+
predictions = {
|
93 |
+
"input": {
|
94 |
+
"height": float(height),
|
95 |
+
"unit": "cm",
|
96 |
+
"timestamp_utc": CURRENT_TIME,
|
97 |
+
"user": CURRENT_USER
|
98 |
+
},
|
99 |
+
"shirt_predictions": {},
|
100 |
+
"model_accuracies": {}
|
101 |
+
}
|
102 |
+
|
103 |
+
if weight is not None:
|
104 |
+
predictions["input"]["weight"] = float(weight)
|
105 |
+
predictions["input"]["weight_unit"] = "kg"
|
106 |
+
if body_type is not None:
|
107 |
+
predictions["input"]["body_type"] = body_type
|
108 |
+
|
109 |
+
# Predict categorical values
|
110 |
+
for col in self.categorical_columns:
|
111 |
+
pred = self.encoders[col].inverse_transform(
|
112 |
+
self.models[col].predict(features_scaled)
|
113 |
+
)[0]
|
114 |
+
predictions["shirt_predictions"][col] = {
|
115 |
+
"value": str(pred)
|
116 |
+
}
|
117 |
+
predictions["model_accuracies"][col] = self.accuracies[col]
|
118 |
|
119 |
+
# Predict numerical values
|
120 |
+
for col in self.numerical_columns:
|
121 |
+
pred = self.models[col].predict(features_scaled)[0]
|
122 |
+
predictions["shirt_predictions"][col] = {
|
123 |
+
"value": round(float(pred), 2),
|
124 |
+
"unit": self.units.get(col, "")
|
125 |
+
}
|
126 |
+
predictions["model_accuracies"][col] = self.accuracies[col]
|
127 |
+
|
128 |
+
return predictions
|
129 |
+
|
130 |
|
131 |
+
|
132 |
+
class BrandSizePredictor:
|
133 |
+
def __init__(self):
|
134 |
+
with open(SHIRT_SIZE_CHARTS_PATH, 'r') as f:
|
135 |
+
self.brand_charts = json.load(f)
|
136 |
+
|
137 |
+
def find_matching_sizes(self, measurements):
|
138 |
+
results = {
|
139 |
+
"input_measurements": {
|
140 |
+
"chest": measurements["chest"],
|
141 |
+
"waist": measurements["waist"],
|
142 |
+
"shoulder": measurements["shoulder"],
|
143 |
+
"unit": "cm"
|
144 |
+
},
|
145 |
+
"brand_recommendations": [],
|
146 |
+
"timestamp_utc": CURRENT_TIME,
|
147 |
+
"user": CURRENT_USER
|
148 |
+
}
|
149 |
|
150 |
+
for brand in self.brand_charts:
|
151 |
+
brand_result = {
|
152 |
+
"brand": brand["brand"],
|
153 |
+
"matching_sizes": []
|
|
|
|
|
154 |
}
|
155 |
+
|
156 |
+
for size in brand["sizes"]:
|
157 |
+
if (size["chest"][0] <= measurements["chest"] <= size["chest"][1] and
|
158 |
+
size["waist"][0] <= measurements["waist"] <= size["waist"][1] and
|
159 |
+
size["shoulder"][0] <= measurements["shoulder"] <= size["shoulder"][1]):
|
160 |
+
|
161 |
+
brand_result["matching_sizes"].append({
|
162 |
+
"size": size["label"],
|
163 |
+
"fit_details": {
|
164 |
+
"chest_range": size["chest"],
|
165 |
+
"waist_range": size["waist"],
|
166 |
+
"shoulder_range": size["shoulder"]
|
167 |
+
}
|
168 |
+
})
|
169 |
+
|
170 |
+
if brand_result["matching_sizes"]:
|
171 |
+
results["brand_recommendations"].append(brand_result)
|
172 |
|
173 |
+
return results
|
174 |
+
|
175 |
+
|
176 |
+
|
177 |
+
# Initialize predictors
|
178 |
+
shirt_predictor = ShirtPredictor()
|
179 |
+
shirt_predictor.train()
|
180 |
+
brand_predictor = BrandSizePredictor()
|
181 |
+
|
182 |
+
def predict_shirt_measurements(height, weight=None, body_type=None):
|
183 |
+
"""Gradio interface function for shirt predictions"""
|
184 |
+
try:
|
185 |
+
predictions = shirt_predictor.predict(height, weight, body_type)
|
186 |
+
return json.dumps(predictions, indent=2)
|
187 |
+
except Exception as e:
|
188 |
+
return json.dumps({
|
189 |
+
"error": str(e),
|
190 |
+
"timestamp_utc": CURRENT_TIME,
|
191 |
+
"user": CURRENT_USER
|
192 |
+
}, indent=2)
|
193 |
+
|
194 |
+
def predict_brand_sizes(chest, waist, shoulder):
|
195 |
+
"""Gradio interface function for brand size predictions"""
|
196 |
+
try:
|
197 |
+
measurements = {
|
198 |
+
"chest": float(chest),
|
199 |
+
"waist": float(waist),
|
200 |
+
"shoulder": float(shoulder)
|
201 |
+
}
|
202 |
+
predictions = brand_predictor.find_matching_sizes(measurements)
|
203 |
+
return json.dumps(predictions, indent=2)
|
204 |
+
except Exception as e:
|
205 |
+
return json.dumps({
|
206 |
+
"error": str(e),
|
207 |
+
"timestamp_utc": CURRENT_TIME,
|
208 |
+
"user": CURRENT_USER
|
209 |
+
}, indent=2)
|
210 |
+
|
211 |
+
|
212 |
+
|
213 |
|
214 |
+
# pants
|
215 |
+
class PantsPredictor:
|
216 |
+
def __init__(self):
|
217 |
+
self.models = {}
|
218 |
+
self.encoders = {}
|
219 |
+
self.scaler = StandardScaler()
|
220 |
+
self.categorical_columns = ['Size', 'Fit']
|
221 |
+
self.numerical_columns = [
|
222 |
+
'Waist', 'Hips', 'LegLength',
|
223 |
+
'WaistToKnee', 'Belly'
|
224 |
+
]
|
225 |
+
self.accuracies = {}
|
226 |
+
self.units = {
|
227 |
+
'Waist': 'cm',
|
228 |
+
'Hips': 'cm',
|
229 |
+
'LegLength': 'cm',
|
230 |
+
'WaistToKnee': 'cm',
|
231 |
+
'Belly': 'cm',
|
232 |
+
'Weight': 'kg',
|
233 |
+
'TotalHeight': 'cm'
|
234 |
+
}
|
235 |
+
|
236 |
+
def train(self):
|
237 |
+
logger.info("Loading and preparing pants prediction models...")
|
238 |
+
df = pd.read_csv(CSV_PATH)
|
239 |
+
|
240 |
# Prepare input features
|
241 |
+
base_features = ['TotalHeight']
|
242 |
+
X = df[base_features].values
|
243 |
+
self.scaler.fit(X)
|
244 |
+
X_scaled = self.scaler.transform(X)
|
245 |
+
|
246 |
+
# Train models for categorical columns
|
247 |
+
for col in self.categorical_columns:
|
248 |
+
self.encoders[col] = LabelEncoder()
|
249 |
+
encoded_values = self.encoders[col].fit_transform(df[col])
|
250 |
+
self.models[col] = RandomForestClassifier(n_estimators=100, random_state=42)
|
251 |
+
self.models[col].fit(X_scaled, encoded_values)
|
252 |
+
predictions = self.models[col].predict(X_scaled)
|
253 |
+
self.accuracies[col] = round(accuracy_score(encoded_values, predictions), 4)
|
254 |
+
|
255 |
+
# Train models for numerical columns
|
256 |
+
for col in self.numerical_columns:
|
257 |
+
self.models[col] = RandomForestRegressor(n_estimators=100, random_state=42)
|
258 |
+
self.models[col].fit(X_scaled, df[col])
|
259 |
+
predictions = self.models[col].predict(X_scaled)
|
260 |
+
self.accuracies[col] = round(r2_score(df[col], predictions), 4)
|
261 |
+
|
262 |
+
logger.info("Pants prediction models trained successfully")
|
263 |
+
|
264 |
+
def predict(self, height, weight=None, body_type=None):
|
265 |
+
features = np.array([[height]])
|
266 |
+
features_scaled = self.scaler.transform(features)
|
267 |
|
268 |
+
predictions = {
|
269 |
+
"input": {
|
270 |
+
"height": float(height),
|
271 |
+
"unit": "cm",
|
272 |
+
"timestamp_utc": CURRENT_TIME,
|
273 |
+
"user": CURRENT_USER
|
274 |
+
},
|
275 |
+
"pants_predictions": {},
|
276 |
+
"model_accuracies": {}
|
277 |
+
}
|
278 |
|
279 |
+
if weight is not None:
|
280 |
+
predictions["input"]["weight"] = float(weight)
|
281 |
+
predictions["input"]["weight_unit"] = "kg"
|
282 |
+
if body_type is not None:
|
283 |
+
predictions["input"]["body_type"] = body_type
|
284 |
|
285 |
+
# Predict categorical values
|
286 |
+
for col in self.categorical_columns:
|
287 |
+
pred = self.encoders[col].inverse_transform(
|
288 |
+
self.models[col].predict(features_scaled)
|
289 |
+
)[0]
|
290 |
+
predictions["pants_predictions"][col] = {
|
291 |
+
"value": str(pred)
|
292 |
+
}
|
293 |
+
predictions["model_accuracies"][col] = self.accuracies[col]
|
294 |
|
295 |
+
# Predict numerical values
|
296 |
+
for col in self.numerical_columns:
|
297 |
+
pred = self.models[col].predict(features_scaled)[0]
|
298 |
+
predictions["pants_predictions"][col] = {
|
299 |
+
"value": round(float(pred), 2),
|
300 |
+
"unit": self.units.get(col, "")
|
301 |
+
}
|
302 |
+
predictions["model_accuracies"][col] = self.accuracies[col]
|
303 |
+
|
304 |
+
return predictions
|
305 |
+
|
306 |
+
class PantsSizePredictor:
|
307 |
+
def __init__(self, size_charts_path='/Users/rohanvashisht/Hackx/pants_size_charts.json'):
|
308 |
+
try:
|
309 |
+
with open(size_charts_path, 'r') as f:
|
310 |
+
self.brand_charts = json.load(f)
|
311 |
+
logger.info(f"Successfully loaded {len(self.brand_charts)} brands from size charts")
|
312 |
+
# Add debug logging for loaded size charts
|
313 |
+
for brand in self.brand_charts:
|
314 |
+
logger.info(f"Loaded size chart for {brand['brand']}")
|
315 |
+
except Exception as e:
|
316 |
+
logger.error(f"Failed to load pants size charts: {str(e)}")
|
317 |
+
self.brand_charts = []
|
318 |
+
|
319 |
+
def find_matching_sizes(self, measurements):
|
320 |
+
logger.info(f"Finding sizes for measurements: {measurements}")
|
321 |
+
results = {
|
322 |
+
"input_measurements": {
|
323 |
+
"waist": measurements["waist"],
|
324 |
+
"hips": measurements["hips"],
|
325 |
+
"leg_length": measurements["leg_length"],
|
326 |
+
"unit": "cm",
|
327 |
+
"timestamp_utc": CURRENT_TIME,
|
328 |
+
"user": CURRENT_USER
|
329 |
+
},
|
330 |
+
"brand_recommendations": []
|
331 |
+
}
|
332 |
+
|
333 |
+
for brand in self.brand_charts:
|
334 |
+
brand_result = {
|
335 |
+
"brand": brand["brand"],
|
336 |
+
"matching_sizes": []
|
337 |
+
}
|
338 |
+
|
339 |
+
for size in brand["sizes"]:
|
340 |
+
# Add tolerance of ±2cm for better matching
|
341 |
+
waist_min = size["waist"][0] - 2
|
342 |
+
waist_max = size["waist"][1] + 2
|
343 |
+
hips_min = size["hips"][0] - 2
|
344 |
+
hips_max = size["hips"][1] + 2
|
345 |
+
leg_min = size["leg_length"][0] - 2
|
346 |
+
leg_max = size["leg_length"][1] + 2
|
347 |
+
|
348 |
+
# Debug logging for size checks
|
349 |
+
logger.debug(f"""
|
350 |
+
Checking {brand['brand']} size {size['label']}:
|
351 |
+
Waist: {measurements['waist']} in range {waist_min}-{waist_max}
|
352 |
+
Hips: {measurements['hips']} in range {hips_min}-{hips_max}
|
353 |
+
Leg: {measurements['leg_length']} in range {leg_min}-{leg_max}
|
354 |
+
""")
|
355 |
+
|
356 |
+
# Check if measurements fall within the size ranges (with tolerance)
|
357 |
+
if (waist_min <= measurements["waist"] <= waist_max and
|
358 |
+
hips_min <= measurements["hips"] <= hips_max and
|
359 |
+
leg_min <= measurements["leg_length"] <= leg_max):
|
360 |
+
|
361 |
+
size_match = {
|
362 |
+
"size": size["label"],
|
363 |
+
"fit_details": {
|
364 |
+
"waist_range": f"{size['waist'][0]}-{size['waist'][1]} cm",
|
365 |
+
"hips_range": f"{size['hips'][0]}-{size['hips'][1]} cm",
|
366 |
+
"leg_length_range": f"{size['leg_length'][0]}-{size['leg_length'][1]} cm"
|
367 |
+
},
|
368 |
+
"fit_quality": {
|
369 |
+
"waist": "Perfect" if size["waist"][0] <= measurements["waist"] <= size["waist"][1] else "Slightly loose/tight",
|
370 |
+
"hips": "Perfect" if size["hips"][0] <= measurements["hips"] <= size["hips"][1] else "Slightly loose/tight",
|
371 |
+
"leg_length": "Perfect" if size["leg_length"][0] <= measurements["leg_length"] <= size["leg_length"][1] else "Slightly long/short"
|
372 |
+
}
|
373 |
+
}
|
374 |
+
brand_result["matching_sizes"].append(size_match)
|
375 |
+
logger.info(f"Found matching size {size['label']} for {brand['brand']}")
|
376 |
+
|
377 |
+
if brand_result["matching_sizes"]:
|
378 |
+
results["brand_recommendations"].append(brand_result)
|
379 |
|
380 |
+
# Add debugging information
|
381 |
+
if not results["brand_recommendations"]:
|
382 |
+
logger.warning(f"No matching sizes found for measurements: {measurements}")
|
383 |
+
results["debug_info"] = {
|
384 |
+
"message": "No exact matches found. Consider these suggestions:",
|
385 |
+
"suggestions": [
|
386 |
+
"Try measurements within these ranges:",
|
387 |
+
"Waist: 72-91 cm",
|
388 |
+
"Hips: 90-110 cm",
|
389 |
+
"Leg Length: 97-106 cm"
|
390 |
+
],
|
391 |
+
"closest_matches": self._find_closest_matches(measurements)
|
392 |
+
}
|
393 |
|
394 |
+
return results
|
395 |
+
|
396 |
+
def _find_closest_matches(self, measurements):
|
397 |
+
closest_matches = []
|
398 |
+
for brand in self.brand_charts:
|
399 |
+
for size in brand["sizes"]:
|
400 |
+
# Calculate how close this size is to the measurements
|
401 |
+
waist_diff = min(abs(measurements["waist"] - size["waist"][0]),
|
402 |
+
abs(measurements["waist"] - size["waist"][1]))
|
403 |
+
hips_diff = min(abs(measurements["hips"] - size["hips"][0]),
|
404 |
+
abs(measurements["hips"] - size["hips"][1]))
|
405 |
+
leg_diff = min(abs(measurements["leg_length"] - size["leg_length"][0]),
|
406 |
+
abs(measurements["leg_length"] - size["leg_length"][1]))
|
407 |
+
|
408 |
+
if waist_diff <= 5 and hips_diff <= 5 and leg_diff <= 5:
|
409 |
+
closest_matches.append({
|
410 |
+
"brand": brand["brand"],
|
411 |
+
"size": size["label"],
|
412 |
+
"adjustments_needed": {
|
413 |
+
"waist": f"{waist_diff:+.1f} cm",
|
414 |
+
"hips": f"{hips_diff:+.1f} cm",
|
415 |
+
"leg_length": f"{leg_diff:+.1f} cm"
|
416 |
+
}
|
417 |
+
})
|
418 |
|
419 |
+
return closest_matches[:3] # Return top 3 closest matches
|
420 |
|
|
|
|
|
421 |
|
|
|
|
|
|
|
422 |
|
423 |
+
# Initialize pants predictors
|
424 |
+
pants_predictor = PantsPredictor()
|
425 |
+
pants_predictor.train()
|
426 |
+
pants_size_predictor = PantsSizePredictor()
|
427 |
+
|
428 |
+
def predict_pants_measurements(height, weight=None, body_type=None):
|
429 |
+
"""Gradio interface function for pants predictions based on height"""
|
430 |
try:
|
431 |
+
predictions = pants_predictor.predict(height, weight, body_type)
|
432 |
+
return json.dumps(predictions, indent=2)
|
433 |
+
except Exception as e:
|
434 |
+
return json.dumps({
|
435 |
+
"error": str(e),
|
436 |
+
"timestamp_utc": CURRENT_TIME,
|
437 |
+
"user": CURRENT_USER
|
438 |
+
}, indent=2)
|
439 |
+
|
440 |
+
def predict_pants_sizes(waist, leg_length, hips):
|
441 |
+
"""Gradio interface function for pants size predictions"""
|
442 |
+
try:
|
443 |
+
measurements = {
|
444 |
+
"waist": float(waist),
|
445 |
+
"leg_length": float(leg_length),
|
446 |
+
"hips": float(hips)
|
|
|
|
|
|
|
|
|
447 |
}
|
448 |
+
predictions = pants_size_predictor.find_matching_sizes(measurements)
|
449 |
+
return json.dumps(predictions, indent=2)
|
450 |
+
except Exception as e:
|
451 |
+
return json.dumps({
|
452 |
+
"error": str(e),
|
453 |
+
"timestamp_utc": CURRENT_TIME,
|
454 |
+
"user": CURRENT_USER
|
455 |
+
}, indent=2)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
456 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
457 |
|
458 |
+
# Create Gradio interface with tabs
|
459 |
+
with gr.Blocks(title="Body Measurements Predictor") as demo:
|
460 |
+
gr.Markdown(f"""
|
461 |
+
# Body Measurements Predictor
|
462 |
+
Created by: {CURRENT_USER}
|
463 |
+
Last Updated: {CURRENT_TIME}
|
464 |
+
""")
|
465 |
+
|
466 |
+
with gr.Tabs():
|
467 |
+
# First Tab - Shirt Measurements
|
468 |
+
with gr.Tab("Shirt Measurements"):
|
469 |
+
with gr.Row():
|
470 |
+
with gr.Column():
|
471 |
+
height_input = gr.Number(
|
472 |
+
label="Height (cm) *",
|
473 |
+
minimum=50,
|
474 |
+
maximum=250,
|
475 |
+
step=1,
|
476 |
+
value=170
|
477 |
+
)
|
478 |
+
weight_input = gr.Number(
|
479 |
+
label="Weight (kg) (optional)",
|
480 |
+
minimum=30,
|
481 |
+
maximum=200,
|
482 |
+
step=0.1
|
483 |
+
)
|
484 |
+
body_type_input = gr.Dropdown(
|
485 |
+
label="Body Type (optional)",
|
486 |
+
choices=["Slim", "Regular", "Athletic", "Large"],
|
487 |
+
value=None
|
488 |
+
)
|
489 |
+
predict_button = gr.Button("Predict Shirt Measurements")
|
490 |
+
|
491 |
+
with gr.Column():
|
492 |
+
output_json = gr.JSON(label="Shirt Measurements Predictions")
|
493 |
+
|
494 |
+
predict_button.click(
|
495 |
+
fn=predict_shirt_measurements,
|
496 |
+
inputs=[height_input, weight_input, body_type_input],
|
497 |
+
outputs=output_json
|
498 |
+
)
|
499 |
+
|
500 |
+
gr.Markdown("""
|
501 |
+
### Instructions:
|
502 |
+
1. Enter your height (required)
|
503 |
+
2. Optionally enter your weight and select your body type
|
504 |
+
3. Click "Predict Shirt Measurements" to get detailed predictions
|
505 |
+
""")
|
506 |
+
|
507 |
+
# Second Tab - Brand Size Finder (Shirts)
|
508 |
+
with gr.Tab("Shirt Size Finder"):
|
509 |
+
with gr.Row():
|
510 |
+
with gr.Column():
|
511 |
+
chest_input = gr.Number(
|
512 |
+
label="Chest Circumference (cm)",
|
513 |
+
minimum=80,
|
514 |
+
maximum=120,
|
515 |
+
step=0.5,
|
516 |
+
value=95
|
517 |
+
)
|
518 |
+
waist_input = gr.Number(
|
519 |
+
label="Waist Circumference (cm)",
|
520 |
+
minimum=60,
|
521 |
+
maximum=110,
|
522 |
+
step=0.5,
|
523 |
+
value=80
|
524 |
+
)
|
525 |
+
shoulder_input = gr.Number(
|
526 |
+
label="Shoulder Width (cm)",
|
527 |
+
minimum=35,
|
528 |
+
maximum=55,
|
529 |
+
step=0.5,
|
530 |
+
value=43
|
531 |
+
)
|
532 |
+
brand_predict_button = gr.Button("Find Matching Sizes")
|
533 |
+
|
534 |
+
with gr.Column():
|
535 |
+
brand_output_json = gr.JSON(label="Brand Size Recommendations")
|
536 |
+
|
537 |
+
brand_predict_button.click(
|
538 |
+
fn=predict_brand_sizes,
|
539 |
+
inputs=[chest_input, waist_input, shoulder_input],
|
540 |
+
outputs=brand_output_json
|
541 |
+
)
|
542 |
+
|
543 |
+
gr.Markdown("""
|
544 |
+
### Instructions:
|
545 |
+
1. Enter your measurements:
|
546 |
+
- Chest circumference
|
547 |
+
- Waist circumference
|
548 |
+
- Shoulder width
|
549 |
+
2. Click "Find Matching Sizes" to see which sizes fit you across different brands
|
550 |
+
""")
|
551 |
+
|
552 |
+
# Third Tab - Pants Measurements
|
553 |
+
with gr.Tab("Pants Measurements"):
|
554 |
+
with gr.Row():
|
555 |
+
with gr.Column():
|
556 |
+
pants_height_input = gr.Number(
|
557 |
+
label="Height (cm) *",
|
558 |
+
minimum=50,
|
559 |
+
maximum=250,
|
560 |
+
step=1,
|
561 |
+
value=170
|
562 |
+
)
|
563 |
+
pants_weight_input = gr.Number(
|
564 |
+
label="Weight (kg) (optional)",
|
565 |
+
minimum=30,
|
566 |
+
maximum=200,
|
567 |
+
step=0.1
|
568 |
+
)
|
569 |
+
pants_body_type_input = gr.Dropdown(
|
570 |
+
label="Body Type (optional)",
|
571 |
+
choices=["Slim", "Regular", "Athletic", "Large"],
|
572 |
+
value=None
|
573 |
+
)
|
574 |
+
pants_predict_button = gr.Button("Predict Pants Measurements")
|
575 |
+
|
576 |
+
with gr.Column():
|
577 |
+
pants_output_json = gr.JSON(label="Pants Measurements Predictions")
|
578 |
+
|
579 |
+
pants_predict_button.click(
|
580 |
+
fn=predict_pants_measurements,
|
581 |
+
inputs=[pants_height_input, pants_weight_input, pants_body_type_input],
|
582 |
+
outputs=pants_output_json
|
583 |
+
)
|
584 |
+
|
585 |
+
gr.Markdown("""
|
586 |
+
### Instructions:
|
587 |
+
1. Enter your height (required)
|
588 |
+
2. Optionally enter your weight and select your body type
|
589 |
+
3. Click "Predict Pants Measurements" to get detailed predictions
|
590 |
+
""")
|
591 |
+
|
592 |
+
# Fourth Tab - Pants Size Finder
|
593 |
+
with gr.Tab("Pants Size Finder"):
|
594 |
+
with gr.Row():
|
595 |
+
with gr.Column():
|
596 |
+
pants_waist_input = gr.Number(
|
597 |
+
label="Waist Circumference (cm)",
|
598 |
+
minimum=60,
|
599 |
+
maximum=120,
|
600 |
+
step=0.5,
|
601 |
+
value=80
|
602 |
+
)
|
603 |
+
pants_leg_input = gr.Number(
|
604 |
+
label="Leg Length (cm)",
|
605 |
+
minimum=60,
|
606 |
+
maximum=120,
|
607 |
+
step=0.5,
|
608 |
+
value=98
|
609 |
+
)
|
610 |
+
pants_hips_input = gr.Number(
|
611 |
+
label="Hips Circumference (cm)",
|
612 |
+
minimum=80,
|
613 |
+
maximum=140,
|
614 |
+
step=0.5,
|
615 |
+
value=102
|
616 |
+
)
|
617 |
+
pants_brand_predict_button = gr.Button("Find Matching Pants Sizes")
|
618 |
+
|
619 |
+
with gr.Column():
|
620 |
+
pants_brand_output_json = gr.JSON(label="Pants Size Recommendations")
|
621 |
+
|
622 |
+
pants_brand_predict_button.click(
|
623 |
+
fn=predict_pants_sizes,
|
624 |
+
inputs=[pants_waist_input, pants_leg_input, pants_hips_input],
|
625 |
+
outputs=pants_brand_output_json
|
626 |
+
)
|
627 |
+
|
628 |
+
gr.Markdown("""
|
629 |
+
### Instructions:
|
630 |
+
1. Enter your measurements:
|
631 |
+
- Waist circumference
|
632 |
+
- Leg length
|
633 |
+
- Hips circumference
|
634 |
+
2. Click "Find Matching Pants Sizes" to see which sizes fit you across different brands
|
635 |
+
""")
|
636 |
+
|
637 |
+
if __name__ == "__main__":
|
638 |
+
logger.info("Starting Gradio interface...")
|
639 |
+
demo.launch()
|