Spaces:
Sleeping
Sleeping
Commit
Β·
f8b306b
1
Parent(s):
c96ca80
Integrate DeepFashion2 dataset: add evaluation module, utilities, and API endpoints for dataset management and analysis
Browse files- DEEPFASHION2_INTEGRATION.md +243 -0
- deepfashion2_evaluation.py +405 -0
- deepfashion2_utils.py +280 -0
- fast.py +196 -0
- requirements.txt +3 -0
DEEPFASHION2_INTEGRATION.md
ADDED
@@ -0,0 +1,243 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# DeepFashion2 Dataset Integration
|
2 |
+
|
3 |
+
This document describes the integration of the DeepFashion2 dataset with the Vestiq fashion analysis system.
|
4 |
+
|
5 |
+
## Overview
|
6 |
+
|
7 |
+
DeepFashion2 is a comprehensive fashion dataset that provides:
|
8 |
+
- 491K diverse images of 13 popular clothing categories
|
9 |
+
- Bounding box annotations for fashion items
|
10 |
+
- Dense pose estimation
|
11 |
+
- Commercial-consumer clothes correspondence
|
12 |
+
- Scale, occlusion, zoom-in, and viewpoint labels
|
13 |
+
|
14 |
+
## Integration Features
|
15 |
+
|
16 |
+
### 1. Dataset Loading and Processing
|
17 |
+
- **DeepFashion2Dataset**: PyTorch dataset class for loading images and annotations
|
18 |
+
- **Category Mapping**: Maps DeepFashion2 categories to yainage90 model categories
|
19 |
+
- **Data Transforms**: Standard preprocessing for fashion images
|
20 |
+
- **Batch Processing**: Efficient DataLoader implementation
|
21 |
+
|
22 |
+
### 2. Evaluation Framework
|
23 |
+
- **Detection Accuracy**: Evaluate fashion object detection performance
|
24 |
+
- **Feature Quality**: Assess feature extraction capabilities
|
25 |
+
- **Classification Metrics**: Precision, recall, F1-score, confusion matrix
|
26 |
+
- **Visualization**: Confusion matrix plots and performance charts
|
27 |
+
|
28 |
+
### 3. API Endpoints
|
29 |
+
- `/deepfashion2/status` - Check integration status and dataset availability
|
30 |
+
- `/deepfashion2/statistics` - Get dataset statistics and category distribution
|
31 |
+
- `/deepfashion2/evaluate` - Run evaluation using DeepFashion2 as benchmark
|
32 |
+
- `/deepfashion2/setup-instructions` - Get setup instructions for the dataset
|
33 |
+
|
34 |
+
## Category Mapping
|
35 |
+
|
36 |
+
DeepFashion2 uses 13 detailed categories that are mapped to yainage90's 7 categories:
|
37 |
+
|
38 |
+
| DeepFashion2 Category | yainage90 Category |
|
39 |
+
|----------------------|-------------------|
|
40 |
+
| short_sleeved_shirt | top |
|
41 |
+
| long_sleeved_shirt | top |
|
42 |
+
| short_sleeved_outwear| outer |
|
43 |
+
| long_sleeved_outwear | outer |
|
44 |
+
| vest | top |
|
45 |
+
| sling | top |
|
46 |
+
| shorts | bottom |
|
47 |
+
| trousers | bottom |
|
48 |
+
| skirt | bottom |
|
49 |
+
| short_sleeved_dress | dress |
|
50 |
+
| long_sleeved_dress | dress |
|
51 |
+
| vest_dress | dress |
|
52 |
+
| sling_dress | dress |
|
53 |
+
|
54 |
+
## Setup Instructions
|
55 |
+
|
56 |
+
### 1. Download the Dataset
|
57 |
+
|
58 |
+
The DeepFashion2 dataset requires manual download due to licensing requirements:
|
59 |
+
|
60 |
+
1. Visit the official repository: https://github.com/switchablenorms/DeepFashion2
|
61 |
+
2. Follow the dataset download instructions
|
62 |
+
3. Register and download the dataset files
|
63 |
+
|
64 |
+
### 2. Dataset Structure
|
65 |
+
|
66 |
+
Extract the dataset to `./data/deepfashion2/` with the following structure:
|
67 |
+
|
68 |
+
```
|
69 |
+
deepfashion2/
|
70 |
+
βββ train/
|
71 |
+
β βββ image/ # Training images
|
72 |
+
β βββ annos/ # Training annotations (JSON)
|
73 |
+
βββ validation/
|
74 |
+
β βββ image/ # Validation images
|
75 |
+
β βββ annos/ # Validation annotations (JSON)
|
76 |
+
βββ test/
|
77 |
+
βββ image/ # Test images
|
78 |
+
βββ annos/ # Test annotations (JSON)
|
79 |
+
```
|
80 |
+
|
81 |
+
### 3. Install Dependencies
|
82 |
+
|
83 |
+
Install additional dependencies for evaluation:
|
84 |
+
|
85 |
+
```bash
|
86 |
+
pip install scikit-learn matplotlib seaborn
|
87 |
+
```
|
88 |
+
|
89 |
+
### 4. Verify Setup
|
90 |
+
|
91 |
+
Check the integration status:
|
92 |
+
|
93 |
+
```bash
|
94 |
+
curl http://localhost:7861/deepfashion2/status
|
95 |
+
```
|
96 |
+
|
97 |
+
## Usage Examples
|
98 |
+
|
99 |
+
### 1. Basic Dataset Loading
|
100 |
+
|
101 |
+
```python
|
102 |
+
from deepfashion2_utils import DeepFashion2Config, DeepFashion2Dataset
|
103 |
+
|
104 |
+
config = DeepFashion2Config()
|
105 |
+
dataset = DeepFashion2Dataset(
|
106 |
+
root_dir=config.dataset_root,
|
107 |
+
split='validation',
|
108 |
+
load_annotations=True
|
109 |
+
)
|
110 |
+
|
111 |
+
# Get a sample
|
112 |
+
sample = dataset[0]
|
113 |
+
print(f"Image: {sample['image_path']}")
|
114 |
+
print(f"Categories: {dataset.get_categories_in_image(sample['annotations'])}")
|
115 |
+
```
|
116 |
+
|
117 |
+
### 2. Running Evaluation
|
118 |
+
|
119 |
+
```python
|
120 |
+
from deepfashion2_evaluation import run_full_evaluation
|
121 |
+
from fast import analyzer
|
122 |
+
|
123 |
+
# Run evaluation with 100 samples
|
124 |
+
report_path = run_full_evaluation(analyzer, max_samples=100)
|
125 |
+
print(f"Evaluation report saved to: {report_path}")
|
126 |
+
```
|
127 |
+
|
128 |
+
### 3. API Usage
|
129 |
+
|
130 |
+
```bash
|
131 |
+
# Check status
|
132 |
+
curl -X GET "http://localhost:7861/deepfashion2/status"
|
133 |
+
|
134 |
+
# Get dataset statistics
|
135 |
+
curl -X GET "http://localhost:7861/deepfashion2/statistics"
|
136 |
+
|
137 |
+
# Run evaluation
|
138 |
+
curl -X POST "http://localhost:7861/deepfashion2/evaluate?max_samples=50"
|
139 |
+
|
140 |
+
# Get setup instructions
|
141 |
+
curl -X GET "http://localhost:7861/deepfashion2/setup-instructions"
|
142 |
+
```
|
143 |
+
|
144 |
+
## Evaluation Metrics
|
145 |
+
|
146 |
+
### Detection Accuracy
|
147 |
+
- **Category-level accuracy**: How well the model detects clothing categories
|
148 |
+
- **Detection score**: IoU-like metric for category overlap
|
149 |
+
- **Confusion matrix**: Detailed breakdown of predictions vs ground truth
|
150 |
+
|
151 |
+
### Feature Quality
|
152 |
+
- **Feature dimension**: Dimensionality of extracted features
|
153 |
+
- **Intra-category similarity**: How similar features are within the same category
|
154 |
+
- **Inter-category distance**: How well features separate different categories
|
155 |
+
- **Feature separability**: Overall quality metric for feature discrimination
|
156 |
+
|
157 |
+
## Configuration Options
|
158 |
+
|
159 |
+
### DeepFashion2Config
|
160 |
+
|
161 |
+
```python
|
162 |
+
@dataclass
|
163 |
+
class DeepFashion2Config:
|
164 |
+
dataset_root: str = "./data/deepfashion2"
|
165 |
+
categories: List[str] = None # Auto-populated with 13 categories
|
166 |
+
image_size: Tuple[int, int] = (224, 224)
|
167 |
+
batch_size: int = 32
|
168 |
+
num_workers: int = 4
|
169 |
+
```
|
170 |
+
|
171 |
+
### Customization
|
172 |
+
|
173 |
+
You can customize the configuration for your specific needs:
|
174 |
+
|
175 |
+
```python
|
176 |
+
config = DeepFashion2Config(
|
177 |
+
dataset_root="/path/to/your/deepfashion2",
|
178 |
+
image_size=(256, 256),
|
179 |
+
batch_size=16
|
180 |
+
)
|
181 |
+
```
|
182 |
+
|
183 |
+
## Performance Considerations
|
184 |
+
|
185 |
+
### Memory Usage
|
186 |
+
- The dataset is large (~15GB), ensure sufficient disk space
|
187 |
+
- Use appropriate batch sizes based on available GPU memory
|
188 |
+
- Consider using `num_workers` for faster data loading
|
189 |
+
|
190 |
+
### CPU Optimization
|
191 |
+
- The system automatically detects CPU vs GPU and optimizes accordingly
|
192 |
+
- CPU inference uses float32 precision and limited threads
|
193 |
+
- GPU inference uses float16 precision for better performance
|
194 |
+
|
195 |
+
### Evaluation Speed
|
196 |
+
- Limit `max_samples` for faster evaluation during development
|
197 |
+
- Full evaluation on the entire validation set may take significant time
|
198 |
+
- Consider running evaluations on a subset for quick feedback
|
199 |
+
|
200 |
+
## Troubleshooting
|
201 |
+
|
202 |
+
### Common Issues
|
203 |
+
|
204 |
+
1. **Dataset not found**: Ensure the dataset is extracted to the correct path
|
205 |
+
2. **Permission errors**: Check file permissions for the dataset directory
|
206 |
+
3. **Memory errors**: Reduce batch size or number of workers
|
207 |
+
4. **Import errors**: Install missing dependencies (scikit-learn, matplotlib, seaborn)
|
208 |
+
|
209 |
+
### Debug Mode
|
210 |
+
|
211 |
+
Enable debug logging to troubleshoot issues:
|
212 |
+
|
213 |
+
```python
|
214 |
+
import logging
|
215 |
+
logging.basicConfig(level=logging.DEBUG)
|
216 |
+
```
|
217 |
+
|
218 |
+
## Future Enhancements
|
219 |
+
|
220 |
+
### Planned Features
|
221 |
+
- **Training Pipeline**: Fine-tune models on DeepFashion2 data
|
222 |
+
- **Advanced Metrics**: Add more sophisticated evaluation metrics
|
223 |
+
- **Visualization Tools**: Enhanced plotting and analysis tools
|
224 |
+
- **Benchmark Comparisons**: Compare against other fashion datasets
|
225 |
+
|
226 |
+
### Contributing
|
227 |
+
|
228 |
+
To contribute to the DeepFashion2 integration:
|
229 |
+
|
230 |
+
1. Fork the repository
|
231 |
+
2. Create a feature branch
|
232 |
+
3. Add tests for new functionality
|
233 |
+
4. Submit a pull request
|
234 |
+
|
235 |
+
## References
|
236 |
+
|
237 |
+
- [DeepFashion2 Paper](https://arxiv.org/abs/1901.07973)
|
238 |
+
- [DeepFashion2 Repository](https://github.com/switchablenorms/DeepFashion2)
|
239 |
+
- [yainage90 Models](https://huggingface.co/yainage90)
|
240 |
+
|
241 |
+
## License
|
242 |
+
|
243 |
+
This integration follows the same license as the main Vestiq project. The DeepFashion2 dataset has its own licensing terms that must be respected.
|
deepfashion2_evaluation.py
ADDED
@@ -0,0 +1,405 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
DeepFashion2 Evaluation Module
|
3 |
+
Provides evaluation capabilities using DeepFashion2 dataset as benchmark
|
4 |
+
for the Vestiq fashion analysis system.
|
5 |
+
"""
|
6 |
+
|
7 |
+
import torch
|
8 |
+
import numpy as np
|
9 |
+
from typing import Dict, List, Tuple, Optional
|
10 |
+
from sklearn.metrics import accuracy_score, precision_recall_fscore_support, confusion_matrix
|
11 |
+
import matplotlib.pyplot as plt
|
12 |
+
import seaborn as sns
|
13 |
+
from pathlib import Path
|
14 |
+
import json
|
15 |
+
from tqdm import tqdm
|
16 |
+
|
17 |
+
from deepfashion2_utils import (
|
18 |
+
DeepFashion2Config,
|
19 |
+
DeepFashion2Dataset,
|
20 |
+
DeepFashion2CategoryMapper,
|
21 |
+
create_deepfashion2_dataloader
|
22 |
+
)
|
23 |
+
|
24 |
+
class DeepFashion2Evaluator:
|
25 |
+
"""Evaluate fashion models using DeepFashion2 dataset"""
|
26 |
+
|
27 |
+
def __init__(self, config: DeepFashion2Config, analyzer=None):
|
28 |
+
"""
|
29 |
+
Initialize evaluator
|
30 |
+
|
31 |
+
Args:
|
32 |
+
config: DeepFashion2 configuration
|
33 |
+
analyzer: HuggingFaceFashionAnalyzer instance
|
34 |
+
"""
|
35 |
+
self.config = config
|
36 |
+
self.analyzer = analyzer
|
37 |
+
self.category_mapper = DeepFashion2CategoryMapper()
|
38 |
+
self.results = {}
|
39 |
+
|
40 |
+
def evaluate_detection_accuracy(self, split: str = 'validation',
|
41 |
+
max_samples: Optional[int] = None) -> Dict:
|
42 |
+
"""
|
43 |
+
Evaluate fashion object detection accuracy on DeepFashion2
|
44 |
+
|
45 |
+
Args:
|
46 |
+
split: Dataset split to evaluate on
|
47 |
+
max_samples: Maximum number of samples to evaluate (None for all)
|
48 |
+
|
49 |
+
Returns:
|
50 |
+
Dictionary containing evaluation metrics
|
51 |
+
"""
|
52 |
+
if not self.analyzer:
|
53 |
+
raise ValueError("Analyzer not provided")
|
54 |
+
|
55 |
+
print(f"Evaluating detection accuracy on {split} split...")
|
56 |
+
|
57 |
+
# Load dataset
|
58 |
+
dataset = DeepFashion2Dataset(
|
59 |
+
root_dir=self.config.dataset_root,
|
60 |
+
split=split,
|
61 |
+
transform=None,
|
62 |
+
load_annotations=True
|
63 |
+
)
|
64 |
+
|
65 |
+
if max_samples:
|
66 |
+
dataset.image_files = dataset.image_files[:max_samples]
|
67 |
+
|
68 |
+
# Evaluation metrics
|
69 |
+
true_categories = []
|
70 |
+
predicted_categories = []
|
71 |
+
detection_scores = []
|
72 |
+
|
73 |
+
for i in tqdm(range(len(dataset)), desc="Evaluating detection"):
|
74 |
+
try:
|
75 |
+
item = dataset[i]
|
76 |
+
image_path = item['image_path']
|
77 |
+
annotations = item['annotations']
|
78 |
+
|
79 |
+
# Get ground truth categories
|
80 |
+
gt_categories = dataset.get_categories_in_image(annotations)
|
81 |
+
gt_yainage_categories = [
|
82 |
+
self.category_mapper.map_to_yainage90(cat)
|
83 |
+
for cat in gt_categories
|
84 |
+
]
|
85 |
+
gt_yainage_categories = list(set(gt_yainage_categories))
|
86 |
+
|
87 |
+
if not gt_yainage_categories:
|
88 |
+
continue
|
89 |
+
|
90 |
+
# Get model predictions
|
91 |
+
with open(image_path, 'rb') as f:
|
92 |
+
image_bytes = f.read()
|
93 |
+
|
94 |
+
detection_results = self.analyzer.detect_fashion_objects(
|
95 |
+
self.analyzer.process_image_from_bytes(image_bytes)
|
96 |
+
)
|
97 |
+
|
98 |
+
if 'detected_items' in detection_results:
|
99 |
+
pred_categories = [
|
100 |
+
item['category'] for item in detection_results['detected_items']
|
101 |
+
if item['confidence'] > 0.5
|
102 |
+
]
|
103 |
+
pred_categories = list(set(pred_categories))
|
104 |
+
|
105 |
+
# Calculate detection score (IoU-like for categories)
|
106 |
+
if pred_categories and gt_yainage_categories:
|
107 |
+
intersection = set(pred_categories) & set(gt_yainage_categories)
|
108 |
+
union = set(pred_categories) | set(gt_yainage_categories)
|
109 |
+
score = len(intersection) / len(union) if union else 0
|
110 |
+
detection_scores.append(score)
|
111 |
+
|
112 |
+
# Store for classification metrics
|
113 |
+
for gt_cat in gt_yainage_categories:
|
114 |
+
true_categories.append(gt_cat)
|
115 |
+
predicted_categories.append(
|
116 |
+
gt_cat if gt_cat in pred_categories else 'none'
|
117 |
+
)
|
118 |
+
|
119 |
+
except Exception as e:
|
120 |
+
print(f"Error processing image {i}: {e}")
|
121 |
+
continue
|
122 |
+
|
123 |
+
# Calculate metrics
|
124 |
+
metrics = self._calculate_classification_metrics(
|
125 |
+
true_categories, predicted_categories
|
126 |
+
)
|
127 |
+
|
128 |
+
metrics['detection_scores'] = detection_scores
|
129 |
+
metrics['mean_detection_score'] = np.mean(detection_scores) if detection_scores else 0
|
130 |
+
metrics['num_samples'] = len(dataset)
|
131 |
+
|
132 |
+
self.results['detection_accuracy'] = metrics
|
133 |
+
return metrics
|
134 |
+
|
135 |
+
def evaluate_feature_extraction(self, split: str = 'validation',
|
136 |
+
max_samples: Optional[int] = None) -> Dict:
|
137 |
+
"""
|
138 |
+
Evaluate feature extraction quality using DeepFashion2
|
139 |
+
|
140 |
+
Args:
|
141 |
+
split: Dataset split to evaluate on
|
142 |
+
max_samples: Maximum number of samples to evaluate
|
143 |
+
|
144 |
+
Returns:
|
145 |
+
Dictionary containing feature evaluation metrics
|
146 |
+
"""
|
147 |
+
if not self.analyzer:
|
148 |
+
raise ValueError("Analyzer not provided")
|
149 |
+
|
150 |
+
print(f"Evaluating feature extraction on {split} split...")
|
151 |
+
|
152 |
+
dataset = DeepFashion2Dataset(
|
153 |
+
root_dir=self.config.dataset_root,
|
154 |
+
split=split,
|
155 |
+
transform=None,
|
156 |
+
load_annotations=True
|
157 |
+
)
|
158 |
+
|
159 |
+
if max_samples:
|
160 |
+
dataset.image_files = dataset.image_files[:max_samples]
|
161 |
+
|
162 |
+
features_by_category = {}
|
163 |
+
feature_dimensions = []
|
164 |
+
|
165 |
+
for i in tqdm(range(len(dataset)), desc="Extracting features"):
|
166 |
+
try:
|
167 |
+
item = dataset[i]
|
168 |
+
image_path = item['image_path']
|
169 |
+
annotations = item['annotations']
|
170 |
+
|
171 |
+
# Get ground truth categories
|
172 |
+
gt_categories = dataset.get_categories_in_image(annotations)
|
173 |
+
gt_yainage_categories = [
|
174 |
+
self.category_mapper.map_to_yainage90(cat)
|
175 |
+
for cat in gt_categories
|
176 |
+
]
|
177 |
+
|
178 |
+
if not gt_yainage_categories:
|
179 |
+
continue
|
180 |
+
|
181 |
+
# Extract features
|
182 |
+
with open(image_path, 'rb') as f:
|
183 |
+
image_bytes = f.read()
|
184 |
+
|
185 |
+
feature_results = self.analyzer.extract_fashion_features(
|
186 |
+
self.analyzer.process_image_from_bytes(image_bytes)
|
187 |
+
)
|
188 |
+
|
189 |
+
if 'feature_vector' in feature_results:
|
190 |
+
features = np.array(feature_results['feature_vector'])
|
191 |
+
feature_dimensions.append(feature_results['feature_dimension'])
|
192 |
+
|
193 |
+
# Group features by category
|
194 |
+
for category in gt_yainage_categories:
|
195 |
+
if category not in features_by_category:
|
196 |
+
features_by_category[category] = []
|
197 |
+
features_by_category[category].append(features)
|
198 |
+
|
199 |
+
except Exception as e:
|
200 |
+
print(f"Error processing image {i}: {e}")
|
201 |
+
continue
|
202 |
+
|
203 |
+
# Calculate feature quality metrics
|
204 |
+
metrics = {
|
205 |
+
'feature_dimension': np.mean(feature_dimensions) if feature_dimensions else 0,
|
206 |
+
'categories_found': list(features_by_category.keys()),
|
207 |
+
'samples_per_category': {
|
208 |
+
cat: len(feats) for cat, feats in features_by_category.items()
|
209 |
+
}
|
210 |
+
}
|
211 |
+
|
212 |
+
# Calculate intra-category similarity and inter-category distance
|
213 |
+
if len(features_by_category) > 1:
|
214 |
+
intra_similarities = []
|
215 |
+
inter_distances = []
|
216 |
+
|
217 |
+
categories = list(features_by_category.keys())
|
218 |
+
for i, cat1 in enumerate(categories):
|
219 |
+
cat1_features = np.array(features_by_category[cat1])
|
220 |
+
|
221 |
+
# Intra-category similarity
|
222 |
+
if len(cat1_features) > 1:
|
223 |
+
similarities = []
|
224 |
+
for j in range(len(cat1_features)):
|
225 |
+
for k in range(j+1, len(cat1_features)):
|
226 |
+
sim = np.dot(cat1_features[j], cat1_features[k])
|
227 |
+
similarities.append(sim)
|
228 |
+
intra_similarities.extend(similarities)
|
229 |
+
|
230 |
+
# Inter-category distance
|
231 |
+
for j, cat2 in enumerate(categories[i+1:], i+1):
|
232 |
+
cat2_features = np.array(features_by_category[cat2])
|
233 |
+
for feat1 in cat1_features:
|
234 |
+
for feat2 in cat2_features:
|
235 |
+
dist = np.linalg.norm(feat1 - feat2)
|
236 |
+
inter_distances.append(dist)
|
237 |
+
|
238 |
+
metrics['mean_intra_similarity'] = np.mean(intra_similarities) if intra_similarities else 0
|
239 |
+
metrics['mean_inter_distance'] = np.mean(inter_distances) if inter_distances else 0
|
240 |
+
metrics['feature_separability'] = (
|
241 |
+
metrics['mean_inter_distance'] - metrics['mean_intra_similarity']
|
242 |
+
)
|
243 |
+
|
244 |
+
self.results['feature_extraction'] = metrics
|
245 |
+
return metrics
|
246 |
+
|
247 |
+
def _calculate_classification_metrics(self, y_true: List[str],
|
248 |
+
y_pred: List[str]) -> Dict:
|
249 |
+
"""Calculate classification metrics"""
|
250 |
+
if not y_true or not y_pred:
|
251 |
+
return {}
|
252 |
+
|
253 |
+
# Get unique labels
|
254 |
+
labels = list(set(y_true + y_pred))
|
255 |
+
|
256 |
+
# Calculate metrics
|
257 |
+
accuracy = accuracy_score(y_true, y_pred)
|
258 |
+
precision, recall, f1, support = precision_recall_fscore_support(
|
259 |
+
y_true, y_pred, labels=labels, average='weighted', zero_division=0
|
260 |
+
)
|
261 |
+
|
262 |
+
# Per-class metrics
|
263 |
+
precision_per_class, recall_per_class, f1_per_class, support_per_class = \
|
264 |
+
precision_recall_fscore_support(
|
265 |
+
y_true, y_pred, labels=labels, average=None, zero_division=0
|
266 |
+
)
|
267 |
+
|
268 |
+
per_class_metrics = {}
|
269 |
+
for i, label in enumerate(labels):
|
270 |
+
per_class_metrics[label] = {
|
271 |
+
'precision': precision_per_class[i],
|
272 |
+
'recall': recall_per_class[i],
|
273 |
+
'f1': f1_per_class[i],
|
274 |
+
'support': support_per_class[i]
|
275 |
+
}
|
276 |
+
|
277 |
+
return {
|
278 |
+
'accuracy': accuracy,
|
279 |
+
'precision': precision,
|
280 |
+
'recall': recall,
|
281 |
+
'f1': f1,
|
282 |
+
'per_class_metrics': per_class_metrics,
|
283 |
+
'confusion_matrix': confusion_matrix(y_true, y_pred, labels=labels).tolist(),
|
284 |
+
'labels': labels
|
285 |
+
}
|
286 |
+
|
287 |
+
def generate_evaluation_report(self, output_dir: str = "./evaluation_results") -> str:
|
288 |
+
"""Generate comprehensive evaluation report"""
|
289 |
+
output_path = Path(output_dir)
|
290 |
+
output_path.mkdir(exist_ok=True)
|
291 |
+
|
292 |
+
report_file = output_path / "deepfashion2_evaluation_report.json"
|
293 |
+
|
294 |
+
# Compile all results
|
295 |
+
full_report = {
|
296 |
+
'config': {
|
297 |
+
'dataset_root': self.config.dataset_root,
|
298 |
+
'categories': self.config.categories,
|
299 |
+
'image_size': self.config.image_size
|
300 |
+
},
|
301 |
+
'results': self.results,
|
302 |
+
'summary': self._generate_summary()
|
303 |
+
}
|
304 |
+
|
305 |
+
# Save report
|
306 |
+
with open(report_file, 'w') as f:
|
307 |
+
json.dump(full_report, f, indent=2)
|
308 |
+
|
309 |
+
print(f"Evaluation report saved to: {report_file}")
|
310 |
+
return str(report_file)
|
311 |
+
|
312 |
+
def _generate_summary(self) -> Dict:
|
313 |
+
"""Generate evaluation summary"""
|
314 |
+
summary = {}
|
315 |
+
|
316 |
+
if 'detection_accuracy' in self.results:
|
317 |
+
det_results = self.results['detection_accuracy']
|
318 |
+
summary['detection'] = {
|
319 |
+
'accuracy': det_results.get('accuracy', 0),
|
320 |
+
'f1_score': det_results.get('f1', 0),
|
321 |
+
'mean_detection_score': det_results.get('mean_detection_score', 0)
|
322 |
+
}
|
323 |
+
|
324 |
+
if 'feature_extraction' in self.results:
|
325 |
+
feat_results = self.results['feature_extraction']
|
326 |
+
summary['features'] = {
|
327 |
+
'feature_dimension': feat_results.get('feature_dimension', 0),
|
328 |
+
'categories_evaluated': len(feat_results.get('categories_found', [])),
|
329 |
+
'feature_separability': feat_results.get('feature_separability', 0)
|
330 |
+
}
|
331 |
+
|
332 |
+
return summary
|
333 |
+
|
334 |
+
def plot_confusion_matrix(self, output_dir: str = "./evaluation_results"):
|
335 |
+
"""Plot confusion matrix for detection results"""
|
336 |
+
if 'detection_accuracy' not in self.results:
|
337 |
+
print("No detection results available for plotting")
|
338 |
+
return
|
339 |
+
|
340 |
+
results = self.results['detection_accuracy']
|
341 |
+
if 'confusion_matrix' not in results:
|
342 |
+
return
|
343 |
+
|
344 |
+
cm = np.array(results['confusion_matrix'])
|
345 |
+
labels = results['labels']
|
346 |
+
|
347 |
+
plt.figure(figsize=(10, 8))
|
348 |
+
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
|
349 |
+
xticklabels=labels, yticklabels=labels)
|
350 |
+
plt.title('Fashion Object Detection Confusion Matrix')
|
351 |
+
plt.xlabel('Predicted')
|
352 |
+
plt.ylabel('Actual')
|
353 |
+
|
354 |
+
output_path = Path(output_dir)
|
355 |
+
output_path.mkdir(exist_ok=True)
|
356 |
+
plt.savefig(output_path / 'confusion_matrix.png', dpi=300, bbox_inches='tight')
|
357 |
+
plt.close()
|
358 |
+
|
359 |
+
print(f"Confusion matrix saved to: {output_path / 'confusion_matrix.png'}")
|
360 |
+
|
361 |
+
def run_full_evaluation(analyzer, config: Optional[DeepFashion2Config] = None,
|
362 |
+
max_samples: int = 100) -> str:
|
363 |
+
"""
|
364 |
+
Run full evaluation pipeline
|
365 |
+
|
366 |
+
Args:
|
367 |
+
analyzer: HuggingFaceFashionAnalyzer instance
|
368 |
+
config: DeepFashion2 configuration
|
369 |
+
max_samples: Maximum samples to evaluate
|
370 |
+
|
371 |
+
Returns:
|
372 |
+
Path to evaluation report
|
373 |
+
"""
|
374 |
+
if config is None:
|
375 |
+
config = DeepFashion2Config()
|
376 |
+
|
377 |
+
evaluator = DeepFashion2Evaluator(config, analyzer)
|
378 |
+
|
379 |
+
print("Starting DeepFashion2 evaluation...")
|
380 |
+
|
381 |
+
# Run detection evaluation
|
382 |
+
try:
|
383 |
+
evaluator.evaluate_detection_accuracy(max_samples=max_samples)
|
384 |
+
print("β Detection evaluation completed")
|
385 |
+
except Exception as e:
|
386 |
+
print(f"β Detection evaluation failed: {e}")
|
387 |
+
|
388 |
+
# Run feature extraction evaluation
|
389 |
+
try:
|
390 |
+
evaluator.evaluate_feature_extraction(max_samples=max_samples)
|
391 |
+
print("β Feature extraction evaluation completed")
|
392 |
+
except Exception as e:
|
393 |
+
print(f"β Feature extraction evaluation failed: {e}")
|
394 |
+
|
395 |
+
# Generate report
|
396 |
+
report_path = evaluator.generate_evaluation_report()
|
397 |
+
|
398 |
+
# Plot confusion matrix
|
399 |
+
try:
|
400 |
+
evaluator.plot_confusion_matrix()
|
401 |
+
print("β Confusion matrix plotted")
|
402 |
+
except Exception as e:
|
403 |
+
print(f"β Confusion matrix plotting failed: {e}")
|
404 |
+
|
405 |
+
return report_path
|
deepfashion2_utils.py
ADDED
@@ -0,0 +1,280 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
DeepFashion2 Dataset Integration Utilities
|
3 |
+
Provides tools for loading, processing, and using the DeepFashion2 dataset
|
4 |
+
with the Vestiq fashion analysis system.
|
5 |
+
"""
|
6 |
+
|
7 |
+
import os
|
8 |
+
import json
|
9 |
+
import torch
|
10 |
+
import numpy as np
|
11 |
+
from PIL import Image
|
12 |
+
from torch.utils.data import Dataset, DataLoader
|
13 |
+
from pathlib import Path
|
14 |
+
from typing import Dict, List, Tuple, Optional, Union
|
15 |
+
import torchvision.transforms as transforms
|
16 |
+
from dataclasses import dataclass, field
|
17 |
+
import requests
|
18 |
+
import zipfile
|
19 |
+
import shutil
|
20 |
+
|
21 |
+
@dataclass
|
22 |
+
class DeepFashion2Config:
|
23 |
+
"""Configuration for DeepFashion2 dataset"""
|
24 |
+
dataset_root: str = "./data/deepfashion2"
|
25 |
+
download_url: str = "https://github.com/switchablenorms/DeepFashion2/releases/download/v1.0/deepfashion2.zip"
|
26 |
+
categories: List[str] = field(default_factory=list)
|
27 |
+
image_size: Tuple[int, int] = (224, 224)
|
28 |
+
batch_size: int = 32
|
29 |
+
num_workers: int = 4
|
30 |
+
|
31 |
+
def __post_init__(self):
|
32 |
+
if not self.categories:
|
33 |
+
# DeepFashion2 13 categories
|
34 |
+
self.categories = [
|
35 |
+
'short_sleeved_shirt', 'long_sleeved_shirt', 'short_sleeved_outwear',
|
36 |
+
'long_sleeved_outwear', 'vest', 'sling', 'shorts', 'trousers',
|
37 |
+
'skirt', 'short_sleeved_dress', 'long_sleeved_dress', 'vest_dress', 'sling_dress'
|
38 |
+
]
|
39 |
+
|
40 |
+
class DeepFashion2CategoryMapper:
|
41 |
+
"""Maps DeepFashion2 categories to yainage90 model categories"""
|
42 |
+
|
43 |
+
def __init__(self):
|
44 |
+
# Mapping from DeepFashion2 categories to yainage90 categories
|
45 |
+
self.df2_to_yainage90 = {
|
46 |
+
'short_sleeved_shirt': 'top',
|
47 |
+
'long_sleeved_shirt': 'top',
|
48 |
+
'short_sleeved_outwear': 'outer',
|
49 |
+
'long_sleeved_outwear': 'outer',
|
50 |
+
'vest': 'top',
|
51 |
+
'sling': 'top',
|
52 |
+
'shorts': 'bottom',
|
53 |
+
'trousers': 'bottom',
|
54 |
+
'skirt': 'bottom',
|
55 |
+
'short_sleeved_dress': 'dress',
|
56 |
+
'long_sleeved_dress': 'dress',
|
57 |
+
'vest_dress': 'dress',
|
58 |
+
'sling_dress': 'dress'
|
59 |
+
}
|
60 |
+
|
61 |
+
# Reverse mapping
|
62 |
+
self.yainage90_to_df2 = {}
|
63 |
+
for df2_cat, yainage_cat in self.df2_to_yainage90.items():
|
64 |
+
if yainage_cat not in self.yainage90_to_df2:
|
65 |
+
self.yainage90_to_df2[yainage_cat] = []
|
66 |
+
self.yainage90_to_df2[yainage_cat].append(df2_cat)
|
67 |
+
|
68 |
+
def map_to_yainage90(self, df2_category: str) -> str:
|
69 |
+
"""Map DeepFashion2 category to yainage90 category"""
|
70 |
+
return self.df2_to_yainage90.get(df2_category, 'unknown')
|
71 |
+
|
72 |
+
def map_from_yainage90(self, yainage_category: str) -> List[str]:
|
73 |
+
"""Map yainage90 category to DeepFashion2 categories"""
|
74 |
+
return self.yainage90_to_df2.get(yainage_category, [])
|
75 |
+
|
76 |
+
class DeepFashion2Dataset(Dataset):
|
77 |
+
"""PyTorch Dataset for DeepFashion2"""
|
78 |
+
|
79 |
+
def __init__(self,
|
80 |
+
root_dir: str,
|
81 |
+
split: str = 'train',
|
82 |
+
transform: Optional[transforms.Compose] = None,
|
83 |
+
load_annotations: bool = True):
|
84 |
+
"""
|
85 |
+
Initialize DeepFashion2 dataset
|
86 |
+
|
87 |
+
Args:
|
88 |
+
root_dir: Root directory of DeepFashion2 dataset
|
89 |
+
split: Dataset split ('train', 'validation', 'test')
|
90 |
+
transform: Image transformations
|
91 |
+
load_annotations: Whether to load bounding box annotations
|
92 |
+
"""
|
93 |
+
self.root_dir = Path(root_dir)
|
94 |
+
self.split = split
|
95 |
+
self.transform = transform
|
96 |
+
self.load_annotations = load_annotations
|
97 |
+
self.category_mapper = DeepFashion2CategoryMapper()
|
98 |
+
|
99 |
+
# Load dataset metadata
|
100 |
+
self.images_dir = self.root_dir / split / "image"
|
101 |
+
self.annos_dir = self.root_dir / split / "annos"
|
102 |
+
|
103 |
+
# Get all image files
|
104 |
+
self.image_files = []
|
105 |
+
if self.images_dir.exists():
|
106 |
+
self.image_files = list(self.images_dir.glob("*.jpg"))
|
107 |
+
|
108 |
+
print(f"Found {len(self.image_files)} images in {split} split")
|
109 |
+
|
110 |
+
def __len__(self):
|
111 |
+
return len(self.image_files)
|
112 |
+
|
113 |
+
def __getitem__(self, idx):
|
114 |
+
"""Get dataset item"""
|
115 |
+
image_path = self.image_files[idx]
|
116 |
+
image_name = image_path.stem
|
117 |
+
|
118 |
+
# Load image
|
119 |
+
image = Image.open(image_path).convert('RGB')
|
120 |
+
|
121 |
+
# Load annotations if requested
|
122 |
+
annotations = None
|
123 |
+
if self.load_annotations:
|
124 |
+
anno_path = self.annos_dir / f"{image_name}.json"
|
125 |
+
if anno_path.exists():
|
126 |
+
with open(anno_path, 'r') as f:
|
127 |
+
annotations = json.load(f)
|
128 |
+
|
129 |
+
# Apply transforms
|
130 |
+
if self.transform:
|
131 |
+
image = self.transform(image)
|
132 |
+
|
133 |
+
return {
|
134 |
+
'image': image,
|
135 |
+
'image_path': str(image_path),
|
136 |
+
'image_name': image_name,
|
137 |
+
'annotations': annotations
|
138 |
+
}
|
139 |
+
|
140 |
+
def get_categories_in_image(self, annotations: Dict) -> List[str]:
|
141 |
+
"""Extract categories from annotations"""
|
142 |
+
if not annotations or 'item' not in annotations:
|
143 |
+
return []
|
144 |
+
|
145 |
+
categories = []
|
146 |
+
for item_id, item_data in annotations['item'].items():
|
147 |
+
if 'category_name' in item_data:
|
148 |
+
categories.append(item_data['category_name'])
|
149 |
+
|
150 |
+
return list(set(categories))
|
151 |
+
|
152 |
+
class DeepFashion2Downloader:
|
153 |
+
"""Download and setup DeepFashion2 dataset"""
|
154 |
+
|
155 |
+
def __init__(self, config: DeepFashion2Config):
|
156 |
+
self.config = config
|
157 |
+
self.dataset_root = Path(config.dataset_root)
|
158 |
+
|
159 |
+
def download_dataset(self, force_download: bool = False) -> bool:
|
160 |
+
"""
|
161 |
+
Download DeepFashion2 dataset
|
162 |
+
|
163 |
+
Args:
|
164 |
+
force_download: Force re-download even if dataset exists
|
165 |
+
|
166 |
+
Returns:
|
167 |
+
True if successful, False otherwise
|
168 |
+
"""
|
169 |
+
if self.dataset_root.exists() and not force_download:
|
170 |
+
print(f"Dataset already exists at {self.dataset_root}")
|
171 |
+
return True
|
172 |
+
|
173 |
+
print("DeepFashion2 dataset download requires manual setup.")
|
174 |
+
print("Please follow these steps:")
|
175 |
+
print("1. Visit: https://github.com/switchablenorms/DeepFashion2")
|
176 |
+
print("2. Follow the dataset download instructions")
|
177 |
+
print("3. Extract the dataset to:", self.dataset_root)
|
178 |
+
print("4. Ensure the directory structure is:")
|
179 |
+
print(" deepfashion2/")
|
180 |
+
print(" βββ train/")
|
181 |
+
print(" β βββ image/")
|
182 |
+
print(" β βββ annos/")
|
183 |
+
print(" βββ validation/")
|
184 |
+
print(" β βββ image/")
|
185 |
+
print(" β βββ annos/")
|
186 |
+
print(" βββ test/")
|
187 |
+
print(" βββ image/")
|
188 |
+
print(" βββ annos/")
|
189 |
+
|
190 |
+
return False
|
191 |
+
|
192 |
+
def verify_dataset(self) -> bool:
|
193 |
+
"""Verify dataset structure"""
|
194 |
+
required_dirs = [
|
195 |
+
self.dataset_root / "train" / "image",
|
196 |
+
self.dataset_root / "train" / "annos",
|
197 |
+
self.dataset_root / "validation" / "image",
|
198 |
+
self.dataset_root / "validation" / "annos"
|
199 |
+
]
|
200 |
+
|
201 |
+
for dir_path in required_dirs:
|
202 |
+
if not dir_path.exists():
|
203 |
+
print(f"Missing required directory: {dir_path}")
|
204 |
+
return False
|
205 |
+
|
206 |
+
print("Dataset structure verified successfully")
|
207 |
+
return True
|
208 |
+
|
209 |
+
def create_deepfashion2_transforms(image_size: Tuple[int, int] = (224, 224)) -> transforms.Compose:
|
210 |
+
"""Create standard transforms for DeepFashion2 images"""
|
211 |
+
return transforms.Compose([
|
212 |
+
transforms.Resize(image_size),
|
213 |
+
transforms.ToTensor(),
|
214 |
+
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
|
215 |
+
])
|
216 |
+
|
217 |
+
def create_deepfashion2_dataloader(config: DeepFashion2Config,
|
218 |
+
split: str = 'train',
|
219 |
+
shuffle: bool = True) -> DataLoader:
|
220 |
+
"""Create DataLoader for DeepFashion2 dataset"""
|
221 |
+
transform = create_deepfashion2_transforms(config.image_size)
|
222 |
+
|
223 |
+
dataset = DeepFashion2Dataset(
|
224 |
+
root_dir=config.dataset_root,
|
225 |
+
split=split,
|
226 |
+
transform=transform,
|
227 |
+
load_annotations=True
|
228 |
+
)
|
229 |
+
|
230 |
+
return DataLoader(
|
231 |
+
dataset,
|
232 |
+
batch_size=config.batch_size,
|
233 |
+
shuffle=shuffle,
|
234 |
+
num_workers=config.num_workers,
|
235 |
+
pin_memory=torch.cuda.is_available()
|
236 |
+
)
|
237 |
+
|
238 |
+
def get_deepfashion2_statistics(config: DeepFashion2Config) -> Dict:
|
239 |
+
"""Get statistics about the DeepFashion2 dataset"""
|
240 |
+
stats = {
|
241 |
+
'splits': {},
|
242 |
+
'total_images': 0,
|
243 |
+
'categories': config.categories,
|
244 |
+
'category_counts': {cat: 0 for cat in config.categories}
|
245 |
+
}
|
246 |
+
|
247 |
+
for split in ['train', 'validation', 'test']:
|
248 |
+
try:
|
249 |
+
dataset = DeepFashion2Dataset(
|
250 |
+
root_dir=config.dataset_root,
|
251 |
+
split=split,
|
252 |
+
transform=None,
|
253 |
+
load_annotations=True
|
254 |
+
)
|
255 |
+
|
256 |
+
split_stats = {
|
257 |
+
'num_images': len(dataset),
|
258 |
+
'categories_found': set()
|
259 |
+
}
|
260 |
+
|
261 |
+
# Sample a few images to get category statistics
|
262 |
+
sample_size = min(100, len(dataset))
|
263 |
+
for i in range(0, len(dataset), max(1, len(dataset) // sample_size)):
|
264 |
+
item = dataset[i]
|
265 |
+
if item['annotations']:
|
266 |
+
categories = dataset.get_categories_in_image(item['annotations'])
|
267 |
+
split_stats['categories_found'].update(categories)
|
268 |
+
for cat in categories:
|
269 |
+
if cat in stats['category_counts']:
|
270 |
+
stats['category_counts'][cat] += 1
|
271 |
+
|
272 |
+
split_stats['categories_found'] = list(split_stats['categories_found'])
|
273 |
+
stats['splits'][split] = split_stats
|
274 |
+
stats['total_images'] += split_stats['num_images']
|
275 |
+
|
276 |
+
except Exception as e:
|
277 |
+
print(f"Error processing {split} split: {e}")
|
278 |
+
stats['splits'][split] = {'error': str(e)}
|
279 |
+
|
280 |
+
return stats
|
fast.py
CHANGED
@@ -16,6 +16,9 @@ import torchvision.transforms as v2
|
|
16 |
from huggingface_hub import PyTorchModelHubMixin
|
17 |
import numpy as np
|
18 |
import warnings
|
|
|
|
|
|
|
19 |
|
20 |
# Suppress specific warnings for cleaner output
|
21 |
warnings.filterwarnings("ignore", message=".*use_fast.*")
|
@@ -1586,9 +1589,28 @@ class HuggingFaceFashionAnalyzer:
|
|
1586 |
else:
|
1587 |
return "Offers unique styling opportunities for specific occasions."
|
1588 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1589 |
# Initialize analyzer
|
1590 |
analyzer = HuggingFaceFashionAnalyzer()
|
1591 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1592 |
# Request/Response models
|
1593 |
class AnalysisResponse(BaseModel):
|
1594 |
analysis: str
|
@@ -1617,6 +1639,7 @@ async def root():
|
|
1617 |
<br>
|
1618 |
<button onclick="analyzeImage()" style="padding: 10px 20px; margin: 10px;">Analyze Fashion (Detailed)</button>
|
1619 |
<button onclick="analyzeStructured()" style="padding: 10px 20px; margin: 10px;">Analyze Fashion (Structured)</button>
|
|
|
1620 |
<br>
|
1621 |
<a href="/refined-prompt" target="_blank" style="color: #007bff; text-decoration: none;">View Refined Prompt Format</a>
|
1622 |
</div>
|
@@ -1682,6 +1705,77 @@ async def root():
|
|
1682 |
document.getElementById('analysisText').textContent = 'Error: ' + error.message;
|
1683 |
}
|
1684 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1685 |
</script>
|
1686 |
</body>
|
1687 |
</html>
|
@@ -1800,5 +1894,107 @@ async def health_check():
|
|
1800 |
except Exception as e:
|
1801 |
return {"status": "unhealthy", "error": str(e)}
|
1802 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1803 |
if __name__ == "__main__":
|
1804 |
uvicorn.run(app, host="0.0.0.0", port=7861)
|
|
|
16 |
from huggingface_hub import PyTorchModelHubMixin
|
17 |
import numpy as np
|
18 |
import warnings
|
19 |
+
import os
|
20 |
+
import json
|
21 |
+
from pathlib import Path
|
22 |
|
23 |
# Suppress specific warnings for cleaner output
|
24 |
warnings.filterwarnings("ignore", message=".*use_fast.*")
|
|
|
1589 |
else:
|
1590 |
return "Offers unique styling opportunities for specific occasions."
|
1591 |
|
1592 |
+
# Import DeepFashion2 utilities
|
1593 |
+
try:
|
1594 |
+
from deepfashion2_utils import DeepFashion2Config, get_deepfashion2_statistics
|
1595 |
+
from deepfashion2_evaluation import run_full_evaluation
|
1596 |
+
DEEPFASHION2_AVAILABLE = True
|
1597 |
+
except ImportError as e:
|
1598 |
+
print(f"DeepFashion2 utilities not available: {e}")
|
1599 |
+
DEEPFASHION2_AVAILABLE = False
|
1600 |
+
|
1601 |
# Initialize analyzer
|
1602 |
analyzer = HuggingFaceFashionAnalyzer()
|
1603 |
|
1604 |
+
# Initialize DeepFashion2 configuration if available
|
1605 |
+
deepfashion2_config = None
|
1606 |
+
if DEEPFASHION2_AVAILABLE:
|
1607 |
+
try:
|
1608 |
+
deepfashion2_config = DeepFashion2Config()
|
1609 |
+
print(f"DeepFashion2 integration initialized. Dataset root: {deepfashion2_config.dataset_root}")
|
1610 |
+
except Exception as e:
|
1611 |
+
print(f"Failed to initialize DeepFashion2 config: {e}")
|
1612 |
+
DEEPFASHION2_AVAILABLE = False
|
1613 |
+
|
1614 |
# Request/Response models
|
1615 |
class AnalysisResponse(BaseModel):
|
1616 |
analysis: str
|
|
|
1639 |
<br>
|
1640 |
<button onclick="analyzeImage()" style="padding: 10px 20px; margin: 10px;">Analyze Fashion (Detailed)</button>
|
1641 |
<button onclick="analyzeStructured()" style="padding: 10px 20px; margin: 10px;">Analyze Fashion (Structured)</button>
|
1642 |
+
<button onclick="checkDeepFashion2Status()" style="padding: 10px 20px; margin: 10px; background-color: #6f42c1; color: white;">DeepFashion2 Status</button>
|
1643 |
<br>
|
1644 |
<a href="/refined-prompt" target="_blank" style="color: #007bff; text-decoration: none;">View Refined Prompt Format</a>
|
1645 |
</div>
|
|
|
1705 |
document.getElementById('analysisText').textContent = 'Error: ' + error.message;
|
1706 |
}
|
1707 |
}
|
1708 |
+
|
1709 |
+
async function checkDeepFashion2Status() {
|
1710 |
+
document.getElementById('analysisText').textContent = 'Checking DeepFashion2 status...';
|
1711 |
+
document.getElementById('result').style.display = 'block';
|
1712 |
+
|
1713 |
+
try {
|
1714 |
+
const response = await fetch('/deepfashion2/status');
|
1715 |
+
const result = await response.json();
|
1716 |
+
|
1717 |
+
let statusText = 'DeepFashion2 Integration Status:\\n\\n';
|
1718 |
+
statusText += `Available: ${result.available}\\n`;
|
1719 |
+
|
1720 |
+
if (result.available) {
|
1721 |
+
statusText += `Dataset Exists: ${result.dataset_exists}\\n`;
|
1722 |
+
statusText += `Dataset Root: ${result.dataset_root}\\n`;
|
1723 |
+
statusText += `Categories: ${result.categories.length} categories\\n`;
|
1724 |
+
statusText += `Image Size: ${result.image_size[0]}x${result.image_size[1]}\\n\\n`;
|
1725 |
+
|
1726 |
+
if (!result.dataset_exists) {
|
1727 |
+
statusText += 'Dataset not found. Click "Setup Instructions" for download guide.\\n';
|
1728 |
+
} else {
|
1729 |
+
statusText += 'Dataset ready! You can run evaluations.\\n';
|
1730 |
+
}
|
1731 |
+
} else {
|
1732 |
+
statusText += `Message: ${result.message}\\n`;
|
1733 |
+
}
|
1734 |
+
|
1735 |
+
document.getElementById('analysisText').textContent = statusText;
|
1736 |
+
|
1737 |
+
// Add setup instructions button if needed
|
1738 |
+
if (result.available && !result.dataset_exists) {
|
1739 |
+
const setupBtn = document.createElement('button');
|
1740 |
+
setupBtn.textContent = 'Get Setup Instructions';
|
1741 |
+
setupBtn.onclick = getSetupInstructions;
|
1742 |
+
setupBtn.style.cssText = 'padding: 10px 20px; margin: 10px; background-color: #17a2b8; color: white;';
|
1743 |
+
document.getElementById('result').appendChild(setupBtn);
|
1744 |
+
}
|
1745 |
+
|
1746 |
+
} catch (error) {
|
1747 |
+
document.getElementById('analysisText').textContent = 'Error checking status: ' + error.message;
|
1748 |
+
}
|
1749 |
+
}
|
1750 |
+
|
1751 |
+
async function getSetupInstructions() {
|
1752 |
+
try {
|
1753 |
+
const response = await fetch('/deepfashion2/setup-instructions');
|
1754 |
+
const result = await response.json();
|
1755 |
+
|
1756 |
+
let instructionsText = result.title + '\\n\\n';
|
1757 |
+
|
1758 |
+
result.steps.forEach(step => {
|
1759 |
+
instructionsText += `Step ${step.step}: ${step.description}\\n`;
|
1760 |
+
if (step.url) instructionsText += `URL: ${step.url}\\n`;
|
1761 |
+
if (step.command) instructionsText += `Command: ${step.command}\\n`;
|
1762 |
+
if (step.structure) {
|
1763 |
+
instructionsText += 'Structure:\\n';
|
1764 |
+
step.structure.forEach(line => instructionsText += ` ${line}\\n`);
|
1765 |
+
}
|
1766 |
+
if (step.endpoint) instructionsText += `Endpoint: ${step.endpoint}\\n`;
|
1767 |
+
instructionsText += '\\n';
|
1768 |
+
});
|
1769 |
+
|
1770 |
+
instructionsText += 'Notes:\\n';
|
1771 |
+
result.notes.forEach(note => instructionsText += `β’ ${note}\\n`);
|
1772 |
+
|
1773 |
+
document.getElementById('analysisText').textContent = instructionsText;
|
1774 |
+
|
1775 |
+
} catch (error) {
|
1776 |
+
document.getElementById('analysisText').textContent = 'Error getting instructions: ' + error.message;
|
1777 |
+
}
|
1778 |
+
}
|
1779 |
</script>
|
1780 |
</body>
|
1781 |
</html>
|
|
|
1894 |
except Exception as e:
|
1895 |
return {"status": "unhealthy", "error": str(e)}
|
1896 |
|
1897 |
+
# DeepFashion2 API endpoints
|
1898 |
+
@app.get("/deepfashion2/status")
|
1899 |
+
async def deepfashion2_status():
|
1900 |
+
"""Get DeepFashion2 integration status"""
|
1901 |
+
if not DEEPFASHION2_AVAILABLE:
|
1902 |
+
return {"available": False, "message": "DeepFashion2 utilities not available"}
|
1903 |
+
|
1904 |
+
if not deepfashion2_config:
|
1905 |
+
return {"available": False, "message": "DeepFashion2 configuration not initialized"}
|
1906 |
+
|
1907 |
+
# Check if dataset exists
|
1908 |
+
dataset_path = Path(deepfashion2_config.dataset_root)
|
1909 |
+
dataset_exists = dataset_path.exists()
|
1910 |
+
|
1911 |
+
return {
|
1912 |
+
"available": True,
|
1913 |
+
"dataset_exists": dataset_exists,
|
1914 |
+
"dataset_root": deepfashion2_config.dataset_root,
|
1915 |
+
"categories": deepfashion2_config.categories,
|
1916 |
+
"image_size": deepfashion2_config.image_size
|
1917 |
+
}
|
1918 |
+
|
1919 |
+
@app.get("/deepfashion2/statistics")
|
1920 |
+
async def deepfashion2_statistics():
|
1921 |
+
"""Get DeepFashion2 dataset statistics"""
|
1922 |
+
if not DEEPFASHION2_AVAILABLE or not deepfashion2_config:
|
1923 |
+
raise HTTPException(status_code=503, detail="DeepFashion2 not available")
|
1924 |
+
|
1925 |
+
try:
|
1926 |
+
stats = get_deepfashion2_statistics(deepfashion2_config)
|
1927 |
+
return stats
|
1928 |
+
except Exception as e:
|
1929 |
+
raise HTTPException(status_code=500, detail=f"Error getting statistics: {str(e)}")
|
1930 |
+
|
1931 |
+
@app.post("/deepfashion2/evaluate")
|
1932 |
+
async def deepfashion2_evaluate(max_samples: int = 50):
|
1933 |
+
"""Run evaluation using DeepFashion2 dataset"""
|
1934 |
+
if not DEEPFASHION2_AVAILABLE or not deepfashion2_config:
|
1935 |
+
raise HTTPException(status_code=503, detail="DeepFashion2 not available")
|
1936 |
+
|
1937 |
+
try:
|
1938 |
+
# Run evaluation in background (for demo purposes, limit samples)
|
1939 |
+
report_path = run_full_evaluation(analyzer, deepfashion2_config, max_samples=max_samples)
|
1940 |
+
|
1941 |
+
return {
|
1942 |
+
"status": "completed",
|
1943 |
+
"report_path": report_path,
|
1944 |
+
"max_samples": max_samples,
|
1945 |
+
"message": f"Evaluation completed with {max_samples} samples"
|
1946 |
+
}
|
1947 |
+
except Exception as e:
|
1948 |
+
raise HTTPException(status_code=500, detail=f"Evaluation failed: {str(e)}")
|
1949 |
+
|
1950 |
+
@app.get("/deepfashion2/setup-instructions")
|
1951 |
+
async def deepfashion2_setup_instructions():
|
1952 |
+
"""Get setup instructions for DeepFashion2 dataset"""
|
1953 |
+
return {
|
1954 |
+
"title": "DeepFashion2 Dataset Setup Instructions",
|
1955 |
+
"steps": [
|
1956 |
+
{
|
1957 |
+
"step": 1,
|
1958 |
+
"description": "Visit the official DeepFashion2 repository",
|
1959 |
+
"url": "https://github.com/switchablenorms/DeepFashion2"
|
1960 |
+
},
|
1961 |
+
{
|
1962 |
+
"step": 2,
|
1963 |
+
"description": "Follow the dataset download instructions in the repository"
|
1964 |
+
},
|
1965 |
+
{
|
1966 |
+
"step": 3,
|
1967 |
+
"description": "Create the dataset directory",
|
1968 |
+
"command": f"mkdir -p {deepfashion2_config.dataset_root if deepfashion2_config else './data/deepfashion2'}"
|
1969 |
+
},
|
1970 |
+
{
|
1971 |
+
"step": 4,
|
1972 |
+
"description": "Extract the dataset with the following structure:",
|
1973 |
+
"structure": [
|
1974 |
+
"deepfashion2/",
|
1975 |
+
"βββ train/",
|
1976 |
+
"β βββ image/",
|
1977 |
+
"β βββ annos/",
|
1978 |
+
"βββ validation/",
|
1979 |
+
"β βββ image/",
|
1980 |
+
"β βββ annos/",
|
1981 |
+
"βββ test/",
|
1982 |
+
" βββ image/",
|
1983 |
+
" βββ annos/"
|
1984 |
+
]
|
1985 |
+
},
|
1986 |
+
{
|
1987 |
+
"step": 5,
|
1988 |
+
"description": "Verify the setup by checking the status endpoint",
|
1989 |
+
"endpoint": "/deepfashion2/status"
|
1990 |
+
}
|
1991 |
+
],
|
1992 |
+
"notes": [
|
1993 |
+
"The DeepFashion2 dataset is large (~15GB) and requires registration",
|
1994 |
+
"Make sure you have sufficient disk space",
|
1995 |
+
"The dataset contains 491K images across 13 clothing categories"
|
1996 |
+
]
|
1997 |
+
}
|
1998 |
+
|
1999 |
if __name__ == "__main__":
|
2000 |
uvicorn.run(app, host="0.0.0.0", port=7861)
|
requirements.txt
CHANGED
@@ -54,3 +54,6 @@ triton==3.3.1
|
|
54 |
typing_extensions==4.14.0
|
55 |
urllib3==2.5.0
|
56 |
uvicorn==0.24.0
|
|
|
|
|
|
|
|
54 |
typing_extensions==4.14.0
|
55 |
urllib3==2.5.0
|
56 |
uvicorn==0.24.0
|
57 |
+
scikit-learn==1.5.2
|
58 |
+
matplotlib==3.9.3
|
59 |
+
seaborn==0.13.2
|