ritz26 commited on
Commit
dc9bbcb
·
1 Parent(s): 7ec1256

Uploaded a virtual try on

Browse files
Files changed (3) hide show
  1. Dockerfile +6 -8
  2. app.py +94 -36
  3. templates/index.html +7 -1
Dockerfile CHANGED
@@ -3,8 +3,8 @@ FROM python:3.10.12-slim
3
  WORKDIR /app
4
 
5
  # Set cache directories and performance optimizations
6
- ENV HF_HOME=/app/.cache
7
- ENV MPLCONFIGDIR=/app/.cache
8
  ENV OMP_NUM_THREADS=4
9
  ENV TOKENIZERS_PARALLELISM=false
10
 
@@ -16,17 +16,15 @@ RUN apt-get update && apt-get install -y \
16
  && apt-get clean \
17
  && rm -rf /var/lib/apt/lists/*
18
 
19
- # Create cache directory
20
- RUN mkdir -p /app/.cache && chmod -R 777 /app/.cache
21
 
22
  # Install Python dependencies
23
  COPY requirements.txt .
24
  RUN pip install --no-cache-dir -r requirements.txt
25
 
26
- # Pre-download model
27
- RUN python -c "from transformers import SamModel, SamProcessor; \
28
- SamModel.from_pretrained('Zigeng/SlimSAM-uniform-50', cache_dir='/app/.cache'); \
29
- SamProcessor.from_pretrained('Zigeng/SlimSAM-uniform-50', cache_dir='/app/.cache')"
30
 
31
  # Copy app code
32
  COPY . .
 
3
  WORKDIR /app
4
 
5
  # Set cache directories and performance optimizations
6
+ ENV HF_HOME=/tmp/.cache
7
+ ENV MPLCONFIGDIR=/tmp/.cache
8
  ENV OMP_NUM_THREADS=4
9
  ENV TOKENIZERS_PARALLELISM=false
10
 
 
16
  && apt-get clean \
17
  && rm -rf /var/lib/apt/lists/*
18
 
19
+ # Create upload and output directories
20
+ RUN mkdir -p /tmp/uploads /tmp/outputs && chmod -R 777 /tmp/uploads /tmp/outputs
21
 
22
  # Install Python dependencies
23
  COPY requirements.txt .
24
  RUN pip install --no-cache-dir -r requirements.txt
25
 
26
+ # Create temporary cache directory (will be created at runtime)
27
+ RUN mkdir -p /tmp/.cache && chmod -R 777 /tmp/.cache
 
 
28
 
29
  # Copy app code
30
  COPY . .
app.py CHANGED
@@ -32,8 +32,8 @@ if not os.path.exists(OUTPUT_FOLDER):
32
  model, processor = None, None
33
  device = None
34
 
35
- def initialize_model():
36
- """Initialize model once at startup"""
37
  global model, processor, device
38
 
39
  # Determine device
@@ -41,39 +41,36 @@ def initialize_model():
41
  print(f"[INFO] Using device: {device}")
42
 
43
  print("[INFO] Loading SAM model and processor...")
44
- model = SamModel.from_pretrained("Zigeng/SlimSAM-uniform-50", cache_dir="/app/.cache")
45
- processor = SamProcessor.from_pretrained("Zigeng/SlimSAM-uniform-50", cache_dir="/app/.cache")
46
 
47
  # Move model to device
48
  model = model.to(device)
49
  print(f"[INFO] Model and processor loaded successfully on {device}!")
50
 
51
- def load_model():
52
- """Ensure model is loaded (should already be loaded at startup)"""
53
- global model, processor
54
- if model is None or processor is None:
55
- print("[WARNING] Model not loaded, initializing now...")
56
- initialize_model()
57
-
58
- def warmup_model():
59
- """Warm up the model with a dummy inference"""
60
- global model, processor, device
61
- if model is None or processor is None:
62
- return
63
-
64
- print("[INFO] Warming up model...")
65
  try:
66
- # Create a dummy image and points for warmup
67
- dummy_img = Image.new('RGB', (512, 512), color='white')
68
- dummy_points = [[[256, 256], [300, 300]]]
69
- inputs = processor(dummy_img, input_points=dummy_points, return_tensors="pt")
70
- inputs = {k: v.to(device) for k, v in inputs.items()}
71
-
72
- with torch.no_grad():
73
- _ = model(**inputs)
74
- print("[INFO] Model warmup completed!")
75
  except Exception as e:
76
- print(f"[WARNING] Model warmup failed: {e}")
77
 
78
  @app.before_request
79
  def log_request_info():
@@ -86,7 +83,28 @@ def health():
86
  # Route to serve outputs dynamically
87
  @app.route('/outputs/<filename>')
88
  def serve_output(filename):
89
- return send_from_directory(OUTPUT_FOLDER, filename)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
 
91
  # Route to serve cached person images
92
  @app.route('/uploads/<filename>')
@@ -186,16 +204,40 @@ def index():
186
  new_tshirt = new_tshirt.resize(img.size, Image.LANCZOS)
187
  img_with_new_tshirt = Image.composite(new_tshirt, img, mask)
188
  result_path = os.path.join(OUTPUT_FOLDER, 'result.jpg')
 
 
 
 
 
189
  img_with_new_tshirt.save(result_path)
190
  print(f"[INFO] Result saved to {result_path}")
 
 
 
 
 
 
 
191
 
192
  # Calculate processing time
193
  processing_time = time.time() - start_time
194
  print(f"[PERF] Total processing time: {processing_time:.2f}s")
195
 
 
 
 
 
 
 
 
 
 
 
 
 
196
  # Serve via dynamic route with cached person info
197
  return render_template('index.html',
198
- result_img='/outputs/result.jpg',
199
  cached_person=use_cached_person,
200
  person_image_path=person_path,
201
  processing_time=f"{processing_time:.2f}s")
@@ -214,13 +256,29 @@ def change_person():
214
  print("[INFO] Cleared cached person data")
215
  return render_template('index.html')
216
 
217
- if __name__ == '__main__':
218
- # Initialize model at startup
219
- print("[INFO] Initializing model...")
220
- initialize_model()
 
 
 
 
 
 
 
 
 
 
 
221
 
222
- # Warm up the model
223
- warmup_model()
 
224
 
 
 
 
225
  print("[INFO] Starting Flask server...")
 
226
  app.run(debug=True, host='0.0.0.0')
 
32
  model, processor = None, None
33
  device = None
34
 
35
+ def load_model():
36
+ """Load model on demand (no caching to save storage)"""
37
  global model, processor, device
38
 
39
  # Determine device
 
41
  print(f"[INFO] Using device: {device}")
42
 
43
  print("[INFO] Loading SAM model and processor...")
44
+ model = SamModel.from_pretrained("Zigeng/SlimSAM-uniform-50", cache_dir="/tmp/.cache")
45
+ processor = SamProcessor.from_pretrained("Zigeng/SlimSAM-uniform-50", cache_dir="/tmp/.cache")
46
 
47
  # Move model to device
48
  model = model.to(device)
49
  print(f"[INFO] Model and processor loaded successfully on {device}!")
50
 
51
+ def cleanup_temp_files():
52
+ """Clean up temporary files to save storage"""
53
+ try:
54
+ import shutil
55
+ if os.path.exists("/tmp/.cache"):
56
+ shutil.rmtree("/tmp/.cache")
57
+ print("[INFO] Cleaned up temporary cache files")
58
+ except Exception as e:
59
+ print(f"[WARNING] Could not clean up temp files: {e}")
60
+
61
+ def cleanup_old_outputs():
62
+ """Clean up old output files to save storage"""
 
 
63
  try:
64
+ if os.path.exists(OUTPUT_FOLDER):
65
+ for file in os.listdir(OUTPUT_FOLDER):
66
+ file_path = os.path.join(OUTPUT_FOLDER, file)
67
+ if os.path.isfile(file_path):
68
+ # Remove files older than 1 hour
69
+ if time.time() - os.path.getctime(file_path) > 3600:
70
+ os.remove(file_path)
71
+ print(f"[INFO] Removed old output file: {file}")
 
72
  except Exception as e:
73
+ print(f"[WARNING] Could not clean up old outputs: {e}")
74
 
75
  @app.before_request
76
  def log_request_info():
 
83
  # Route to serve outputs dynamically
84
  @app.route('/outputs/<filename>')
85
  def serve_output(filename):
86
+ print(f"[DEBUG] Serving file: {filename} from {OUTPUT_FOLDER}")
87
+ if not os.path.exists(OUTPUT_FOLDER):
88
+ print(f"[ERROR] Output folder does not exist: {OUTPUT_FOLDER}")
89
+ return "Output folder not found", 404
90
+
91
+ file_path = os.path.join(OUTPUT_FOLDER, filename)
92
+ if not os.path.exists(file_path):
93
+ print(f"[ERROR] File does not exist: {file_path}")
94
+ return "File not found", 404
95
+
96
+ print(f"[DEBUG] File exists, serving: {file_path}")
97
+
98
+ # Set proper MIME type for images
99
+ from flask import Response
100
+ if filename.lower().endswith(('.jpg', '.jpeg')):
101
+ mimetype = 'image/jpeg'
102
+ elif filename.lower().endswith('.png'):
103
+ mimetype = 'image/png'
104
+ else:
105
+ mimetype = 'application/octet-stream'
106
+
107
+ return send_from_directory(OUTPUT_FOLDER, filename, mimetype=mimetype)
108
 
109
  # Route to serve cached person images
110
  @app.route('/uploads/<filename>')
 
204
  new_tshirt = new_tshirt.resize(img.size, Image.LANCZOS)
205
  img_with_new_tshirt = Image.composite(new_tshirt, img, mask)
206
  result_path = os.path.join(OUTPUT_FOLDER, 'result.jpg')
207
+
208
+ # Ensure output directory exists
209
+ os.makedirs(OUTPUT_FOLDER, exist_ok=True)
210
+
211
+ # Save the result image
212
  img_with_new_tshirt.save(result_path)
213
  print(f"[INFO] Result saved to {result_path}")
214
+
215
+ # Verify file was saved
216
+ if os.path.exists(result_path):
217
+ file_size = os.path.getsize(result_path)
218
+ print(f"[DEBUG] File saved successfully, size: {file_size} bytes")
219
+ else:
220
+ print(f"[ERROR] File was not saved to {result_path}")
221
 
222
  # Calculate processing time
223
  processing_time = time.time() - start_time
224
  print(f"[PERF] Total processing time: {processing_time:.2f}s")
225
 
226
+ # Clean up old files to save storage
227
+ cleanup_old_outputs()
228
+
229
+ # Generate a unique filename to avoid caching issues
230
+ import uuid
231
+ unique_filename = f"result_{uuid.uuid4().hex[:8]}.jpg"
232
+ unique_result_path = os.path.join(OUTPUT_FOLDER, unique_filename)
233
+
234
+ # Copy the result to a unique filename
235
+ import shutil
236
+ shutil.copy2(result_path, unique_result_path)
237
+
238
  # Serve via dynamic route with cached person info
239
  return render_template('index.html',
240
+ result_img=f'/outputs/{unique_filename}',
241
  cached_person=use_cached_person,
242
  person_image_path=person_path,
243
  processing_time=f"{processing_time:.2f}s")
 
256
  print("[INFO] Cleared cached person data")
257
  return render_template('index.html')
258
 
259
+ @app.route('/cleanup', methods=['POST'])
260
+ def cleanup():
261
+ """Manual cleanup of temporary files"""
262
+ cleanup_temp_files()
263
+ cleanup_old_outputs()
264
+ return "Cleanup completed", 200
265
+
266
+ @app.route('/test-image')
267
+ def test_image():
268
+ """Test route to check if image serving works"""
269
+ # Create a simple test image
270
+ from PIL import Image, ImageDraw
271
+ img = Image.new('RGB', (200, 200), color='red')
272
+ draw = ImageDraw.Draw(img)
273
+ draw.text((50, 100), "TEST IMAGE", fill='white')
274
 
275
+ test_path = os.path.join(OUTPUT_FOLDER, 'test.jpg')
276
+ os.makedirs(OUTPUT_FOLDER, exist_ok=True)
277
+ img.save(test_path)
278
 
279
+ return f'<img src="/outputs/test.jpg" alt="Test Image">'
280
+
281
+ if __name__ == '__main__':
282
  print("[INFO] Starting Flask server...")
283
+ print("[INFO] Model will be loaded on first request to save memory...")
284
  app.run(debug=True, host='0.0.0.0')
templates/index.html CHANGED
@@ -116,7 +116,13 @@
116
  </div>
117
  {% endif %}
118
  <div class="flex justify-center mb-6">
119
- <img id="result-image" src="{{ result_img }}" alt="Result Image" class="rounded-xl">
 
 
 
 
 
 
120
  </div>
121
  <div class="flex justify-center">
122
  <button onclick="downloadImage()" class="bg-green-500 hover:bg-green-600 text-white font-semibold py-3 px-6 rounded-xl shadow-md transition flex items-center gap-2">
 
116
  </div>
117
  {% endif %}
118
  <div class="flex justify-center mb-6">
119
+ <img id="result-image" src="{{ result_img }}" alt="Result Image" class="rounded-xl"
120
+ onerror="this.style.display='none'; this.nextElementSibling.style.display='block';">
121
+ <div style="display:none;" class="text-red-500 p-4 border border-red-500 rounded-xl">
122
+ <p>❌ Image failed to load</p>
123
+ <p class="text-sm">Please try again or check the server logs</p>
124
+ <p class="text-xs">Image URL: {{ result_img }}</p>
125
+ </div>
126
  </div>
127
  <div class="flex justify-center">
128
  <button onclick="downloadImage()" class="bg-green-500 hover:bg-green-600 text-white font-semibold py-3 px-6 rounded-xl shadow-md transition flex items-center gap-2">