codernotme commited on
Commit
a5a6a2e
·
verified ·
1 Parent(s): 9f71814
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ lbfmodel.yaml filter=lfs diff=lfs merge=lfs -text
Dockerfile ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use Python 3.10 slim image for compatibility with AI libraries
2
+ FROM python:3.10-slim-bullseye
3
+
4
+ # Set environment variables
5
+ ENV PYTHONDONTWRITEBYTECODE=1
6
+ ENV PYTHONUNBUFFERED=1
7
+ ENV DEBIAN_FRONTEND=noninteractive
8
+
9
+ # Set working directory
10
+ WORKDIR /app
11
+
12
+ # Install system dependencies required for OpenCV
13
+ RUN apt-get update && apt-get install -y --no-install-recommends \
14
+ libgl1-mesa-glx \
15
+ libglib2.0-0 \
16
+ libsm6 \
17
+ libxext6 \
18
+ libxrender-dev \
19
+ wget \
20
+ && rm -rf /var/lib/apt/lists/*
21
+
22
+
23
+
24
+ # Copy requirements and install Python dependencies
25
+ COPY requirements.txt .
26
+ RUN pip install --no-cache-dir -r requirements.txt
27
+
28
+ # Copy the rest of the application code
29
+ COPY . .
30
+
31
+ # Ensure the LBF model is present (download if missing during build)
32
+ # We can run a small python script to check/download it
33
+ RUN python3 -c "import landmarks; landmarks.ensure_model()"
34
+
35
+ # Train the model during build (if dataset is provided in the context)
36
+ RUN if [ -d "dataset" ]; then \
37
+ echo "Dataset found. Training face shape model..." && \
38
+ python3 train_means.py; \
39
+ else \
40
+ echo "No dataset found. Using pre-trained face_shape_means.pkl if available."; \
41
+ fi
42
+
43
+
44
+ # Expose port (HF Spaces uses 7860 by default)
45
+ EXPOSE 7860
46
+
47
+ # Start the server (Railway/Spaces provide PORT env var)
48
+ # We default to 7860 for HF Spaces
49
+ CMD ["sh", "-c", "uvicorn app:app --host 0.0.0.0 --port ${PORT:-7860}"]
50
+
51
+
README.md CHANGED
@@ -1,10 +1,31 @@
1
- ---
2
- title: Kataria Opticals Api
3
- emoji: 📉
4
- colorFrom: blue
5
- colorTo: green
6
- sdk: docker
7
- pinned: false
8
- ---
9
-
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # AI Frame Recommendation Service
2
+
3
+ This service analyzes facial features to recommend eyewear frames.
4
+
5
+ ## Tech Stack
6
+ - Python 3.10+
7
+ - FastAPI
8
+ - OpenCV (Contrib)
9
+ - Scikit-learn
10
+
11
+ ## Run with Docker (Recommended)
12
+ Docker handles all dependencies and ensures the AI models run correctly, avoiding Python version incompatibilities.
13
+
14
+ 1. **Build the Image**
15
+ ```bash
16
+ docker build -t ai_service .
17
+ ```
18
+
19
+ 2. **Run the Container**
20
+ ```bash
21
+ docker run -p 8000:8000 ai_service
22
+ ```
23
+
24
+ The API will be available at `http://localhost:8000`.
25
+
26
+ ## Local Setup (Dev Only)
27
+ If you cannot use Docker, you can run locally, but some AI features might be mocked if your Python version is incompatible (e.g., Python 3.14).
28
+
29
+ ```bash
30
+ ./start_and_setup.sh
31
+ ```
app.py ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, UploadFile, File, HTTPException
2
+ from fastapi.middleware.cors import CORSMiddleware
3
+ from fastapi.responses import JSONResponse
4
+ import uvicorn
5
+ import shutil
6
+ import os
7
+ import uuid
8
+ from pathlib import Path
9
+ import logging
10
+
11
+ from detect import detect_face_shape
12
+ from recommend import get_recommendations
13
+
14
+ # Configure logging
15
+ logging.basicConfig(level=logging.INFO)
16
+ logger = logging.getLogger(__name__)
17
+
18
+ app = FastAPI(title="AI Frame Recommendation Service")
19
+
20
+ # Configure CORS
21
+ # For Hugging Face Spaces, we allow all origins or you can add specific patterns later
22
+ app.add_middleware(
23
+ CORSMiddleware,
24
+ allow_origins=["*"],
25
+ allow_credentials=True,
26
+ allow_methods=["*"],
27
+ allow_headers=["*"],
28
+ )
29
+
30
+
31
+
32
+ UPLOAD_DIR = Path("uploads")
33
+ UPLOAD_DIR.mkdir(exist_ok=True)
34
+
35
+ @app.post("/recommend")
36
+ async def recommend(file: UploadFile = File(...), source: str = "upload", consent: bool = False):
37
+ try:
38
+ # Save uploaded file temporarily
39
+ file_ext = file.filename.split(".")[-1] if file.filename else "jpg"
40
+ filename = f"{uuid.uuid4()}.{file_ext}"
41
+ file_path = UPLOAD_DIR / filename
42
+
43
+ with open(file_path, "wb") as buffer:
44
+ shutil.copyfileobj(file.file, buffer)
45
+
46
+ logger.info(f"Processing image: {file_path}")
47
+
48
+ # Analyze face shape
49
+ try:
50
+ # Returns dict: {'Oval': 0.6, 'Square': 0.4, ...}
51
+ face_shape_probs = detect_face_shape(str(file_path))
52
+
53
+ # Get top result (keys are sorted descending in detect.py)
54
+ top_shape = list(face_shape_probs.keys())[0]
55
+ top_confidence = face_shape_probs[top_shape]
56
+
57
+ logger.info(f"Detected face shape: {top_shape} ({top_confidence})")
58
+ except Exception as e:
59
+ logger.error(f"Error analyzing face: {str(e)}")
60
+ # Clean up file if analysis fails
61
+ if not consent:
62
+ try:
63
+ os.remove(file_path)
64
+ except: pass
65
+ raise HTTPException(status_code=400, detail=f"Could not analyze face: {str(e)}")
66
+
67
+ # Get recommendations using top shape
68
+ recommendations = get_recommendations(top_shape)
69
+
70
+ # Format response
71
+ response = {
72
+ "face_shape": top_shape,
73
+ "confidence": top_confidence,
74
+ "shape_confidence": top_confidence,
75
+ "shape_probabilities": face_shape_probs,
76
+ "frame_types": [
77
+ {"label": rec, "confidence": 0.9} for rec in recommendations.get("glasses", [])
78
+ ],
79
+ "frame_size": {"label": "Medium", "confidence": 0.8}, # Placeholder
80
+ "bridge_type": {"label": "Regular", "confidence": 0.8}, # Placeholder
81
+ "materials": [{"label": "Acetate", "confidence": 0.7}], # Placeholder
82
+ "measurements": {
83
+ "face_width": 140, # Placeholder
84
+ "temple_length": 145 # Placeholder
85
+ },
86
+ "ml_predicted_frame": recommendations.get("glasses", [])[0] if recommendations.get("glasses") else None,
87
+ "ml_confidence": 0.85,
88
+ "image_id": str(filename) if consent else None
89
+ }
90
+
91
+ # Clean up if no consent to save
92
+ if not consent:
93
+ os.remove(file_path)
94
+
95
+ return JSONResponse(content=response)
96
+
97
+ except Exception as e:
98
+ logger.error(f"Server error: {str(e)}")
99
+ raise HTTPException(status_code=500, detail=str(e))
100
+
101
+ @app.get("/health")
102
+ def health_check():
103
+ return {"status": "ok"}
104
+
105
+ if __name__ == "__main__":
106
+ uvicorn.run("app:app", host="0.0.0.0", port=8000, reload=True)
check_dims.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from PIL import Image
2
+ import os
3
+
4
+ folder = "../dataset/test/Heart"
5
+ if os.path.exists(folder):
6
+ files = os.listdir(folder)[:5]
7
+ for f in files:
8
+ path = os.path.join(folder, f)
9
+ try:
10
+ with Image.open(path) as img:
11
+ print(f"{f}: {img.size}")
12
+ except:
13
+ pass
check_env.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ print("Starting import check...")
3
+ try:
4
+ import cv2
5
+ print(f"CV2 imported: {cv2.__version__}")
6
+ except Exception as e:
7
+ print(f"CV2 import failed: {e}")
8
+
9
+ try:
10
+ import numpy
11
+ print(f"Numpy imported: {numpy.__version__}")
12
+ except Exception as e:
13
+ print(f"Numpy import failed: {e}")
14
+
15
+ try:
16
+ import sklearn
17
+ print(f"Sklearn imported: {sklearn.__version__}")
18
+ except Exception as e:
19
+ print(f"Sklearn import failed: {e}")
20
+
21
+ print("Import check complete.")
classifier.py ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import pickle
3
+ import numpy as np
4
+ from PIL import Image
5
+ from huggingface_hub import hf_hub_download
6
+
7
+ REPO_ID = "codernotme/kataria_optical"
8
+ MODEL_PATH = "face_shape_means.pkl"
9
+ IMG_SIZE = (64, 64)
10
+
11
+ # Global model cache (dictionary of mean vectors)
12
+ _means = None
13
+
14
+ def load_model():
15
+ global _means
16
+ if _means is None:
17
+ # Check local first
18
+ local_path = MODEL_PATH
19
+ if not os.path.exists(local_path):
20
+ try:
21
+ print(f"Downloading {MODEL_PATH} from HF Hub...")
22
+ local_path = hf_hub_download(repo_id=REPO_ID, filename=MODEL_PATH)
23
+ except Exception as e:
24
+ print(f"Could not download from HF Hub: {e}")
25
+ return None
26
+
27
+ try:
28
+ with open(local_path, "rb") as f:
29
+ _means = pickle.load(f)
30
+ print("Loaded Mean Face vectors.")
31
+ except Exception as e:
32
+ print(f"Failed to load model: {e}")
33
+ return _means
34
+
35
+
36
+ def classify_face_shape(image_input):
37
+ """
38
+ Classifies face shape using Mean Face (Centroid) classification.
39
+
40
+ Args:
41
+ image_input: PIL Image or numpy array.
42
+
43
+ Returns:
44
+ dict: Sorted dictionary of probabilities.
45
+ """
46
+ means = load_model()
47
+
48
+ if means is None or image_input is None:
49
+ return {"Unknown": 1.0}
50
+
51
+ try:
52
+ # Preprocess: Resize to 64x64, Grayscale, Flatten
53
+ if isinstance(image_input, np.ndarray):
54
+ # Helper to convert numpy (if passed from somewhere else) to PIL for consistent resize
55
+ img = Image.fromarray(image_input)
56
+ else:
57
+ img = image_input
58
+
59
+ # Ensuring grayscale and resize
60
+ img = img.convert("L").resize(IMG_SIZE)
61
+ input_vector = np.array(img).flatten()
62
+
63
+ # Calculate Cosine Similarity to each class mean
64
+ # Cosine Sim = (A . B) / (||A|| * ||B||)
65
+ scores = {}
66
+ input_norm = np.linalg.norm(input_vector)
67
+ if input_norm == 0:
68
+ return {"Unknown": 1.0}
69
+
70
+ for label, mean_vector in means.items():
71
+ mean_norm = np.linalg.norm(mean_vector)
72
+ if mean_norm == 0:
73
+ similarity = 0
74
+ else:
75
+ similarity = np.dot(input_vector, mean_vector) / (input_norm * mean_norm)
76
+
77
+ scores[label] = similarity
78
+
79
+ # Softmax directly on similarity scores?
80
+ # Similarity is between -1 and 1 (usually 0 to 1 for images).
81
+ # We can treat similarity as a logit or just normalize.
82
+ # Let's use simple normalization if all are positive.
83
+
84
+ min_score = min(scores.values())
85
+ if min_score < 0:
86
+ # Shift to positive
87
+ scores = {k: v - min_score for k, v in scores.items()}
88
+
89
+ total_score = sum(scores.values())
90
+ if total_score == 0:
91
+ total_score = 1
92
+
93
+ probabilities = {k: round(float(v / total_score), 4) for k, v in scores.items()}
94
+
95
+ # Sort by probability descending
96
+ return dict(sorted(probabilities.items(), key=lambda item: item[1], reverse=True))
97
+
98
+
99
+ except Exception as e:
100
+ print(f"Prediction error: {e}")
101
+ return {"Error": 1.0}
102
+
103
+
dataset_prep.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import shutil
3
+
4
+ dataset_path = "/home/codernotme/Projects/Github/katariaoptics/dataset"
5
+ standard_shapes = ["heart", "oblong", "oval", "round", "square"]
6
+
7
+ def normalize_dataset():
8
+ if not os.path.exists(dataset_path):
9
+ print(f"Dataset path {dataset_path} does not exist.")
10
+ return
11
+
12
+ # Create standard folders if they don't exist
13
+ for shape in standard_shapes:
14
+ target_dir = os.path.join(dataset_path, shape)
15
+ os.makedirs(target_dir, exist_ok=True)
16
+
17
+ # Walk through the dataset directory
18
+ for item in os.listdir(dataset_path):
19
+ item_path = os.path.join(dataset_path, item)
20
+
21
+ if os.path.isdir(item_path):
22
+ lower_name = item.lower()
23
+
24
+ # If it matches a standard shape but has different case (e.g., "Heart")
25
+ if lower_name in standard_shapes and item != lower_name:
26
+ target_dir = os.path.join(dataset_path, lower_name)
27
+ print(f"Merging {item} into {lower_name}...")
28
+
29
+ for file_name in os.listdir(item_path):
30
+ src_file = os.path.join(item_path, file_name)
31
+ dst_file = os.path.join(target_dir, file_name)
32
+
33
+ # Handle duplicates by renaming
34
+ if os.path.exists(dst_file):
35
+ base, ext = os.path.splitext(file_name)
36
+ dst_file = os.path.join(target_dir, f"{base}_1{ext}")
37
+
38
+ shutil.move(src_file, dst_file)
39
+
40
+ # Remove the empty directory
41
+ os.rmdir(item_path)
42
+ print(f"Removed {item}")
43
+
44
+ if __name__ == "__main__":
45
+ normalize_dataset()
debug_mp.py ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import mediapipe
2
+ import sys
3
+
4
+ print(f"Python version: {sys.version}")
5
+ print(f"MediaPipe location: {mediapipe.__file__}")
6
+ print("dir(mediapipe):")
7
+ print(dir(mediapipe))
8
+
9
+ try:
10
+ import mediapipe.python.solutions
11
+ print("Imported mediapipe.python.solutions successfully")
12
+ except ImportError as e:
13
+ print(f"Failed to import mediapipe.python.solutions: {e}")
14
+
15
+ try:
16
+ import mediapipe.solutions
17
+ print("Imported mediapipe.solutions successfully")
18
+ except ImportError as e:
19
+ print(f"Failed to import mediapipe.solutions: {e}")
detect.py ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ import random
3
+
4
+ logger = logging.getLogger(__name__)
5
+
6
+ def detect_face_shape(image_path):
7
+ """
8
+ Detects face shape using PIL and trained Mean Face model.
9
+ """
10
+ from PIL import Image
11
+ import numpy as np
12
+ from classifier import classify_face_shape
13
+
14
+ try:
15
+ # Load image with PIL
16
+ if isinstance(image_path, str):
17
+ img = Image.open(image_path)
18
+ else:
19
+ # Assume it's already a PIL image or numpy array
20
+ img = image_path
21
+
22
+ if img is None:
23
+ raise ValueError("Could not load image")
24
+
25
+ # Classify directly (classifier handles resizing/grayscale)
26
+ shape_probs = classify_face_shape(img)
27
+
28
+ # Get the top shape
29
+ best_shape = list(shape_probs.keys())[0]
30
+ logger.info(f"Detected face shape: {best_shape} for {image_path}")
31
+
32
+ return shape_probs
33
+
34
+ except Exception as e:
35
+ logger.error(f"Detection failed: {e}")
36
+ return {"Error": 1.0}
37
+
face_shape_means.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:36b3f552e1eaf32c2abc3b3ef1fee9cb272b9a6e37ab85ba1d50d49f678eb93a
3
+ size 164135
geometry.py ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ from scipy.spatial import distance as dist
3
+
4
+ def euclidean_dist(p1, p2):
5
+ return dist.euclidean(p1, p2)
6
+
7
+ def extract_features(landmarks):
8
+ """
9
+ Extracts geometric features from 68-point facial landmarks.
10
+ Indices based on dlib/OpenCV LBF standard.
11
+
12
+ Key Points:
13
+ - Jaw: 0-16 (8 is chin)
14
+ - Eyebrows: 17-21 (left), 22-26 (right)
15
+ - Nose: 27-35 (27 is bridge top)
16
+ - Eyes: 36-41 (left), 42-47 (right)
17
+ """
18
+ landmarks = np.array(landmarks)
19
+
20
+ # defined points
21
+ jaw_left = landmarks[4]
22
+ jaw_right = landmarks[12]
23
+ chin = landmarks[8]
24
+
25
+ temple_left = landmarks[0]
26
+ temple_right = landmarks[16]
27
+
28
+ nose_top = landmarks[27] # Bridge of nose
29
+
30
+ # Measurements
31
+ jaw_width = euclidean_dist(jaw_left, jaw_right)
32
+ face_width = euclidean_dist(temple_left, temple_right) # Cheekbone/Temple width
33
+
34
+ # Estimate Face Length: distance from chin to nose bridge * 2.2 (heuristic)
35
+ # or chin to mid-eyebrow?
36
+ # Let's use chin to nose_top distance as a reliable segment
37
+ nose_to_chin = euclidean_dist(chin, nose_top)
38
+ face_length = nose_to_chin * 2.2
39
+
40
+ # Approximations
41
+ forehead_width = face_width * 0.9 # Hard to measure with 68 points, assume slightly narrower than temples
42
+
43
+ # Ratios
44
+ lw_ratio = face_length / face_width if face_width > 0 else 0
45
+ jaw_ratio = jaw_width / face_width if face_width > 0 else 0
46
+ forehead_ratio = forehead_width / face_width if face_width > 0 else 0 # Will be constant-ish with this heuristic
47
+
48
+ return {
49
+ "lw_ratio": lw_ratio,
50
+ "jaw_ratio": jaw_ratio,
51
+ "forehead_ratio": forehead_ratio
52
+ }
hf_upload.py ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from huggingface_hub import HfApi, login
3
+
4
+ # 1. Login to Hugging Face
5
+ print("Please provide your Hugging Face WRITE token.")
6
+ login()
7
+
8
+ api = HfApi()
9
+ repo_id = "codernotme/kataria_optical"
10
+
11
+ # 2. Upload only the necessary model files
12
+ files_to_upload = {
13
+ "face_shape_means.pkl": "face_shape_means.pkl",
14
+ "lbfmodel.yaml": "lbfmodel.yaml"
15
+ }
16
+
17
+ print(f"Uploading model files to https://huggingface.co/{repo_id}...")
18
+
19
+ for local_file, repo_path in files_to_upload.items():
20
+ if os.path.exists(local_file):
21
+ print(f"Uploading {local_file}...")
22
+ api.upload_file(
23
+ path_or_fileobj=local_file,
24
+ path_in_repo=repo_path,
25
+ repo_id=repo_id,
26
+ repo_type="model"
27
+ )
28
+ else:
29
+ print(f"Error: {local_file} not found in ai_service directory!")
30
+
31
+ print("\nDone! Now your code can pull the model from Hugging Face.")
landmarks.py ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import numpy as np
3
+ import os
4
+ import urllib.request
5
+ from huggingface_hub import hf_hub_download
6
+
7
+ REPO_ID = "codernotme/kataria_optical"
8
+ MODEL_FILE = "lbfmodel.yaml"
9
+
10
+ def ensure_model():
11
+ global MODEL_FILE
12
+ if not os.path.exists(MODEL_FILE):
13
+ print(f"Attempting to download {MODEL_FILE} from HF Hub...")
14
+ try:
15
+ downloaded_path = hf_hub_download(repo_id=REPO_ID, filename=MODEL_FILE)
16
+ MODEL_FILE = downloaded_path
17
+ print("Download from HF Hub complete.")
18
+ except Exception as e:
19
+ print(f"HF Hub download failed ({e}), falling back to GitHub...")
20
+ MODEL_URL = "https://raw.githubusercontent.com/kurnianggoro/GSOC2017/master/data/lbfmodel.yaml"
21
+ try:
22
+ urllib.request.urlretrieve(MODEL_URL, MODEL_FILE)
23
+ print("Download from GitHub complete.")
24
+ except Exception as e2:
25
+ raise RuntimeError(f"Failed to download LBF model from all sources: {e2}")
26
+
27
+
28
+ def get_landmarks(image_path):
29
+ """
30
+ Extracts 68 face landmarks using OpenCV FacemarkLBF.
31
+ Returns a list/array of (x, y) coordinates.
32
+ """
33
+ ensure_model()
34
+
35
+ facemark = cv2.face.createFacemarkLBF()
36
+ try:
37
+ facemark.loadModel(MODEL_FILE)
38
+ except Exception as e:
39
+ raise RuntimeError(f"Could not load LBF model: {e}")
40
+
41
+ image = cv2.imread(image_path)
42
+ if image is None:
43
+ raise ValueError(f"Could not read image: {image_path}")
44
+
45
+ gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
46
+
47
+ # Detect faces using Haar Cascade (included in cv2)
48
+ face_cascade_path = cv2.data.haarcascades + "haarcascade_frontalface_alt2.xml"
49
+ if not os.path.exists(face_cascade_path):
50
+ # Fallback to default if alt2 not found
51
+ face_cascade_path = cv2.data.haarcascades + "haarcascade_frontalface_default.xml"
52
+
53
+ face_cascade = cv2.CascadeClassifier(face_cascade_path)
54
+ faces = face_cascade.detectMultiScale(gray, 1.3, 5)
55
+
56
+ if len(faces) == 0:
57
+ raise ValueError("No face detected.")
58
+
59
+ # Find landmarks
60
+ ok, landmarks = facemark.fit(gray, faces)
61
+ if not ok:
62
+ raise ValueError("Could not detect landmarks.")
63
+
64
+ # landmarks comes as a list of numpy arrays, we extract the first face's landmarks
65
+ # landmarks[0] is shape (1, 68, 2)
66
+ return landmarks[0][0]
lbfmodel.yaml ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:70dd8b1657c42d1595d6bd13d97d932877b3bed54a95d3c4733a0f740d1fd66b
3
+ size 56375857
recommend.py ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from landmarks import get_landmarks
2
+ from geometry import extract_features
3
+ from classifier import classify_face_shape
4
+ import logging
5
+
6
+ logger = logging.getLogger(__name__)
7
+
8
+ RECOMMENDATIONS = {
9
+ # Keys must match frame-metadata.ts:
10
+ # Wayfarer, Aviator, Round, Square, Rectangle, Cat-eye, Clubmaster, Geometric, Oversized
11
+ "Oval": {
12
+ "glasses": ["Square", "Rectangle", "Wayfarer", "Aviator"],
13
+ "hair": ["Quiff", "Side Part", "Buzz Fade", "Pompadour"],
14
+ "beard": ["Light Stubble", "Short Boxed", "Verdi"],
15
+ "description": "Balanced proportions. Most frame styles work well."
16
+ },
17
+ "Round": {
18
+ "glasses": ["Rectangle", "Square", "Wayfarer", "Geometric"],
19
+ "hair": ["Pompadour", "High Fade", "Faux Hawk", "Side Part"],
20
+ "beard": ["Goatee", "Van Dyke", "Extended Chin Curtain"],
21
+ "description": "Soft angles with similar width and height. Angular frames add definition."
22
+ },
23
+ "Square": {
24
+ "glasses": ["Round", "Aviator", "Cat-eye", "Clubmaster"],
25
+ "hair": ["Textured Crop", "Side Fade", "Buzz Cut", "Crew Cut"],
26
+ "beard": ["Light Stubble", "Circle Beard", "Royale"],
27
+ "description": "Strong jawline and broad forehead. Round frames soften features."
28
+ },
29
+ "Oblong": {
30
+ "glasses": ["Oversized", "Aviator", "Square", "Wayfarer"],
31
+ "hair": ["Side Part", "Buzz Cut", "Fringe", "Caesar Cut"],
32
+ "beard": ["Mutton Chops", "Chin Strap", "Short Stubble"],
33
+ "description": "Face is longer than it is wide. Oversized frames balance length."
34
+ },
35
+ "Heart": {
36
+ "glasses": ["Round", "Wayfarer", "Cat-eye", "Geometric"],
37
+ "hair": ["Long Fringe", "Side Part", "Textured Quiff", "Swept Back"],
38
+ "beard": ["Full Beard", "Garibaldi", "Extended Goatee"],
39
+ "description": "Wider forehead and narrower chin. Bottom-heavy frames help balance."
40
+ },
41
+ "Diamond": {
42
+ "glasses": ["Round", "Cat-eye", "Clubmaster", "Oval"], # Oval frame type not in metadata, but maybe acceptable as fallback or mapped later. Kept for logic.
43
+ "hair": ["Faux Hawk", "Textured Crop", "Quiff", "Fringe"],
44
+ "beard": ["Full Beard", "Goatee", "Balbo"],
45
+ "description": "Cheekbones are the widest part. Curves soften the cheekbones."
46
+ }
47
+ }
48
+
49
+ def get_recommendations(face_shape):
50
+ """Returns style recommendations based on face shape."""
51
+ return RECOMMENDATIONS.get(face_shape, RECOMMENDATIONS["Oval"])
requirements.txt ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ opencv-contrib-python-headless
2
+ numpy
3
+ fastapi
4
+ uvicorn
5
+ python-multipart
6
+ pydantic
7
+ requests
8
+ Pillow
9
+ scipy
10
+ huggingface_hub
11
+
12
+ huggingface_hub
server.log ADDED
@@ -0,0 +1,284 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ INFO: Will watch for changes in these directories: ['/home/codernotme/Projects/Github/katariaoptics/ai_service']
2
+ INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
3
+ INFO: Started reloader process [433406] using StatReload
4
+ Process SpawnProcess-1:
5
+ Traceback (most recent call last):
6
+ File "/usr/lib64/python3.14/multiprocessing/process.py", line 320, in _bootstrap
7
+ self.run()
8
+ ~~~~~~~~^^
9
+ File "/usr/lib64/python3.14/multiprocessing/process.py", line 108, in run
10
+ self._target(*self._args, **self._kwargs)
11
+ ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
12
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/_subprocess.py", line 80, in subprocess_started
13
+ target(sockets=sockets)
14
+ ~~~~~~^^^^^^^^^^^^^^^^^
15
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/server.py", line 67, in run
16
+ return asyncio_run(self.serve(sockets=sockets), loop_factory=self.config.get_loop_factory())
17
+ File "/usr/lib64/python3.14/asyncio/runners.py", line 204, in run
18
+ return runner.run(main)
19
+ ~~~~~~~~~~^^^^^^
20
+ File "/usr/lib64/python3.14/asyncio/runners.py", line 127, in run
21
+ return self._loop.run_until_complete(task)
22
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
23
+ File "/usr/lib64/python3.14/asyncio/base_events.py", line 719, in run_until_complete
24
+ return future.result()
25
+ ~~~~~~~~~~~~~^^
26
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/server.py", line 71, in serve
27
+ await self._serve(sockets)
28
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/server.py", line 78, in _serve
29
+ config.load()
30
+ ~~~~~~~~~~~^^
31
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/config.py", line 439, in load
32
+ self.loaded_app = import_from_string(self.app)
33
+ ~~~~~~~~~~~~~~~~~~^^^^^^^^^^
34
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/importer.py", line 19, in import_from_string
35
+ module = importlib.import_module(module_str)
36
+ File "/usr/lib64/python3.14/importlib/__init__.py", line 88, in import_module
37
+ return _bootstrap._gcd_import(name[level:], package, level)
38
+ ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
39
+ File "<frozen importlib._bootstrap>", line 1398, in _gcd_import
40
+ File "<frozen importlib._bootstrap>", line 1371, in _find_and_load
41
+ File "<frozen importlib._bootstrap>", line 1342, in _find_and_load_unlocked
42
+ File "<frozen importlib._bootstrap>", line 938, in _load_unlocked
43
+ File "<frozen importlib._bootstrap_external>", line 759, in exec_module
44
+ File "<frozen importlib._bootstrap>", line 491, in _call_with_frames_removed
45
+ File "/home/codernotme/Projects/Github/katariaoptics/ai_service/app.py", line 11, in <module>
46
+ from detect import detect_face_shape
47
+ File "/home/codernotme/Projects/Github/katariaoptics/ai_service/detect.py", line 1, in <module>
48
+ from landmarks import get_landmarks
49
+ File "/home/codernotme/Projects/Github/katariaoptics/ai_service/landmarks.py", line 5, in <module>
50
+ mp_face_mesh = mp.solutions.face_mesh
51
+ ^^^^^^^^^^^^
52
+ AttributeError: module 'mediapipe' has no attribute 'solutions'
53
+ WARNING: StatReload detected changes in 'detect.py'. Reloading...
54
+ WARNING: StatReload detected changes in 'landmarks.py'. Reloading...
55
+ Process SpawnProcess-2:
56
+ Traceback (most recent call last):
57
+ File "/usr/lib64/python3.14/multiprocessing/process.py", line 320, in _bootstrap
58
+ self.run()
59
+ ~~~~~~~~^^
60
+ File "/usr/lib64/python3.14/multiprocessing/process.py", line 108, in run
61
+ self._target(*self._args, **self._kwargs)
62
+ ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
63
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/_subprocess.py", line 80, in subprocess_started
64
+ target(sockets=sockets)
65
+ ~~~~~~^^^^^^^^^^^^^^^^^
66
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/server.py", line 67, in run
67
+ return asyncio_run(self.serve(sockets=sockets), loop_factory=self.config.get_loop_factory())
68
+ File "/usr/lib64/python3.14/asyncio/runners.py", line 204, in run
69
+ return runner.run(main)
70
+ ~~~~~~~~~~^^^^^^
71
+ File "/usr/lib64/python3.14/asyncio/runners.py", line 127, in run
72
+ return self._loop.run_until_complete(task)
73
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
74
+ File "/usr/lib64/python3.14/asyncio/base_events.py", line 719, in run_until_complete
75
+ return future.result()
76
+ ~~~~~~~~~~~~~^^
77
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/server.py", line 71, in serve
78
+ await self._serve(sockets)
79
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/server.py", line 78, in _serve
80
+ config.load()
81
+ ~~~~~~~~~~~^^
82
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/config.py", line 439, in load
83
+ self.loaded_app = import_from_string(self.app)
84
+ ~~~~~~~~~~~~~~~~~~^^^^^^^^^^
85
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/importer.py", line 19, in import_from_string
86
+ module = importlib.import_module(module_str)
87
+ File "/usr/lib64/python3.14/importlib/__init__.py", line 88, in import_module
88
+ return _bootstrap._gcd_import(name[level:], package, level)
89
+ ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
90
+ File "<frozen importlib._bootstrap>", line 1398, in _gcd_import
91
+ File "<frozen importlib._bootstrap>", line 1371, in _find_and_load
92
+ File "<frozen importlib._bootstrap>", line 1342, in _find_and_load_unlocked
93
+ File "<frozen importlib._bootstrap>", line 938, in _load_unlocked
94
+ File "<frozen importlib._bootstrap_external>", line 759, in exec_module
95
+ File "<frozen importlib._bootstrap>", line 491, in _call_with_frames_removed
96
+ File "/home/codernotme/Projects/Github/katariaoptics/ai_service/app.py", line 11, in <module>
97
+ from detect import detect_face_shape
98
+ File "/home/codernotme/Projects/Github/katariaoptics/ai_service/detect.py", line 1, in <module>
99
+ from landmarks import get_landmarks
100
+ File "/home/codernotme/Projects/Github/katariaoptics/ai_service/landmarks.py", line 5, in <module>
101
+ # Try to handle potential import issues with mp.solutions
102
+ AttributeError: module 'mediapipe' has no attribute 'solutions'
103
+ Process SpawnProcess-3:
104
+ Traceback (most recent call last):
105
+ File "/usr/lib64/python3.14/multiprocessing/process.py", line 320, in _bootstrap
106
+ self.run()
107
+ ~~~~~~~~^^
108
+ File "/usr/lib64/python3.14/multiprocessing/process.py", line 108, in run
109
+ self._target(*self._args, **self._kwargs)
110
+ ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
111
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/_subprocess.py", line 80, in subprocess_started
112
+ target(sockets=sockets)
113
+ ~~~~~~^^^^^^^^^^^^^^^^^
114
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/server.py", line 67, in run
115
+ return asyncio_run(self.serve(sockets=sockets), loop_factory=self.config.get_loop_factory())
116
+ File "/usr/lib64/python3.14/asyncio/runners.py", line 204, in run
117
+ return runner.run(main)
118
+ ~~~~~~~~~~^^^^^^
119
+ File "/usr/lib64/python3.14/asyncio/runners.py", line 127, in run
120
+ return self._loop.run_until_complete(task)
121
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
122
+ File "/usr/lib64/python3.14/asyncio/base_events.py", line 719, in run_until_complete
123
+ return future.result()
124
+ ~~~~~~~~~~~~~^^
125
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/server.py", line 71, in serve
126
+ await self._serve(sockets)
127
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/server.py", line 78, in _serve
128
+ config.load()
129
+ ~~~~~~~~~~~^^
130
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/config.py", line 439, in load
131
+ self.loaded_app = import_from_string(self.app)
132
+ ~~~~~~~~~~~~~~~~~~^^^^^^^^^^
133
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/importer.py", line 22, in import_from_string
134
+ raise exc from None
135
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/importer.py", line 19, in import_from_string
136
+ module = importlib.import_module(module_str)
137
+ File "/usr/lib64/python3.14/importlib/__init__.py", line 88, in import_module
138
+ return _bootstrap._gcd_import(name[level:], package, level)
139
+ ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
140
+ File "<frozen importlib._bootstrap>", line 1398, in _gcd_import
141
+ File "<frozen importlib._bootstrap>", line 1371, in _find_and_load
142
+ File "<frozen importlib._bootstrap>", line 1342, in _find_and_load_unlocked
143
+ File "<frozen importlib._bootstrap>", line 938, in _load_unlocked
144
+ File "<frozen importlib._bootstrap_external>", line 759, in exec_module
145
+ File "<frozen importlib._bootstrap>", line 491, in _call_with_frames_removed
146
+ File "/home/codernotme/Projects/Github/katariaoptics/ai_service/app.py", line 11, in <module>
147
+ from detect import detect_face_shape
148
+ File "/home/codernotme/Projects/Github/katariaoptics/ai_service/detect.py", line 1, in <module>
149
+ from landmarks import get_landmarks
150
+ File "/home/codernotme/Projects/Github/katariaoptics/ai_service/landmarks.py", line 10, in <module>
151
+ import mediapipe.python.solutions.face_mesh as mp_face_mesh
152
+ ModuleNotFoundError: No module named 'mediapipe.python'
153
+ WARNING: StatReload detected changes in 'classifier.py'. Reloading...
154
+ Process SpawnProcess-4:
155
+ Traceback (most recent call last):
156
+ File "/usr/lib64/python3.14/multiprocessing/process.py", line 320, in _bootstrap
157
+ self.run()
158
+ ~~~~~~~~^^
159
+ File "/usr/lib64/python3.14/multiprocessing/process.py", line 108, in run
160
+ self._target(*self._args, **self._kwargs)
161
+ ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
162
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/_subprocess.py", line 80, in subprocess_started
163
+ target(sockets=sockets)
164
+ ~~~~~~^^^^^^^^^^^^^^^^^
165
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/server.py", line 67, in run
166
+ return asyncio_run(self.serve(sockets=sockets), loop_factory=self.config.get_loop_factory())
167
+ File "/usr/lib64/python3.14/asyncio/runners.py", line 204, in run
168
+ return runner.run(main)
169
+ ~~~~~~~~~~^^^^^^
170
+ File "/usr/lib64/python3.14/asyncio/runners.py", line 127, in run
171
+ return self._loop.run_until_complete(task)
172
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
173
+ File "/usr/lib64/python3.14/asyncio/base_events.py", line 719, in run_until_complete
174
+ return future.result()
175
+ ~~~~~~~~~~~~~^^
176
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/server.py", line 71, in serve
177
+ await self._serve(sockets)
178
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/server.py", line 78, in _serve
179
+ config.load()
180
+ ~~~~~~~~~~~^^
181
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/config.py", line 439, in load
182
+ self.loaded_app = import_from_string(self.app)
183
+ ~~~~~~~~~~~~~~~~~~^^^^^^^^^^
184
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/importer.py", line 22, in import_from_string
185
+ raise exc from None
186
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/importer.py", line 19, in import_from_string
187
+ module = importlib.import_module(module_str)
188
+ File "/usr/lib64/python3.14/importlib/__init__.py", line 88, in import_module
189
+ return _bootstrap._gcd_import(name[level:], package, level)
190
+ ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
191
+ File "<frozen importlib._bootstrap>", line 1398, in _gcd_import
192
+ File "<frozen importlib._bootstrap>", line 1371, in _find_and_load
193
+ File "<frozen importlib._bootstrap>", line 1342, in _find_and_load_unlocked
194
+ File "<frozen importlib._bootstrap>", line 938, in _load_unlocked
195
+ File "<frozen importlib._bootstrap_external>", line 759, in exec_module
196
+ File "<frozen importlib._bootstrap>", line 491, in _call_with_frames_removed
197
+ File "/home/codernotme/Projects/Github/katariaoptics/ai_service/app.py", line 11, in <module>
198
+ from detect import detect_face_shape
199
+ File "/home/codernotme/Projects/Github/katariaoptics/ai_service/detect.py", line 1, in <module>
200
+ from landmarks import get_landmarks
201
+ File "/home/codernotme/Projects/Github/katariaoptics/ai_service/landmarks.py", line 10, in <module>
202
+ import mediapipe.python.solutions.face_mesh as mp_face_mesh
203
+ ModuleNotFoundError: No module named 'mediapipe.python'
204
+ WARNING: StatReload detected changes in 'train_model.py'. Reloading...
205
+ Process SpawnProcess-5:
206
+ Traceback (most recent call last):
207
+ File "/usr/lib64/python3.14/multiprocessing/process.py", line 320, in _bootstrap
208
+ self.run()
209
+ ~~~~~~~~^^
210
+ File "/usr/lib64/python3.14/multiprocessing/process.py", line 108, in run
211
+ self._target(*self._args, **self._kwargs)
212
+ ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
213
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/_subprocess.py", line 80, in subprocess_started
214
+ target(sockets=sockets)
215
+ ~~~~~~^^^^^^^^^^^^^^^^^
216
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/server.py", line 67, in run
217
+ return asyncio_run(self.serve(sockets=sockets), loop_factory=self.config.get_loop_factory())
218
+ File "/usr/lib64/python3.14/asyncio/runners.py", line 204, in run
219
+ return runner.run(main)
220
+ ~~~~~~~~~~^^^^^^
221
+ File "/usr/lib64/python3.14/asyncio/runners.py", line 127, in run
222
+ return self._loop.run_until_complete(task)
223
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
224
+ File "/usr/lib64/python3.14/asyncio/base_events.py", line 719, in run_until_complete
225
+ return future.result()
226
+ ~~~~~~~~~~~~~^^
227
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/server.py", line 71, in serve
228
+ await self._serve(sockets)
229
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/server.py", line 78, in _serve
230
+ config.load()
231
+ ~~~~~~~~~~~^^
232
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/config.py", line 439, in load
233
+ self.loaded_app = import_from_string(self.app)
234
+ ~~~~~~~~~~~~~~~~~~^^^^^^^^^^
235
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/importer.py", line 22, in import_from_string
236
+ raise exc from None
237
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/importer.py", line 19, in import_from_string
238
+ module = importlib.import_module(module_str)
239
+ File "/usr/lib64/python3.14/importlib/__init__.py", line 88, in import_module
240
+ return _bootstrap._gcd_import(name[level:], package, level)
241
+ ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
242
+ File "<frozen importlib._bootstrap>", line 1398, in _gcd_import
243
+ File "<frozen importlib._bootstrap>", line 1371, in _find_and_load
244
+ File "<frozen importlib._bootstrap>", line 1342, in _find_and_load_unlocked
245
+ File "<frozen importlib._bootstrap>", line 938, in _load_unlocked
246
+ File "<frozen importlib._bootstrap_external>", line 759, in exec_module
247
+ File "<frozen importlib._bootstrap>", line 491, in _call_with_frames_removed
248
+ File "/home/codernotme/Projects/Github/katariaoptics/ai_service/app.py", line 11, in <module>
249
+ from detect import detect_face_shape
250
+ File "/home/codernotme/Projects/Github/katariaoptics/ai_service/detect.py", line 1, in <module>
251
+ from landmarks import get_landmarks
252
+ File "/home/codernotme/Projects/Github/katariaoptics/ai_service/landmarks.py", line 10, in <module>
253
+ import mediapipe.python.solutions.face_mesh as mp_face_mesh
254
+ ModuleNotFoundError: No module named 'mediapipe.python'
255
+ WARNING: StatReload detected changes in 'landmarks.py'. Reloading...
256
+ INFO: Started server process [434936]
257
+ INFO: Waiting for application startup.
258
+ INFO: Application startup complete.
259
+ WARNING: StatReload detected changes in 'geometry.py'. Reloading...
260
+ INFO: Shutting down
261
+ INFO: Waiting for application shutdown.
262
+ INFO: Application shutdown complete.
263
+ INFO: Finished server process [434936]
264
+ INFO: Started server process [434970]
265
+ INFO: Waiting for application startup.
266
+ INFO: Application startup complete.
267
+ WARNING: StatReload detected changes in 'detect.py'. Reloading...
268
+ INFO: Shutting down
269
+ INFO: Waiting for application shutdown.
270
+ INFO: Application shutdown complete.
271
+ INFO: Finished server process [434970]
272
+ INFO: Started server process [440285]
273
+ INFO: Waiting for application startup.
274
+ INFO: Application startup complete.
275
+ WARNING: StatReload detected changes in 'detect.py'. Reloading...
276
+ INFO: Shutting down
277
+ INFO: Waiting for application shutdown.
278
+ INFO: Application shutdown complete.
279
+ INFO: Finished server process [440285]
280
+ INFO: Started server process [440403]
281
+ INFO: Waiting for application startup.
282
+ INFO: Application startup complete.
283
+ INFO: 127.0.0.1:38906 - "GET /health HTTP/1.1" 200 OK
284
+ ./start.sh: line 2: 433406 Killed uvicorn app:app --host 0.0.0.0 --port 8000 --reload
server_mock.log ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ INFO: Will watch for changes in these directories: ['/home/codernotme/Projects/Github/katariaoptics/ai_service']
2
+ ERROR: [Errno 98] Address already in use
server_mock_2.log ADDED
@@ -0,0 +1,590 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ INFO: Will watch for changes in these directories: ['/home/codernotme/Projects/Github/katariaoptics/ai_service']
2
+ INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
3
+ INFO: Started reloader process [449427] using StatReload
4
+ INFO: Started server process [449444]
5
+ INFO: Waiting for application startup.
6
+ INFO: Application startup complete.
7
+ INFO: 127.0.0.1:53460 - "GET /health HTTP/1.1" 200 OK
8
+ INFO:app:Processing image: uploads/d00563f7-942a-41dd-9406-e968cbf3de51.jpg
9
+ ERROR:detect:Detection failed (using mock): Enforced MOCK mode due to Python 3.14 incompatibility
10
+ INFO:app:Detected face shape: Square
11
+ WARNING: Using mock face shape 'Square' due to library failure.
12
+ INFO: 127.0.0.1:38648 - "POST /recommend HTTP/1.1" 200 OK
13
+ INFO:app:Processing image: uploads/9683e6b9-5f86-4131-8b2b-830044b8a701.jpeg
14
+ ERROR:detect:Detection failed (using mock): Enforced MOCK mode due to Python 3.14 incompatibility
15
+ INFO:app:Detected face shape: Round
16
+ WARNING: Using mock face shape 'Round' due to library failure.
17
+ INFO: 127.0.0.1:35840 - "POST /recommend HTTP/1.1" 200 OK
18
+ WARNING: StatReload detected changes in 'detect.py'. Reloading...
19
+ INFO: Shutting down
20
+ INFO: Waiting for application shutdown.
21
+ INFO: Application shutdown complete.
22
+ INFO: Finished server process [449444]
23
+ INFO: Started server process [453875]
24
+ INFO: Waiting for application startup.
25
+ INFO: Application startup complete.
26
+ INFO:app:Processing image: uploads/45e6ba74-1f38-4589-866d-20ec3449e93f.webp
27
+ INFO:detect:Detected face shape: Oblong
28
+ INFO:app:Detected face shape: Oblong
29
+ INFO: 127.0.0.1:35992 - "POST /recommend HTTP/1.1" 200 OK
30
+ INFO:app:Processing image: uploads/e88a7c7f-f25e-4e3b-aec1-4d7b10ab2ae3.jpg
31
+ INFO:detect:Detected face shape: Oblong
32
+ INFO:app:Detected face shape: Oblong
33
+ INFO: 127.0.0.1:47182 - "POST /recommend HTTP/1.1" 200 OK
34
+ WARNING: StatReload detected changes in 'classifier.py'. Reloading...
35
+ INFO: Shutting down
36
+ INFO: Waiting for application shutdown.
37
+ INFO: Application shutdown complete.
38
+ INFO: Finished server process [453875]
39
+ INFO: Started server process [466007]
40
+ INFO: Waiting for application startup.
41
+ INFO: Application startup complete.
42
+ WARNING: StatReload detected changes in 'detect.py'. Reloading...
43
+ INFO: Shutting down
44
+ INFO: Waiting for application shutdown.
45
+ INFO: Application shutdown complete.
46
+ INFO: Finished server process [466007]
47
+ INFO: Started server process [466157]
48
+ INFO: Waiting for application startup.
49
+ INFO: Application startup complete.
50
+ WARNING: StatReload detected changes in 'app.py'. Reloading...
51
+ INFO: Shutting down
52
+ INFO: Waiting for application shutdown.
53
+ INFO: Application shutdown complete.
54
+ INFO: Finished server process [466157]
55
+ INFO: Started server process [466442]
56
+ INFO: Waiting for application startup.
57
+ INFO: Application startup complete.
58
+ INFO:app:Processing image: uploads/3b37116f-c5bd-459b-8d18-8834b219bf77.jpg
59
+ INFO:detect:Detected face shape: Oblong with probabilities: {'Oblong': np.float64(0.4518), 'Oval': np.float64(0.1806), 'Heart': np.float64(0.121), 'Diamond': np.float64(0.1095), 'Square': np.float64(0.0754), 'Round': np.float64(0.0617)}
60
+ INFO:app:Detected face shape: Oblong (0.4518)
61
+ INFO: 127.0.0.1:58160 - "POST /recommend HTTP/1.1" 200 OK
62
+ INFO:app:Processing image: uploads/385a3c1a-c307-476a-ba59-33ee63ad238c.webp
63
+ INFO:detect:Detected face shape: Oblong with probabilities: {'Oblong': np.float64(0.5095), 'Oval': np.float64(0.1535), 'Heart': np.float64(0.1029), 'Diamond': np.float64(0.0931), 'Square': np.float64(0.0846), 'Round': np.float64(0.0565)}
64
+ INFO:app:Detected face shape: Oblong (0.5095)
65
+ INFO: 127.0.0.1:41656 - "POST /recommend HTTP/1.1" 200 OK
66
+ INFO:app:Processing image: uploads/55292f82-0aac-496d-bcb8-481e5b0eca6e.jpg
67
+ INFO:detect:Detected face shape: Oblong with probabilities: {'Oblong': np.float64(0.4619), 'Oval': np.float64(0.1754), 'Heart': np.float64(0.1175), 'Diamond': np.float64(0.1064), 'Square': np.float64(0.0763), 'Round': np.float64(0.0625)}
68
+ INFO:app:Detected face shape: Oblong (0.4619)
69
+ INFO: 127.0.0.1:41672 - "POST /recommend HTTP/1.1" 200 OK
70
+ INFO:app:Processing image: uploads/5be790fc-d900-423d-b174-929dff668401.jpeg
71
+ INFO:detect:Detected face shape: Oblong with probabilities: {'Oblong': np.float64(0.4638), 'Oval': np.float64(0.1743), 'Heart': np.float64(0.1168), 'Diamond': np.float64(0.1057), 'Square': np.float64(0.0767), 'Round': np.float64(0.0628)}
72
+ INFO:app:Detected face shape: Oblong (0.4638)
73
+ INFO: 127.0.0.1:56742 - "POST /recommend HTTP/1.1" 200 OK
74
+ WARNING: StatReload detected changes in 'train.py'. Reloading...
75
+ INFO: Shutting down
76
+ INFO: Waiting for application shutdown.
77
+ INFO: Application shutdown complete.
78
+ INFO: Finished server process [466442]
79
+ INFO: Started server process [470871]
80
+ INFO: Waiting for application startup.
81
+ INFO: Application startup complete.
82
+ WARNING: StatReload detected changes in 'classifier.py'. Reloading...
83
+ INFO: Shutting down
84
+ INFO: Waiting for application shutdown.
85
+ INFO: Application shutdown complete.
86
+ INFO: Finished server process [470871]
87
+ INFO: Started server process [471707]
88
+ INFO: Waiting for application startup.
89
+ INFO: Application startup complete.
90
+ WARNING: StatReload detected changes in 'detect.py'. Reloading...
91
+ INFO: Shutting down
92
+ INFO: Waiting for application shutdown.
93
+ INFO: Application shutdown complete.
94
+ INFO: Finished server process [471707]
95
+ INFO: Started server process [472251]
96
+ INFO: Waiting for application startup.
97
+ INFO: Application startup complete.
98
+ WARNING: StatReload detected changes in 'train.py'. Reloading...
99
+ INFO: Shutting down
100
+ INFO: Waiting for application shutdown.
101
+ INFO: Application shutdown complete.
102
+ INFO: Finished server process [472251]
103
+ INFO: Started server process [473164]
104
+ INFO: Waiting for application startup.
105
+ INFO: Application startup complete.
106
+ WARNING: StatReload detected changes in 'classifier.py'. Reloading...
107
+ INFO: Shutting down
108
+ INFO: Waiting for application shutdown.
109
+ INFO: Application shutdown complete.
110
+ INFO: Finished server process [473164]
111
+ INFO: Started server process [475917]
112
+ INFO: Waiting for application startup.
113
+ INFO: Application startup complete.
114
+ WARNING: StatReload detected changes in 'detect.py'. Reloading...
115
+ INFO: Shutting down
116
+ INFO: Waiting for application shutdown.
117
+ INFO: Application shutdown complete.
118
+ INFO: Finished server process [475917]
119
+ INFO: Started server process [476076]
120
+ INFO: Waiting for application startup.
121
+ INFO: Application startup complete.
122
+ INFO:app:Processing image: uploads/8901022d-62a1-4c33-b7c6-17c8f2007adc.jpg
123
+ INFO:detect:Detected face shape: oblong for uploads/8901022d-62a1-4c33-b7c6-17c8f2007adc.jpg
124
+ INFO:app:Detected face shape: oblong (0.2111)
125
+ Loaded Mean Face vectors.
126
+ INFO: 127.0.0.1:55444 - "POST /recommend HTTP/1.1" 200 OK
127
+ INFO:app:Processing image: uploads/58318108-6d07-46b4-a93b-8a4183b46ccb.webp
128
+ INFO:detect:Detected face shape: round for uploads/58318108-6d07-46b4-a93b-8a4183b46ccb.webp
129
+ INFO:app:Detected face shape: round (0.2082)
130
+ INFO: 127.0.0.1:55444 - "POST /recommend HTTP/1.1" 200 OK
131
+ INFO:app:Processing image: uploads/3c160486-9fe6-40bc-bc98-f56b6dde3e8e.jpg
132
+ INFO:detect:Detected face shape: heart for uploads/3c160486-9fe6-40bc-bc98-f56b6dde3e8e.jpg
133
+ INFO:app:Detected face shape: heart (0.2041)
134
+ INFO: 127.0.0.1:51692 - "POST /recommend HTTP/1.1" 200 OK
135
+ INFO:app:Processing image: uploads/529b4f8d-dc6c-4195-880e-7644a1a34637.jpeg
136
+ INFO:detect:Detected face shape: oblong for uploads/529b4f8d-dc6c-4195-880e-7644a1a34637.jpeg
137
+ INFO:app:Detected face shape: oblong (0.2076)
138
+ INFO: 127.0.0.1:51700 - "POST /recommend HTTP/1.1" 200 OK
139
+ WARNING: StatReload detected changes in 'test_accuracy_simple.py'. Reloading...
140
+ INFO: Shutting down
141
+ INFO: Waiting for application shutdown.
142
+ INFO: Application shutdown complete.
143
+ INFO: Finished server process [476076]
144
+ INFO: Started server process [493208]
145
+ INFO: Waiting for application startup.
146
+ INFO: Application startup complete.
147
+ WARNING: StatReload detected changes in 'classifier.py'. Reloading...
148
+ INFO: Shutting down
149
+ INFO: Waiting for application shutdown.
150
+ INFO: Application shutdown complete.
151
+ INFO: Finished server process [493208]
152
+ INFO: Started server process [493836]
153
+ INFO: Waiting for application startup.
154
+ INFO: Application startup complete.
155
+ WARNING: StatReload detected changes in 'app.py'. Reloading...
156
+ INFO: Shutting down
157
+ INFO: Waiting for application shutdown.
158
+ INFO: Application shutdown complete.
159
+ INFO: Finished server process [493836]
160
+ INFO: Started server process [519286]
161
+ INFO: Waiting for application startup.
162
+ INFO: Application startup complete.
163
+ WARNING: StatReload detected changes in 'train_means.py'. Reloading...
164
+ INFO: Shutting down
165
+ INFO: Waiting for application shutdown.
166
+ INFO: Application shutdown complete.
167
+ INFO: Finished server process [519286]
168
+ INFO: Started server process [520894]
169
+ INFO: Waiting for application startup.
170
+ INFO: Application startup complete.
171
+ WARNING: StatReload detected changes in 'classifier.py'. Reloading...
172
+ INFO: Shutting down
173
+ INFO: Waiting for application shutdown.
174
+ INFO: Application shutdown complete.
175
+ INFO: Finished server process [520894]
176
+ Process SpawnProcess-16:
177
+ Traceback (most recent call last):
178
+ File "/usr/lib64/python3.14/multiprocessing/process.py", line 320, in _bootstrap
179
+ self.run()
180
+ ~~~~~~~~^^
181
+ File "/usr/lib64/python3.14/multiprocessing/process.py", line 108, in run
182
+ self._target(*self._args, **self._kwargs)
183
+ ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
184
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/_subprocess.py", line 80, in subprocess_started
185
+ target(sockets=sockets)
186
+ ~~~~~~^^^^^^^^^^^^^^^^^
187
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/server.py", line 67, in run
188
+ return asyncio_run(self.serve(sockets=sockets), loop_factory=self.config.get_loop_factory())
189
+ File "/usr/lib64/python3.14/asyncio/runners.py", line 204, in run
190
+ return runner.run(main)
191
+ ~~~~~~~~~~^^^^^^
192
+ File "/usr/lib64/python3.14/asyncio/runners.py", line 127, in run
193
+ return self._loop.run_until_complete(task)
194
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
195
+ File "/usr/lib64/python3.14/asyncio/base_events.py", line 719, in run_until_complete
196
+ return future.result()
197
+ ~~~~~~~~~~~~~^^
198
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/server.py", line 71, in serve
199
+ await self._serve(sockets)
200
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/server.py", line 78, in _serve
201
+ config.load()
202
+ ~~~~~~~~~~~^^
203
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/config.py", line 439, in load
204
+ self.loaded_app = import_from_string(self.app)
205
+ ~~~~~~~~~~~~~~~~~~^^^^^^^^^^
206
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/importer.py", line 22, in import_from_string
207
+ raise exc from None
208
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/importer.py", line 19, in import_from_string
209
+ module = importlib.import_module(module_str)
210
+ File "/usr/lib64/python3.14/importlib/__init__.py", line 88, in import_module
211
+ return _bootstrap._gcd_import(name[level:], package, level)
212
+ ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
213
+ File "<frozen importlib._bootstrap>", line 1398, in _gcd_import
214
+ File "<frozen importlib._bootstrap>", line 1371, in _find_and_load
215
+ File "<frozen importlib._bootstrap>", line 1342, in _find_and_load_unlocked
216
+ File "<frozen importlib._bootstrap>", line 938, in _load_unlocked
217
+ File "<frozen importlib._bootstrap_external>", line 759, in exec_module
218
+ File "<frozen importlib._bootstrap>", line 491, in _call_with_frames_removed
219
+ File "/home/codernotme/Projects/Github/katariaoptics/ai_service/app.py", line 12, in <module>
220
+ from recommend import get_recommendations
221
+ File "/home/codernotme/Projects/Github/katariaoptics/ai_service/recommend.py", line 3, in <module>
222
+ from classifier import classify_face_shape
223
+ File "/home/codernotme/Projects/Github/katariaoptics/ai_service/classifier.py", line 1, in <module>
224
+ from huggingface_hub import hf_hub_download
225
+ ModuleNotFoundError: No module named 'huggingface_hub'
226
+ WARNING: StatReload detected changes in 'landmarks.py'. Reloading...
227
+ Process SpawnProcess-17:
228
+ Traceback (most recent call last):
229
+ File "/usr/lib64/python3.14/multiprocessing/process.py", line 320, in _bootstrap
230
+ self.run()
231
+ ~~~~~~~~^^
232
+ File "/usr/lib64/python3.14/multiprocessing/process.py", line 108, in run
233
+ self._target(*self._args, **self._kwargs)
234
+ ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
235
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/_subprocess.py", line 80, in subprocess_started
236
+ target(sockets=sockets)
237
+ ~~~~~~^^^^^^^^^^^^^^^^^
238
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/server.py", line 67, in run
239
+ return asyncio_run(self.serve(sockets=sockets), loop_factory=self.config.get_loop_factory())
240
+ File "/usr/lib64/python3.14/asyncio/runners.py", line 204, in run
241
+ return runner.run(main)
242
+ ~~~~~~~~~~^^^^^^
243
+ File "/usr/lib64/python3.14/asyncio/runners.py", line 127, in run
244
+ return self._loop.run_until_complete(task)
245
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
246
+ File "/usr/lib64/python3.14/asyncio/base_events.py", line 719, in run_until_complete
247
+ return future.result()
248
+ ~~~~~~~~~~~~~^^
249
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/server.py", line 71, in serve
250
+ await self._serve(sockets)
251
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/server.py", line 78, in _serve
252
+ config.load()
253
+ ~~~~~~~~~~~^^
254
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/config.py", line 439, in load
255
+ self.loaded_app = import_from_string(self.app)
256
+ ~~~~~~~~~~~~~~~~~~^^^^^^^^^^
257
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/importer.py", line 22, in import_from_string
258
+ raise exc from None
259
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/importer.py", line 19, in import_from_string
260
+ module = importlib.import_module(module_str)
261
+ File "/usr/lib64/python3.14/importlib/__init__.py", line 88, in import_module
262
+ return _bootstrap._gcd_import(name[level:], package, level)
263
+ ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
264
+ File "<frozen importlib._bootstrap>", line 1398, in _gcd_import
265
+ File "<frozen importlib._bootstrap>", line 1371, in _find_and_load
266
+ File "<frozen importlib._bootstrap>", line 1342, in _find_and_load_unlocked
267
+ File "<frozen importlib._bootstrap>", line 938, in _load_unlocked
268
+ File "<frozen importlib._bootstrap_external>", line 759, in exec_module
269
+ File "<frozen importlib._bootstrap>", line 491, in _call_with_frames_removed
270
+ File "/home/codernotme/Projects/Github/katariaoptics/ai_service/app.py", line 12, in <module>
271
+ from recommend import get_recommendations
272
+ File "/home/codernotme/Projects/Github/katariaoptics/ai_service/recommend.py", line 1, in <module>
273
+ from landmarks import get_landmarks
274
+ File "/home/codernotme/Projects/Github/katariaoptics/ai_service/landmarks.py", line 6, in <module>
275
+ from huggingface_hub import hf_hub_download
276
+ ModuleNotFoundError: No module named 'huggingface_hub'
277
+ WARNING: StatReload detected changes in 'classifier.py'. Reloading...
278
+ Process SpawnProcess-18:
279
+ Traceback (most recent call last):
280
+ File "/usr/lib64/python3.14/multiprocessing/process.py", line 320, in _bootstrap
281
+ self.run()
282
+ ~~~~~~~~^^
283
+ File "/usr/lib64/python3.14/multiprocessing/process.py", line 108, in run
284
+ self._target(*self._args, **self._kwargs)
285
+ ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
286
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/_subprocess.py", line 80, in subprocess_started
287
+ target(sockets=sockets)
288
+ ~~~~~~^^^^^^^^^^^^^^^^^
289
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/server.py", line 67, in run
290
+ return asyncio_run(self.serve(sockets=sockets), loop_factory=self.config.get_loop_factory())
291
+ File "/usr/lib64/python3.14/asyncio/runners.py", line 204, in run
292
+ return runner.run(main)
293
+ ~~~~~~~~~~^^^^^^
294
+ File "/usr/lib64/python3.14/asyncio/runners.py", line 127, in run
295
+ return self._loop.run_until_complete(task)
296
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
297
+ File "/usr/lib64/python3.14/asyncio/base_events.py", line 719, in run_until_complete
298
+ return future.result()
299
+ ~~~~~~~~~~~~~^^
300
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/server.py", line 71, in serve
301
+ await self._serve(sockets)
302
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/server.py", line 78, in _serve
303
+ config.load()
304
+ ~~~~~~~~~~~^^
305
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/config.py", line 439, in load
306
+ self.loaded_app = import_from_string(self.app)
307
+ ~~~~~~~~~~~~~~~~~~^^^^^^^^^^
308
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/importer.py", line 22, in import_from_string
309
+ raise exc from None
310
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/importer.py", line 19, in import_from_string
311
+ module = importlib.import_module(module_str)
312
+ File "/usr/lib64/python3.14/importlib/__init__.py", line 88, in import_module
313
+ return _bootstrap._gcd_import(name[level:], package, level)
314
+ ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
315
+ File "<frozen importlib._bootstrap>", line 1398, in _gcd_import
316
+ File "<frozen importlib._bootstrap>", line 1371, in _find_and_load
317
+ File "<frozen importlib._bootstrap>", line 1342, in _find_and_load_unlocked
318
+ File "<frozen importlib._bootstrap>", line 938, in _load_unlocked
319
+ File "<frozen importlib._bootstrap_external>", line 759, in exec_module
320
+ File "<frozen importlib._bootstrap>", line 491, in _call_with_frames_removed
321
+ File "/home/codernotme/Projects/Github/katariaoptics/ai_service/app.py", line 12, in <module>
322
+ from recommend import get_recommendations
323
+ File "/home/codernotme/Projects/Github/katariaoptics/ai_service/recommend.py", line 1, in <module>
324
+ from landmarks import get_landmarks
325
+ File "/home/codernotme/Projects/Github/katariaoptics/ai_service/landmarks.py", line 6, in <module>
326
+ from huggingface_hub import hf_hub_download
327
+ ModuleNotFoundError: No module named 'huggingface_hub'
328
+ WARNING: StatReload detected changes in 'app.py'. Reloading...
329
+ Process SpawnProcess-19:
330
+ Traceback (most recent call last):
331
+ File "/usr/lib64/python3.14/multiprocessing/process.py", line 320, in _bootstrap
332
+ self.run()
333
+ ~~~~~~~~^^
334
+ File "/usr/lib64/python3.14/multiprocessing/process.py", line 108, in run
335
+ self._target(*self._args, **self._kwargs)
336
+ ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
337
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/_subprocess.py", line 80, in subprocess_started
338
+ target(sockets=sockets)
339
+ ~~~~~~^^^^^^^^^^^^^^^^^
340
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/server.py", line 67, in run
341
+ return asyncio_run(self.serve(sockets=sockets), loop_factory=self.config.get_loop_factory())
342
+ File "/usr/lib64/python3.14/asyncio/runners.py", line 204, in run
343
+ return runner.run(main)
344
+ ~~~~~~~~~~^^^^^^
345
+ File "/usr/lib64/python3.14/asyncio/runners.py", line 127, in run
346
+ return self._loop.run_until_complete(task)
347
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
348
+ File "/usr/lib64/python3.14/asyncio/base_events.py", line 719, in run_until_complete
349
+ return future.result()
350
+ ~~~~~~~~~~~~~^^
351
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/server.py", line 71, in serve
352
+ await self._serve(sockets)
353
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/server.py", line 78, in _serve
354
+ config.load()
355
+ ~~~~~~~~~~~^^
356
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/config.py", line 439, in load
357
+ self.loaded_app = import_from_string(self.app)
358
+ ~~~~~~~~~~~~~~~~~~^^^^^^^^^^
359
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/importer.py", line 22, in import_from_string
360
+ raise exc from None
361
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/importer.py", line 19, in import_from_string
362
+ module = importlib.import_module(module_str)
363
+ File "/usr/lib64/python3.14/importlib/__init__.py", line 88, in import_module
364
+ return _bootstrap._gcd_import(name[level:], package, level)
365
+ ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
366
+ File "<frozen importlib._bootstrap>", line 1398, in _gcd_import
367
+ File "<frozen importlib._bootstrap>", line 1371, in _find_and_load
368
+ File "<frozen importlib._bootstrap>", line 1342, in _find_and_load_unlocked
369
+ File "<frozen importlib._bootstrap>", line 938, in _load_unlocked
370
+ File "<frozen importlib._bootstrap_external>", line 759, in exec_module
371
+ File "<frozen importlib._bootstrap>", line 491, in _call_with_frames_removed
372
+ File "/home/codernotme/Projects/Github/katariaoptics/ai_service/app.py", line 12, in <module>
373
+ from recommend import get_recommendations
374
+ File "/home/codernotme/Projects/Github/katariaoptics/ai_service/recommend.py", line 1, in <module>
375
+ from landmarks import get_landmarks
376
+ File "/home/codernotme/Projects/Github/katariaoptics/ai_service/landmarks.py", line 6, in <module>
377
+ from huggingface_hub import hf_hub_download
378
+ ModuleNotFoundError: No module named 'huggingface_hub'
379
+ WARNING: StatReload detected changes in 'app.py'. Reloading...
380
+ INFO: Started server process [525740]
381
+ INFO: Waiting for application startup.
382
+ INFO: Application startup complete.
383
+ WARNING: StatReload detected changes in 'hf_upload.py'. Reloading...
384
+ INFO: Shutting down
385
+ INFO: Waiting for application shutdown.
386
+ INFO: Application shutdown complete.
387
+ INFO: Finished server process [525740]
388
+ WARNING: StatReload detected changes in 'classifier.py'. Reloading...
389
+ Process SpawnProcess-21:
390
+ Traceback (most recent call last):
391
+ File "/usr/lib64/python3.14/multiprocessing/process.py", line 320, in _bootstrap
392
+ self.run()
393
+ ~~~~~~~~^^
394
+ File "/usr/lib64/python3.14/multiprocessing/process.py", line 108, in run
395
+ self._target(*self._args, **self._kwargs)
396
+ ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
397
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/_subprocess.py", line 80, in subprocess_started
398
+ target(sockets=sockets)
399
+ ~~~~~~^^^^^^^^^^^^^^^^^
400
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/server.py", line 67, in run
401
+ return asyncio_run(self.serve(sockets=sockets), loop_factory=self.config.get_loop_factory())
402
+ File "/usr/lib64/python3.14/asyncio/runners.py", line 204, in run
403
+ return runner.run(main)
404
+ ~~~~~~~~~~^^^^^^
405
+ File "/usr/lib64/python3.14/asyncio/runners.py", line 127, in run
406
+ return self._loop.run_until_complete(task)
407
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
408
+ File "/usr/lib64/python3.14/asyncio/base_events.py", line 719, in run_until_complete
409
+ return future.result()
410
+ ~~~~~~~~~~~~~^^
411
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/server.py", line 71, in serve
412
+ await self._serve(sockets)
413
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/server.py", line 78, in _serve
414
+ config.load()
415
+ ~~~~~~~~~~~^^
416
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/config.py", line 439, in load
417
+ self.loaded_app = import_from_string(self.app)
418
+ ~~~~~~~~~~~~~~~~~~^^^^^^^^^^
419
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/importer.py", line 22, in import_from_string
420
+ raise exc from None
421
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/importer.py", line 19, in import_from_string
422
+ module = importlib.import_module(module_str)
423
+ File "/usr/lib64/python3.14/importlib/__init__.py", line 88, in import_module
424
+ return _bootstrap._gcd_import(name[level:], package, level)
425
+ ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
426
+ File "<frozen importlib._bootstrap>", line 1398, in _gcd_import
427
+ File "<frozen importlib._bootstrap>", line 1371, in _find_and_load
428
+ File "<frozen importlib._bootstrap>", line 1342, in _find_and_load_unlocked
429
+ File "<frozen importlib._bootstrap>", line 938, in _load_unlocked
430
+ File "<frozen importlib._bootstrap_external>", line 759, in exec_module
431
+ File "<frozen importlib._bootstrap>", line 491, in _call_with_frames_removed
432
+ File "/home/codernotme/Projects/Github/katariaoptics/ai_service/app.py", line 12, in <module>
433
+ from recommend import get_recommendations
434
+ File "/home/codernotme/Projects/Github/katariaoptics/ai_service/recommend.py", line 3, in <module>
435
+ from classifier import classify_face_shape
436
+ File "/home/codernotme/Projects/Github/katariaoptics/ai_service/classifier.py", line 5, in <module>
437
+ from huggingface_hub import hf_hub_download
438
+ ModuleNotFoundError: No module named 'huggingface_hub'
439
+ Process SpawnProcess-22:
440
+ Traceback (most recent call last):
441
+ File "/usr/lib64/python3.14/multiprocessing/process.py", line 320, in _bootstrap
442
+ self.run()
443
+ ~~~~~~~~^^
444
+ File "/usr/lib64/python3.14/multiprocessing/process.py", line 108, in run
445
+ self._target(*self._args, **self._kwargs)
446
+ ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
447
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/_subprocess.py", line 80, in subprocess_started
448
+ target(sockets=sockets)
449
+ ~~~~~~^^^^^^^^^^^^^^^^^
450
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/server.py", line 67, in run
451
+ return asyncio_run(self.serve(sockets=sockets), loop_factory=self.config.get_loop_factory())
452
+ File "/usr/lib64/python3.14/asyncio/runners.py", line 204, in run
453
+ return runner.run(main)
454
+ ~~~~~~~~~~^^^^^^
455
+ File "/usr/lib64/python3.14/asyncio/runners.py", line 127, in run
456
+ return self._loop.run_until_complete(task)
457
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
458
+ File "/usr/lib64/python3.14/asyncio/base_events.py", line 719, in run_until_complete
459
+ return future.result()
460
+ ~~~~~~~~~~~~~^^
461
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/server.py", line 71, in serve
462
+ await self._serve(sockets)
463
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/server.py", line 78, in _serve
464
+ config.load()
465
+ ~~~~~~~~~~~^^
466
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/config.py", line 439, in load
467
+ self.loaded_app = import_from_string(self.app)
468
+ ~~~~~~~~~~~~~~~~~~^^^^^^^^^^
469
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/importer.py", line 22, in import_from_string
470
+ raise exc from None
471
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/importer.py", line 19, in import_from_string
472
+ module = importlib.import_module(module_str)
473
+ File "/usr/lib64/python3.14/importlib/__init__.py", line 88, in import_module
474
+ return _bootstrap._gcd_import(name[level:], package, level)
475
+ ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
476
+ File "<frozen importlib._bootstrap>", line 1398, in _gcd_import
477
+ File "<frozen importlib._bootstrap>", line 1371, in _find_and_load
478
+ File "<frozen importlib._bootstrap>", line 1342, in _find_and_load_unlocked
479
+ File "<frozen importlib._bootstrap>", line 938, in _load_unlocked
480
+ File "<frozen importlib._bootstrap_external>", line 759, in exec_module
481
+ File "<frozen importlib._bootstrap>", line 491, in _call_with_frames_removed
482
+ File "/home/codernotme/Projects/Github/katariaoptics/ai_service/app.py", line 12, in <module>
483
+ from recommend import get_recommendations
484
+ File "/home/codernotme/Projects/Github/katariaoptics/ai_service/recommend.py", line 3, in <module>
485
+ from classifier import classify_face_shape
486
+ File "/home/codernotme/Projects/Github/katariaoptics/ai_service/classifier.py", line 5, in <module>
487
+ from huggingface_hub import hf_hub_download
488
+ ModuleNotFoundError: No module named 'huggingface_hub'
489
+ WARNING: StatReload detected changes in 'landmarks.py'. Reloading...
490
+ Process SpawnProcess-23:
491
+ Traceback (most recent call last):
492
+ File "/usr/lib64/python3.14/multiprocessing/process.py", line 320, in _bootstrap
493
+ self.run()
494
+ ~~~~~~~~^^
495
+ File "/usr/lib64/python3.14/multiprocessing/process.py", line 108, in run
496
+ self._target(*self._args, **self._kwargs)
497
+ ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
498
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/_subprocess.py", line 80, in subprocess_started
499
+ target(sockets=sockets)
500
+ ~~~~~~^^^^^^^^^^^^^^^^^
501
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/server.py", line 67, in run
502
+ return asyncio_run(self.serve(sockets=sockets), loop_factory=self.config.get_loop_factory())
503
+ File "/usr/lib64/python3.14/asyncio/runners.py", line 204, in run
504
+ return runner.run(main)
505
+ ~~~~~~~~~~^^^^^^
506
+ File "/usr/lib64/python3.14/asyncio/runners.py", line 127, in run
507
+ return self._loop.run_until_complete(task)
508
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
509
+ File "/usr/lib64/python3.14/asyncio/base_events.py", line 719, in run_until_complete
510
+ return future.result()
511
+ ~~~~~~~~~~~~~^^
512
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/server.py", line 71, in serve
513
+ await self._serve(sockets)
514
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/server.py", line 78, in _serve
515
+ config.load()
516
+ ~~~~~~~~~~~^^
517
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/config.py", line 439, in load
518
+ self.loaded_app = import_from_string(self.app)
519
+ ~~~~~~~~~~~~~~~~~~^^^^^^^^^^
520
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/importer.py", line 22, in import_from_string
521
+ raise exc from None
522
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/importer.py", line 19, in import_from_string
523
+ module = importlib.import_module(module_str)
524
+ File "/usr/lib64/python3.14/importlib/__init__.py", line 88, in import_module
525
+ return _bootstrap._gcd_import(name[level:], package, level)
526
+ ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
527
+ File "<frozen importlib._bootstrap>", line 1398, in _gcd_import
528
+ File "<frozen importlib._bootstrap>", line 1371, in _find_and_load
529
+ File "<frozen importlib._bootstrap>", line 1342, in _find_and_load_unlocked
530
+ File "<frozen importlib._bootstrap>", line 938, in _load_unlocked
531
+ File "<frozen importlib._bootstrap_external>", line 759, in exec_module
532
+ File "<frozen importlib._bootstrap>", line 491, in _call_with_frames_removed
533
+ File "/home/codernotme/Projects/Github/katariaoptics/ai_service/app.py", line 12, in <module>
534
+ from recommend import get_recommendations
535
+ File "/home/codernotme/Projects/Github/katariaoptics/ai_service/recommend.py", line 1, in <module>
536
+ from landmarks import get_landmarks
537
+ File "/home/codernotme/Projects/Github/katariaoptics/ai_service/landmarks.py", line 5, in <module>
538
+ from huggingface_hub import hf_hub_download
539
+ ModuleNotFoundError: No module named 'huggingface_hub'
540
+ WARNING: StatReload detected changes in 'app.py'. Reloading...
541
+ Process SpawnProcess-24:
542
+ Traceback (most recent call last):
543
+ File "/usr/lib64/python3.14/multiprocessing/process.py", line 320, in _bootstrap
544
+ self.run()
545
+ ~~~~~~~~^^
546
+ File "/usr/lib64/python3.14/multiprocessing/process.py", line 108, in run
547
+ self._target(*self._args, **self._kwargs)
548
+ ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
549
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/_subprocess.py", line 80, in subprocess_started
550
+ target(sockets=sockets)
551
+ ~~~~~~^^^^^^^^^^^^^^^^^
552
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/server.py", line 67, in run
553
+ return asyncio_run(self.serve(sockets=sockets), loop_factory=self.config.get_loop_factory())
554
+ File "/usr/lib64/python3.14/asyncio/runners.py", line 204, in run
555
+ return runner.run(main)
556
+ ~~~~~~~~~~^^^^^^
557
+ File "/usr/lib64/python3.14/asyncio/runners.py", line 127, in run
558
+ return self._loop.run_until_complete(task)
559
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
560
+ File "/usr/lib64/python3.14/asyncio/base_events.py", line 719, in run_until_complete
561
+ return future.result()
562
+ ~~~~~~~~~~~~~^^
563
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/server.py", line 71, in serve
564
+ await self._serve(sockets)
565
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/server.py", line 78, in _serve
566
+ config.load()
567
+ ~~~~~~~~~~~^^
568
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/config.py", line 439, in load
569
+ self.loaded_app = import_from_string(self.app)
570
+ ~~~~~~~~~~~~~~~~~~^^^^^^^^^^
571
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/importer.py", line 22, in import_from_string
572
+ raise exc from None
573
+ File "/home/codernotme/.local/lib/python3.14/site-packages/uvicorn/importer.py", line 19, in import_from_string
574
+ module = importlib.import_module(module_str)
575
+ File "/usr/lib64/python3.14/importlib/__init__.py", line 88, in import_module
576
+ return _bootstrap._gcd_import(name[level:], package, level)
577
+ ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
578
+ File "<frozen importlib._bootstrap>", line 1398, in _gcd_import
579
+ File "<frozen importlib._bootstrap>", line 1371, in _find_and_load
580
+ File "<frozen importlib._bootstrap>", line 1342, in _find_and_load_unlocked
581
+ File "<frozen importlib._bootstrap>", line 938, in _load_unlocked
582
+ File "<frozen importlib._bootstrap_external>", line 759, in exec_module
583
+ File "<frozen importlib._bootstrap>", line 491, in _call_with_frames_removed
584
+ File "/home/codernotme/Projects/Github/katariaoptics/ai_service/app.py", line 12, in <module>
585
+ from recommend import get_recommendations
586
+ File "/home/codernotme/Projects/Github/katariaoptics/ai_service/recommend.py", line 1, in <module>
587
+ from landmarks import get_landmarks
588
+ File "/home/codernotme/Projects/Github/katariaoptics/ai_service/landmarks.py", line 5, in <module>
589
+ from huggingface_hub import hf_hub_download
590
+ ModuleNotFoundError: No module named 'huggingface_hub'
start.sh ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ #!/bin/bash
2
+ uvicorn app:app --host 0.0.0.0 --port 8000 --reload
start_and_setup.sh ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ echo "Installing dependencies..."
3
+ pip install -r requirements.txt
4
+ if [ $? -eq 0 ]; then
5
+ echo "Dependencies installed successfully."
6
+
7
+ echo "Training model on dataset..."
8
+ python3 train_model.py
9
+
10
+ echo "Starting server..."
11
+ ./start.sh
12
+ else
13
+ echo "Failed to install dependencies."
14
+ exit 1
15
+ fi
test_accuracy.py ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+ from detect import detect_face_shape
4
+
5
+ # Configuration
6
+ TEST_DATASET_DIR = "/home/codernotme/Projects/Github/katariaoptics/dataset/test"
7
+
8
+ def test_accuracy():
9
+ print(f"Testing accuracy using dataset at: {TEST_DATASET_DIR}")
10
+
11
+ if not os.path.exists(TEST_DATASET_DIR):
12
+ print("Test dataset directory not found.")
13
+ return
14
+
15
+ total_images = 0
16
+ correct_predictions = 0
17
+ class_stats = {}
18
+
19
+ # Get list of classes (subdirectories)
20
+ classes = [d for d in os.listdir(TEST_DATASET_DIR) if os.path.isdir(os.path.join(TEST_DATASET_DIR, d))]
21
+
22
+ for label in classes:
23
+ folder_path = os.path.join(TEST_DATASET_DIR, label)
24
+ files = os.listdir(folder_path)
25
+
26
+ # Ground truth label (normalize to lowercase to match model classes if needed,
27
+ # though detect.py returns capitalized keys usually? Let's check model keys.)
28
+ # The model uses "heart", "oblong" etc. (lowercase) as keys in the pickle from train_means.py
29
+ # BUT detect.py / classifier.py capitalizes the keys in the return dict.
30
+ # Let's verify what classifier.py returns.
31
+ # classifier.py: return dict(sorted(probabilities.items()...)) where keys are labels.
32
+ # In train_means.py, keys are "heart", "oblong"... (lowercase).
33
+ # In classifier.py, it constructs the dict from the `means` keys.
34
+ # So classifier.py returns lowercase keys like "heart", "oblong".
35
+ # Wait, let me double check classifier.py content.
36
+
37
+ ground_truth = label.lower()
38
+
39
+ class_stats[ground_truth] = {"total": 0, "correct": 0}
40
+
41
+ print(f"Processing class {label} (Ground Truth: {ground_truth})...")
42
+
43
+ for filename in files:
44
+ img_path = os.path.join(folder_path, filename)
45
+ try:
46
+ # Run detection
47
+ result = detect_face_shape(img_path)
48
+
49
+ # Get predicted label (highest probability)
50
+ # detect.py returns dict like {'heart': 0.8, 'oval': 0.2}
51
+ if not result or "Error" in result or "Unknown" in result:
52
+ continue
53
+
54
+ predicted_label = list(result.keys())[0].lower()
55
+
56
+ total_images += 1
57
+ class_stats[ground_truth]["total"] += 1
58
+
59
+ if predicted_label == ground_truth:
60
+ correct_predictions += 1
61
+ class_stats[ground_truth]["correct"] += 1
62
+
63
+ except Exception as e:
64
+ # print(f"Error processing {filename}: {e}")
65
+ pass
66
+
67
+ if total_images == 0:
68
+ print("No images found or processed.")
69
+ return
70
+
71
+ print("\n--- Results ---")
72
+ print(f"Overall Accuracy: {correct_predictions}/{total_images} ({correct_predictions/total_images*100:.2f}%)")
73
+
74
+ print("\nPer Class Accuracy:")
75
+ for cls, stats in class_stats.items():
76
+ if stats["total"] > 0:
77
+ acc = stats["correct"] / stats["total"] * 100
78
+ print(f" {cls}: {stats['correct']}/{stats['total']} ({acc:.2f}%)")
79
+ else:
80
+ print(f" {cls}: No images")
81
+
82
+ if __name__ == "__main__":
83
+ test_accuracy()
test_accuracy_simple.py ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+
4
+ # Ensure we can import from current directory
5
+ sys.path.append(os.getcwd())
6
+
7
+ from detect import detect_face_shape
8
+
9
+ TEST_DATASET_DIR = "../dataset/test"
10
+
11
+ def test():
12
+ with open("test_results.txt", "w") as f:
13
+ f.write("Starting simple accuracy test...\n")
14
+ if not os.path.exists(TEST_DATASET_DIR):
15
+ f.write(f"Dataset not found at {TEST_DATASET_DIR}\n")
16
+ return
17
+
18
+ classes = os.listdir(TEST_DATASET_DIR)
19
+ f.write(f"Classes found: {classes}\n")
20
+
21
+ for label in classes:
22
+ folder = os.path.join(TEST_DATASET_DIR, label)
23
+ if not os.path.isdir(folder):
24
+ continue
25
+
26
+ files = os.listdir(folder)
27
+ if not files:
28
+ f.write(f"No files in {label}\n")
29
+ continue
30
+
31
+ # Test just one image per class to verify pipeline
32
+ img_path = os.path.join(folder, files[0])
33
+ f.write(f"Testing {label} with {img_path}\n")
34
+
35
+ try:
36
+ result = detect_face_shape(img_path)
37
+ f.write(f"Result for {label}: {result}\n")
38
+ except Exception as e:
39
+ f.write(f"Error for {label}: {e}\n")
40
+ f.flush()
41
+
42
+ if __name__ == "__main__":
43
+ test()
test_api.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import requests
2
+ import time
3
+ import sys
4
+ import os
5
+
6
+ API_URL = "http://localhost:8000"
7
+ IMAGE_PATH = "/home/codernotme/.gemini/antigravity/brain/fd9634fe-740d-41a0-9d95-4bd5df08a975/test_face_1770615920629.png" # Updated path
8
+
9
+ def wait_for_server():
10
+ print("Waiting for server...")
11
+ for i in range(30):
12
+ try:
13
+ response = requests.get(f"{API_URL}/health")
14
+ if response.status_code == 200:
15
+ print("Server is ready!")
16
+ return True
17
+ except requests.exceptions.ConnectionError:
18
+ pass
19
+ time.sleep(2)
20
+ print(".", end="", flush=True)
21
+ print("\nServer timed out.")
22
+ return False
23
+
24
+ def test_recommendation():
25
+ if not os.path.exists(IMAGE_PATH):
26
+ print(f"Test image not found at {IMAGE_PATH}")
27
+ return
28
+
29
+ print(f"Testing recommendation with {IMAGE_PATH}...")
30
+ try:
31
+ with open(IMAGE_PATH, "rb") as f:
32
+ files = {"file": f}
33
+ response = requests.post(f"{API_URL}/recommend", files=files)
34
+
35
+ if response.status_code == 200:
36
+ print("Success!")
37
+ print(response.json())
38
+ else:
39
+ print(f"Failed: {response.status_code}")
40
+ print(response.text)
41
+ except Exception as e:
42
+ print(f"Error: {e}")
43
+
44
+ if __name__ == "__main__":
45
+ if wait_for_server():
46
+ test_recommendation()
47
+ else:
48
+ sys.exit(1)
test_results.txt ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Starting simple accuracy test...
2
+ Classes found: ['Heart', 'Oblong', 'Oval', 'Round', 'Square']
3
+ Testing Heart with ../dataset/test/Heart/Heart(0).jpg
4
+ Result for Heart: {'oblong': 0.2003, 'heart': 0.2001, 'square': 0.2001, 'oval': 0.2, 'round': 0.1995}
5
+ Testing Oblong with ../dataset/test/Oblong/Oblong(0).jpg
6
+ Result for Oblong: {'square': 0.2005, 'heart': 0.2001, 'oval': 0.2001, 'oblong': 0.2, 'round': 0.1992}
7
+ Testing Oval with ../dataset/test/Oval/Oval(0).jpg
8
+ Result for Oval: {'square': 0.2013, 'oblong': 0.2005, 'heart': 0.2002, 'oval': 0.2001, 'round': 0.1979}
9
+ Testing Round with ../dataset/test/Round/Round(0).jpg
10
+ Result for Round: {'square': 0.2005, 'oblong': 0.2002, 'oval': 0.2002, 'round': 0.1996, 'heart': 0.1995}
11
+ Testing Square with ../dataset/test/Square/Square(0).jpg
12
+ Result for Square: {'heart': 0.2005, 'oval': 0.2003, 'round': 0.1999, 'square': 0.1998, 'oblong': 0.1995}
train.py ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import cv2
3
+ import numpy as np
4
+ import pickle
5
+ from sklearn.ensemble import RandomForestClassifier
6
+ from sklearn.model_selection import train_test_split
7
+ from sklearn.metrics import accuracy_score, classification_report
8
+ from sklearn.preprocessing import LabelEncoder
9
+
10
+ # Configuration
11
+ DATASET_DIR = "/home/codernotme/Projects/Github/katariaoptics/dataset"
12
+ MODEL_PATH = "/home/codernotme/Projects/Github/katariaoptics/ai_service/face_shape_model.pkl"
13
+ IMG_SIZE = (64, 64) # Resize detected faces to this size
14
+
15
+ # Load Haar Cascade for face detection
16
+ cascade_path = cv2.data.haarcascades + 'haarcascade_frontalface_default.xml'
17
+ face_cascade = cv2.CascadeClassifier(cascade_path)
18
+
19
+ def load_dataset():
20
+ images = []
21
+ labels = []
22
+
23
+ # Expected classes
24
+ classes = ["heart", "oblong", "oval", "round", "square"]
25
+
26
+ print(f"Loading dataset from {DATASET_DIR}...")
27
+
28
+ for label in classes:
29
+ folder_path = os.path.join(DATASET_DIR, label)
30
+ if not os.path.isdir(folder_path):
31
+ print(f"Warning: Folder {folder_path} not found.")
32
+ continue
33
+
34
+ print(f"Processing class: {label}")
35
+ count = 0
36
+ total_files = len(os.listdir(folder_path))
37
+
38
+ for i, filename in enumerate(os.listdir(folder_path)):
39
+ if i % 50 == 0:
40
+ print(f" Processed {i}/{total_files} images...")
41
+
42
+ img_path = os.path.join(folder_path, filename)
43
+ try:
44
+ # Read image
45
+ img = cv2.imread(img_path)
46
+ if img is None:
47
+ continue
48
+
49
+ # Resize if huge to speed up detection
50
+ h, w = img.shape[:2]
51
+ if w > 1000:
52
+ scale = 1000 / w
53
+ img = cv2.resize(img, (1000, int(h * scale)))
54
+
55
+ # Convert to grayscale
56
+ gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
57
+
58
+ # Detect faces
59
+ faces = face_cascade.detectMultiScale(gray, 1.1, 4)
60
+
61
+ # If face detected, crop and use it
62
+ if len(faces) > 0:
63
+ # Use the largest face
64
+ (x, y, w, h) = max(faces, key=lambda f: f[2] * f[3])
65
+ face_roi = gray[y:y+h, x:x+w]
66
+
67
+ # Resize and flatten
68
+ resized = cv2.resize(face_roi, IMG_SIZE)
69
+ flat_features = resized.flatten()
70
+
71
+ images.append(flat_features)
72
+ labels.append(label)
73
+ count += 1
74
+ except Exception as e:
75
+ print(f"Error processing {img_path}: {e}")
76
+
77
+ print(f" Loaded {count} images for {label}")
78
+
79
+ return np.array(images), np.array(labels)
80
+
81
+ def train_model():
82
+ X, y = load_dataset()
83
+
84
+ if len(X) == 0:
85
+ print("Error: No images loaded. Dataset might be empty or paths incorrect.")
86
+ return
87
+
88
+ print(f"Total dataset size: {len(X)} samples")
89
+
90
+ # Encode labels
91
+ le = LabelEncoder()
92
+ y_encoded = le.fit_transform(y)
93
+
94
+ # Save label encoder classes for inference
95
+ with open(MODEL_PATH.replace(".pkl", "_classes.pkl"), "wb") as f:
96
+ pickle.dump(le.classes_, f)
97
+ print(f"Saved class labels: {le.classes_}")
98
+
99
+ # Split dataset
100
+ X_train, X_test, y_train, y_test = train_test_split(X, y_encoded, test_size=0.2, random_state=42)
101
+
102
+ # Train Random Forest
103
+ print("Training Random Forest Classifier...")
104
+ clf = RandomForestClassifier(n_estimators=100, random_state=42)
105
+ clf.fit(X_train, y_train)
106
+
107
+ # Evaluate
108
+ y_pred = clf.predict(X_test)
109
+ accuracy = accuracy_score(y_test, y_pred)
110
+ print(f"Model Accuracy: {accuracy * 100:.2f}%")
111
+ print("\nClassification Report:")
112
+ print(classification_report(y_test, y_pred, target_names=le.classes_))
113
+
114
+ # Save model
115
+ with open(MODEL_PATH, "wb") as f:
116
+ pickle.dump(clf, f)
117
+ print(f"Model saved to {MODEL_PATH}")
118
+
119
+ if __name__ == "__main__":
120
+ train_model()
train_means.py ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import pickle
3
+ import numpy as np
4
+ from PIL import Image
5
+
6
+ # Configuration
7
+ # Use local paths relative to the script location
8
+ BASE_DIR = os.path.dirname(os.path.abspath(__file__))
9
+ DATASET_DIR = os.environ.get("DATASET_DIR", os.path.join(BASE_DIR, "dataset"))
10
+ MODEL_PATH = os.environ.get("MODEL_PATH", os.path.join(BASE_DIR, "face_shape_means.pkl"))
11
+ IMG_SIZE = (64, 64)
12
+
13
+
14
+ def load_and_compute_means():
15
+ classes = ["heart", "oblong", "oval", "round", "square"]
16
+ class_means = {}
17
+
18
+ print(f"Dataset Dir: {DATASET_DIR}")
19
+
20
+ for label in classes:
21
+ folder_path = os.path.join(DATASET_DIR, label)
22
+ if not os.path.isdir(folder_path):
23
+ print(f"Warning: Folder {folder_path} not found")
24
+ continue
25
+
26
+ print(f"Processing {label}...")
27
+ vectors = []
28
+
29
+ files = os.listdir(folder_path)
30
+ total = len(files)
31
+
32
+ for i, filename in enumerate(files):
33
+ if i % 100 == 0:
34
+ print(f" {i}/{total}")
35
+
36
+ try:
37
+ img_path = os.path.join(folder_path, filename)
38
+ with Image.open(img_path) as img:
39
+ # Convert to grayscale and resize
40
+ img = img.convert("L").resize(IMG_SIZE)
41
+ # Flatten
42
+ arr = np.array(img).flatten()
43
+ vectors.append(arr)
44
+ except Exception as e:
45
+ # print(f"Error {filename}: {e}")
46
+ pass
47
+
48
+ if vectors:
49
+ # Compute mean vector for this class
50
+ mean_vector = np.mean(vectors, axis=0)
51
+ class_means[label] = mean_vector
52
+ print(f"Computed mean for {label} from {len(vectors)} images.")
53
+ else:
54
+ print(f"No valid images for {label}!")
55
+
56
+ # Save means
57
+ with open(MODEL_PATH, "wb") as f:
58
+ pickle.dump(class_means, f)
59
+ print(f"Saved mean vectors to {MODEL_PATH}")
60
+
61
+ if __name__ == "__main__":
62
+ load_and_compute_means()
train_model.py ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import cv2
3
+ import glob
4
+ import numpy as np
5
+ import joblib
6
+ from sklearn.model_selection import train_test_split
7
+ from sklearn.svm import SVC
8
+ from sklearn.preprocessing import LabelEncoder, StandardScaler
9
+ from sklearn.pipeline import make_pipeline
10
+ from sklearn.metrics import classification_report
11
+
12
+ from landmarks import get_landmarks
13
+ from geometry import extract_features
14
+
15
+ DATASET_DIR = "../dataset"
16
+ MODEL_PATH = "face_shape_model.pkl"
17
+
18
+ def get_feature_vector(features):
19
+ """Converts feature dictionary to a list for the model."""
20
+ return [
21
+ features["lw_ratio"],
22
+ features["jaw_ratio"],
23
+ features["forehead_ratio"],
24
+ # Add more if geometry.py calculates them
25
+ ]
26
+
27
+ def train():
28
+ print("Starting training...")
29
+ X = []
30
+ y = []
31
+
32
+ # Iterate through each subdirectory in the dataset
33
+ # Assumes structure: dataset/ShapeName/image.jpg
34
+ if not os.path.exists(DATASET_DIR):
35
+ print(f"Dataset directory not found: {DATASET_DIR}")
36
+ return
37
+
38
+ classes = [d for d in os.listdir(DATASET_DIR) if os.path.isdir(os.path.join(DATASET_DIR, d))]
39
+ print(f"Found classes: {classes}")
40
+
41
+ for label in classes:
42
+ class_dir = os.path.join(DATASET_DIR, label)
43
+ image_files = glob.glob(os.path.join(class_dir, "*"))
44
+
45
+ # Normalize label to capitalized format (e.g., "oval" -> "Oval") to match recommendation engine keys
46
+ normalized_label = label.capitalize()
47
+
48
+ print(f"Processing {label} (normalized to {normalized_label}): {len(image_files)} images")
49
+
50
+ for img_path in image_files:
51
+ try:
52
+ landmarks = get_landmarks(img_path)
53
+ feats = extract_features(landmarks)
54
+ vector = get_feature_vector(feats)
55
+
56
+ X.append(vector)
57
+ y.append(normalized_label)
58
+ except Exception as e:
59
+ # print(f"Skipping {img_path}: {e}")
60
+ pass
61
+
62
+ if len(X) == 0:
63
+ print("No valid data found. Check dataset and landmarks extraction.")
64
+ return
65
+
66
+ X = np.array(X)
67
+ y = np.array(y)
68
+
69
+ print(f"Training on {len(X)} samples...")
70
+
71
+ # Train/Test split
72
+ X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
73
+
74
+ # Pipeline: Scale features -> SVM Classifier
75
+ model = make_pipeline(StandardScaler(), SVC(kernel='rbf', probability=True))
76
+ model.fit(X_train, y_train)
77
+
78
+ # Evaluate
79
+ print("Evaluating model...")
80
+ predictions = model.predict(X_test)
81
+ print(classification_report(y_test, predictions))
82
+
83
+ # Save
84
+ joblib.dump(model, MODEL_PATH)
85
+ print(f"Model saved to {MODEL_PATH}")
86
+
87
+ if __name__ == "__main__":
88
+ train()
verify_simple.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+ from detect import detect_face_shape
4
+
5
+ # Add current dir to path logic if needed, but running from ai_service should be fine
6
+ print("Starting simple verification...")
7
+
8
+ DATASET_DIR = "../dataset"
9
+ # Pick a file
10
+ if os.path.exists(DATASET_DIR):
11
+ heart_dir = os.path.join(DATASET_DIR, "heart")
12
+ if os.path.exists(heart_dir):
13
+ files = os.listdir(heart_dir)
14
+ if files:
15
+ img_path = os.path.join(heart_dir, files[0])
16
+ print(f"Testing {img_path}")
17
+ try:
18
+ result = detect_face_shape(img_path)
19
+ print(f"Result: {result}")
20
+ except Exception as e:
21
+ print(f"Error: {e}")
22
+ else:
23
+ print("No files in heart dir")
24
+ else:
25
+ print("No heart dir")
26
+ else:
27
+ print("No dataset dir")