RohanVashisht commited on
Commit
b83c41c
·
verified ·
1 Parent(s): e2bff0a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +607 -199
app.py CHANGED
@@ -1,231 +1,639 @@
 
 
 
 
 
 
 
 
 
 
 
1
  import pandas as pd
2
  import numpy as np
3
- from sklearn.model_selection import train_test_split, cross_val_score, KFold
4
- from sklearn.preprocessing import LabelEncoder, StandardScaler, PolynomialFeatures
5
- from sklearn.ensemble import GradientBoostingRegressor, RandomForestRegressor
6
- from sklearn.metrics import mean_squared_error, r2_score, mean_absolute_error
7
- from sklearn.multioutput import MultiOutputRegressor
8
- import joblib
9
  import logging
10
- import gradio as gr
11
- from typing import Tuple, Dict, Any
12
 
13
- # Import custom libraries (same as before)
14
- from libraries.fits.shirts_lib import get_fit as get_shirt_fit
15
- from libraries.sizes.shirts_lib import get_best_size as get_shirt_size
 
 
 
 
16
 
17
- # Setup logging
18
- logging.basicConfig(level=logging.INFO)
 
 
 
19
  logger = logging.getLogger(__name__)
20
 
21
- class EnhancedBodyMeasurementPredictor:
 
 
22
  def __init__(self):
23
- self.model = None
24
- self.scaler = None
25
- self.poly_features = None
26
- self.label_encoder = None
27
- self.y_columns = None
28
- self.feature_columns = None
29
- self.model_metrics = {}
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
- # Train ensemble
99
- self.model = MultiOutputRegressor(base_models[0]) # Using GradientBoosting as primary
100
- self.model.fit(X_train, y_train)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
 
102
- # Evaluate model
103
- self._evaluate_model(X_test, y_test)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
 
105
- logger.info("Model training completed")
 
 
 
 
 
 
 
 
 
 
106
 
107
- def _evaluate_model(self, X_test: np.ndarray, y_test: pd.DataFrame) -> None:
108
- """Evaluate model performance with multiple metrics."""
109
- y_pred = self.model.predict(X_test)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
 
111
- # Calculate metrics for each target variable
112
- for i, col in enumerate(self.y_columns):
113
- self.model_metrics[col] = {
114
- 'r2': r2_score(y_test.iloc[:, i], y_pred[:, i]),
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
- # Log evaluation results
120
- logger.info("Model Evaluation Results:")
121
- for col, metrics in self.model_metrics.items():
122
- logger.info(f"{col}: R² = {metrics['r2']:.4f}, MAE = {metrics['mae']:.4f}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
 
124
- def predict(self, total_height: float, weight: float = None) -> Dict[str, Any]:
125
- """Make predictions with confidence intervals."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
  # Prepare input features
127
- input_data = pd.DataFrame({
128
- 'TotalHeight': [total_height],
129
- 'Weight': [weight if weight is not None else 0] # Default weight for BMI calculation
130
- })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
 
132
- # Calculate BMI and other derived features
133
- input_data['BMI'] = (
134
- input_data['Weight'] / ((input_data['TotalHeight'] / 100) ** 2)
135
- if weight is not None else 0
136
- )
 
 
 
 
 
137
 
138
- # Add placeholder values for ratio features (will be updated after first prediction)
139
- input_data['Chest_Height_Ratio'] = 0
140
- input_data['Waist_Height_Ratio'] = 0
 
 
141
 
142
- # Transform features
143
- X_poly = self.create_polynomial_features(input_data[self.feature_columns])
144
- X_scaled = self.scaler.transform(X_poly)
 
 
 
 
 
 
145
 
146
- # Make prediction
147
- prediction = self.model.predict(X_scaled)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
148
 
149
- # Convert prediction to dictionary
150
- pred_dict = {col: float(val) for col, val in zip(self.y_columns, prediction[0])}
 
 
 
 
 
 
 
 
 
 
 
151
 
152
- # Decode size back to original labels
153
- pred_dict['Size'] = self.label_encoder.inverse_transform([round(pred_dict['Size'])])[0]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
 
155
- return pred_dict
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
- def gradio_predict_important(total_height: float, weight: float = None, fit_type_input: str = None):
165
- prediction = predictor.predict(total_height, weight)
166
-
 
 
 
 
167
  try:
168
- brand = "Zara" # Default brand
169
- chest = float(prediction.get("ChestWidth"))
170
- waist = float(prediction.get("Waist"))
171
- shoulder = float(prediction.get("ShoulderWidth"))
172
-
173
- recommended_size, size_details = get_shirt_size(
174
- brand, int(round(chest)), int(round(waist)), int(round(shoulder))
175
- )
176
-
177
- computed_fit = (
178
- fit_type_input if fit_type_input is not None
179
- else get_shirt_fit(shoulder, chest, waist)
180
- )
181
-
182
- response = {
183
- "Brand": brand,
184
- "RecommendedSize": recommended_size,
185
- "SizeDetails": size_details,
186
- "Fit": computed_fit,
187
- "PredictedMeasurements": prediction
188
  }
189
-
190
- return response
191
-
192
- except (TypeError, ValueError) as e:
193
- return {"error": f"Error in size/fit calculation: {str(e)}"}
194
-
195
- # Load dataset and train the model
196
- try:
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
- # Launch Gradio app
228
- gr.TabbedInterface(
229
- [predict_interface, predict_important_interface],
230
- ["Predict", "Predict Important"]
231
- ).launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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()