hamxaameer commited on
Commit
bd4265f
·
verified ·
1 Parent(s): ac3c7f1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +139 -7
app.py CHANGED
@@ -460,25 +460,125 @@ class BrandCatalog:
460
  ranked = [(i, sims[i]) for i in np.argsort(sims)[::-1][:top_k]]
461
  return [dict(self.items[i], score=float(s), source='Catalog') for i, s in ranked]
462
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
463
  # ============================================
464
  # Outfit Recommender
465
  # ============================================
466
 
467
  class OutfitRecommender:
468
- def __init__(self, pipeline, color_engine, catalog):
469
  self.pipe = pipeline
470
  self.ce = color_engine
471
  self.cat = catalog
 
472
 
473
  def recommend(self, image_path, top_k=5, style='Casual', occasion='everyday',
474
- complement=True, query_category=None):
475
  qf = self.pipe.extractor.extract_all_features(image_path, query_category or 'query')
476
  qe = self.pipe.embed(image_path)
477
 
478
  cat_filter = _get_complement_cats(query_category or '') if complement else None
479
 
480
  results = []
481
- if self.cat and self.cat.items:
 
 
 
 
482
  results.extend(self.cat.search(qe, top_k * 2, cat_filter))
483
 
484
  for r in results:
@@ -632,7 +732,11 @@ print(f" ❌ Skipped - no image path: {skipped_no_path}")
632
  print(f" ❌ Skipped - file not found: {skipped_no_file}")
633
  print(f" ❌ Skipped - processing error: {processed_error}")
634
 
635
- outfit_recommender = OutfitRecommender(pipeline, color_engine, brand_catalog)
 
 
 
 
636
 
637
  if len(brand_catalog.items) > 0:
638
  print(f"\n✅ System ready with {len(brand_catalog.items)} clothing items!")
@@ -651,17 +755,45 @@ def _save_temp(img_array, name='query'):
651
  Image.fromarray(img_array).save(path, quality=95)
652
  return path
653
 
654
- def fn_recommend(image, style, occasion, complement, query_cat):
655
  if image is None:
656
  return [], "⚠️ Please upload an image."
657
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
658
  tmp = _save_temp(image)
659
  res = outfit_recommender.recommend(
660
  tmp, top_k=6, style=style, occasion=occasion,
661
- complement=complement, query_category=query_cat if query_cat != 'Auto' else None)
 
662
 
663
  gallery = []
664
- lines = [f"🔍 Detected: Colors={', '.join(res['query_features'].get('colors',[])[:3])} | Style={res['query_features'].get('style','N/A')}"]
 
665
 
666
  _qcat = query_cat if query_cat != 'Auto' else 'Auto-detected'
667
  _comp_cats = _get_complement_cats(query_cat if query_cat != 'Auto' else '')
 
460
  ranked = [(i, sims[i]) for i in np.argsort(sims)[::-1][:top_k]]
461
  return [dict(self.items[i], score=float(s), source='Catalog') for i, s in ranked]
462
 
463
+ # ============================================
464
+ # Personal Wardrobe (User Upload)
465
+ # ============================================
466
+
467
+ class PersonalWardrobe:
468
+ """
469
+ Manages user's personal wardrobe - uploaded images in session
470
+ Items stored in memory (not persistent across sessions)
471
+ """
472
+ def __init__(self, pipeline, color_engine):
473
+ self.pipe = pipeline
474
+ self.color_engine = color_engine
475
+ self.items = []
476
+ self.embeddings = None
477
+
478
+ def add_item(self, image_path, category='Unknown', name=None):
479
+ """Add a clothing item to personal wardrobe"""
480
+ if not os.path.exists(image_path):
481
+ return None
482
+
483
+ try:
484
+ # Extract features
485
+ features = self.pipe.extractor.extract_all_features(image_path, category)
486
+
487
+ # Compute embedding
488
+ emb = self.pipe.embed(image_path)
489
+
490
+ item = {
491
+ 'id': len(self.items),
492
+ 'name': name or f"My {category} #{len(self.items)+1}",
493
+ 'image_path': image_path,
494
+ 'category': category,
495
+ 'colors': features['colors'],
496
+ 'style': features['style'],
497
+ 'fabric': features['fabric'],
498
+ 'texture': features.get('texture', 'Solid'),
499
+ }
500
+
501
+ self.items.append(item)
502
+
503
+ # Add embedding
504
+ if self.embeddings is None:
505
+ self.embeddings = emb.reshape(1, -1)
506
+ else:
507
+ self.embeddings = np.vstack([self.embeddings, emb.reshape(1, -1)])
508
+
509
+ return item
510
+ except Exception as e:
511
+ print(f"Error adding item: {e}")
512
+ return None
513
+
514
+ def remove_item(self, item_id):
515
+ """Remove item from wardrobe by ID"""
516
+ if 0 <= item_id < len(self.items):
517
+ self.items.pop(item_id)
518
+ if self.embeddings is not None:
519
+ self.embeddings = np.delete(self.embeddings, item_id, axis=0)
520
+ if len(self.embeddings) == 0:
521
+ self.embeddings = None
522
+ # Re-index remaining items
523
+ for i, it in enumerate(self.items):
524
+ it['id'] = i
525
+ return True
526
+ return False
527
+
528
+ def get_all_items(self):
529
+ """Get all wardrobe items"""
530
+ return self.items
531
+
532
+ def clear_all(self):
533
+ """Clear entire wardrobe"""
534
+ self.items = []
535
+ self.embeddings = None
536
+
537
+ def search(self, query_emb, top_k=5, cat_filter=None):
538
+ """Search wardrobe for matching items"""
539
+ if self.embeddings is None or not self.items:
540
+ return []
541
+
542
+ sims = cosine_similarity([query_emb], self.embeddings)[0]
543
+
544
+ if cat_filter:
545
+ cf_list = cat_filter if isinstance(cat_filter, list) else [cat_filter]
546
+ idxs = [i for i, it in enumerate(self.items)
547
+ if any(cf.lower() in it.get('category', '').lower() for cf in cf_list)]
548
+ if idxs:
549
+ ranked = sorted([(i, sims[i]) for i in idxs],
550
+ key=lambda x: x[1], reverse=True)[:top_k]
551
+ else:
552
+ ranked = [(i, sims[i]) for i in np.argsort(sims)[::-1][:top_k]]
553
+ else:
554
+ ranked = [(i, sims[i]) for i in np.argsort(sims)[::-1][:top_k]]
555
+
556
+ return [dict(self.items[i], score=float(s), source='My Wardrobe') for i, s in ranked]
557
+
558
  # ============================================
559
  # Outfit Recommender
560
  # ============================================
561
 
562
  class OutfitRecommender:
563
+ def __init__(self, pipeline, color_engine, catalog, personal_wardrobe=None):
564
  self.pipe = pipeline
565
  self.ce = color_engine
566
  self.cat = catalog
567
+ self.pw = personal_wardrobe # Personal Wardrobe
568
 
569
  def recommend(self, image_path, top_k=5, style='Casual', occasion='everyday',
570
+ complement=True, query_category=None, use_personal_wardrobe=False):
571
  qf = self.pipe.extractor.extract_all_features(image_path, query_category or 'query')
572
  qe = self.pipe.embed(image_path)
573
 
574
  cat_filter = _get_complement_cats(query_category or '') if complement else None
575
 
576
  results = []
577
+
578
+ # Choose source: Personal Wardrobe or Catalog
579
+ if use_personal_wardrobe and self.pw and self.pw.items:
580
+ results.extend(self.pw.search(qe, top_k * 2, cat_filter))
581
+ elif self.cat and self.cat.items:
582
  results.extend(self.cat.search(qe, top_k * 2, cat_filter))
583
 
584
  for r in results:
 
732
  print(f" ❌ Skipped - file not found: {skipped_no_file}")
733
  print(f" ❌ Skipped - processing error: {processed_error}")
734
 
735
+ # Initialize Personal Wardrobe
736
+ personal_wardrobe = PersonalWardrobe(pipeline, color_engine)
737
+ print(f"\n👤 Personal Wardrobe initialized (0 items)")
738
+
739
+ outfit_recommender = OutfitRecommender(pipeline, color_engine, brand_catalog, personal_wardrobe)
740
 
741
  if len(brand_catalog.items) > 0:
742
  print(f"\n✅ System ready with {len(brand_catalog.items)} clothing items!")
 
755
  Image.fromarray(img_array).save(path, quality=95)
756
  return path
757
 
758
+ def fn_recommend(image, style, occasion, complement, query_cat, use_personal_wardrobe=False):
759
  if image is None:
760
  return [], "⚠️ Please upload an image."
761
 
762
+ # Check which source to use
763
+ if use_personal_wardrobe:
764
+ if len(personal_wardrobe.items) == 0:
765
+ return [], "⚠️ Your personal wardrobe is empty! Upload items first in the 'My Wardrobe' tab."
766
+ else:
767
+ if len(brand_catalog.items) == 0:
768
+ error_msg = """❌ No items in catalog!
769
+
770
+ The system has 0 items because image files are missing.
771
+
772
+ 🔧 To fix this:
773
+ 1. Check that images exist at paths in features_metadata.csv
774
+ 2. Current paths look like: /kaggle/working/preprocessed/...
775
+ 3. Upload images to Hugging Face Space in 'images' folder
776
+ 4. Run fix_image_paths.py to update CSV paths
777
+ 5. Replace features_metadata.csv with the fixed version
778
+
779
+ 📁 Expected structure:
780
+ images/
781
+ ├── tops/
782
+ ├── bottoms/
783
+ ├── outerwear/
784
+ └── dresses/
785
+ """
786
+ return [], error_msg
787
+
788
  tmp = _save_temp(image)
789
  res = outfit_recommender.recommend(
790
  tmp, top_k=6, style=style, occasion=occasion,
791
+ complement=complement, query_category=query_cat if query_cat != 'Auto' else None,
792
+ use_personal_wardrobe=use_personal_wardrobe)
793
 
794
  gallery = []
795
+ source_label = "🏠 My Wardrobe" if use_personal_wardrobe else "🏬 Brand Catalog"
796
+ lines = [f"{source_label} | 🔍 Detected: Colors={', '.join(res['query_features'].get('colors',[])[:3])} | Style={res['query_features'].get('style','N/A')}"]
797
 
798
  _qcat = query_cat if query_cat != 'Auto' else 'Auto-detected'
799
  _comp_cats = _get_complement_cats(query_cat if query_cat != 'Auto' else '')