TDN-M commited on
Commit
25dd38f
·
verified ·
1 Parent(s): 480e30f

Tôi muốn có một ứng dụng hoạt động như một chiếc gương thử đồ tại các shop thời trang, ứng dụng này được tích hợp flux kontext để tạo ra hình ảnh realtime của người dùng và các loại sản phẩm họ chọn. Ứng dụng cần được tối ưu hiển thị cho màn hình dọc. Phần gương soi chiếm trọn màn hình. Tổng thể UI tinh tế, UX hoàn hảo - Initial Deployment

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +556 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Fitting Mirror
3
- emoji: 🏆
4
- colorFrom: indigo
5
- colorTo: gray
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: fitting-mirror
3
+ emoji: 🐳
4
+ colorFrom: green
5
+ colorTo: yellow
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,556 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Virtual Dressing Mirror</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@latest"></script>
9
+ <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/body-pix@latest"></script>
10
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
11
+ <style>
12
+ .mirror-container {
13
+ position: relative;
14
+ width: 100%;
15
+ height: 100vh;
16
+ overflow: hidden;
17
+ background-color: #000;
18
+ }
19
+
20
+ #mirror-video {
21
+ width: 100%;
22
+ height: 100%;
23
+ object-fit: cover;
24
+ transform: scaleX(-1);
25
+ }
26
+
27
+ #mirror-canvas {
28
+ position: absolute;
29
+ top: 0;
30
+ left: 0;
31
+ width: 100%;
32
+ height: 100%;
33
+ z-index: 10;
34
+ }
35
+
36
+ .product-overlay {
37
+ position: absolute;
38
+ top: 0;
39
+ left: 0;
40
+ width: 100%;
41
+ height: 100%;
42
+ z-index: 20;
43
+ pointer-events: none;
44
+ }
45
+
46
+ .product-item {
47
+ position: absolute;
48
+ transition: all 0.3s ease;
49
+ }
50
+
51
+ .controls {
52
+ position: absolute;
53
+ bottom: 20px;
54
+ left: 0;
55
+ width: 100%;
56
+ z-index: 30;
57
+ display: flex;
58
+ justify-content: center;
59
+ gap: 15px;
60
+ }
61
+
62
+ .control-btn {
63
+ width: 60px;
64
+ height: 60px;
65
+ border-radius: 50%;
66
+ background: rgba(255, 255, 255, 0.2);
67
+ backdrop-filter: blur(10px);
68
+ display: flex;
69
+ align-items: center;
70
+ justify-content: center;
71
+ color: white;
72
+ font-size: 24px;
73
+ cursor: pointer;
74
+ transition: all 0.2s ease;
75
+ border: 2px solid rgba(255, 255, 255, 0.3);
76
+ }
77
+
78
+ .control-btn:hover {
79
+ background: rgba(255, 255, 255, 0.3);
80
+ transform: scale(1.1);
81
+ }
82
+
83
+ .product-selector {
84
+ position: absolute;
85
+ bottom: 100px;
86
+ left: 0;
87
+ width: 100%;
88
+ height: 120px;
89
+ z-index: 30;
90
+ display: flex;
91
+ justify-content: center;
92
+ gap: 10px;
93
+ padding: 0 20px;
94
+ overflow-x: auto;
95
+ scrollbar-width: none;
96
+ }
97
+
98
+ .product-selector::-webkit-scrollbar {
99
+ display: none;
100
+ }
101
+
102
+ .product-thumbnail {
103
+ width: 80px;
104
+ height: 80px;
105
+ border-radius: 10px;
106
+ background: rgba(255, 255, 255, 0.1);
107
+ backdrop-filter: blur(5px);
108
+ display: flex;
109
+ align-items: center;
110
+ justify-content: center;
111
+ color: white;
112
+ cursor: pointer;
113
+ transition: all 0.2s ease;
114
+ flex-shrink: 0;
115
+ overflow: hidden;
116
+ border: 2px solid rgba(255, 255, 255, 0.2);
117
+ }
118
+
119
+ .product-thumbnail img {
120
+ width: 100%;
121
+ height: 100%;
122
+ object-fit: cover;
123
+ }
124
+
125
+ .product-thumbnail.active {
126
+ border-color: white;
127
+ transform: scale(1.1);
128
+ }
129
+
130
+ .loading-overlay {
131
+ position: absolute;
132
+ top: 0;
133
+ left: 0;
134
+ width: 100%;
135
+ height: 100%;
136
+ background: rgba(0, 0, 0, 0.8);
137
+ z-index: 100;
138
+ display: flex;
139
+ flex-direction: column;
140
+ align-items: center;
141
+ justify-content: center;
142
+ color: white;
143
+ font-size: 18px;
144
+ }
145
+
146
+ .spinner {
147
+ width: 50px;
148
+ height: 50px;
149
+ border: 5px solid rgba(255, 255, 255, 0.3);
150
+ border-radius: 50%;
151
+ border-top-color: white;
152
+ animation: spin 1s ease-in-out infinite;
153
+ margin-bottom: 20px;
154
+ }
155
+
156
+ @keyframes spin {
157
+ to { transform: rotate(360deg); }
158
+ }
159
+
160
+ .category-selector {
161
+ position: absolute;
162
+ top: 20px;
163
+ left: 0;
164
+ width: 100%;
165
+ z-index: 30;
166
+ display: flex;
167
+ justify-content: center;
168
+ gap: 10px;
169
+ padding: 10px;
170
+ background: rgba(0, 0, 0, 0.5);
171
+ backdrop-filter: blur(5px);
172
+ }
173
+
174
+ .category-btn {
175
+ padding: 8px 15px;
176
+ border-radius: 20px;
177
+ background: rgba(255, 255, 255, 0.1);
178
+ color: white;
179
+ font-size: 14px;
180
+ cursor: pointer;
181
+ transition: all 0.2s ease;
182
+ border: none;
183
+ outline: none;
184
+ }
185
+
186
+ .category-btn.active {
187
+ background: white;
188
+ color: black;
189
+ }
190
+
191
+ .product-info {
192
+ position: absolute;
193
+ top: 20px;
194
+ right: 20px;
195
+ background: rgba(0, 0, 0, 0.7);
196
+ backdrop-filter: blur(5px);
197
+ color: white;
198
+ padding: 15px;
199
+ border-radius: 10px;
200
+ max-width: 200px;
201
+ z-index: 30;
202
+ display: none;
203
+ }
204
+
205
+ .product-info h3 {
206
+ margin: 0 0 5px 0;
207
+ font-size: 16px;
208
+ }
209
+
210
+ .product-info p {
211
+ margin: 0;
212
+ font-size: 14px;
213
+ opacity: 0.8;
214
+ }
215
+
216
+ .product-info .price {
217
+ font-weight: bold;
218
+ margin-top: 10px;
219
+ font-size: 18px;
220
+ }
221
+ </style>
222
+ </head>
223
+ <body class="bg-black text-white">
224
+ <div class="mirror-container">
225
+ <!-- Loading overlay -->
226
+ <div class="loading-overlay">
227
+ <div class="spinner"></div>
228
+ <div>Initializing virtual mirror...</div>
229
+ </div>
230
+
231
+ <!-- Video feed -->
232
+ <video id="mirror-video" autoplay muted></video>
233
+
234
+ <!-- Canvas for segmentation -->
235
+ <canvas id="mirror-canvas"></canvas>
236
+
237
+ <!-- Product overlays -->
238
+ <div class="product-overlay" id="product-overlay"></div>
239
+
240
+ <!-- Category selector -->
241
+ <div class="category-selector">
242
+ <button class="category-btn active" data-category="tops">Tops</button>
243
+ <button class="category-btn" data-category="bottoms">Bottoms</button>
244
+ <button class="category-btn" data-category="dresses">Dresses</button>
245
+ <button class="category-btn" data-category="outerwear">Outerwear</button>
246
+ <button class="category-btn" data-category="accessories">Accessories</button>
247
+ </div>
248
+
249
+ <!-- Product info panel -->
250
+ <div class="product-info" id="product-info">
251
+ <h3 id="product-name">Product Name</h3>
252
+ <p id="product-description">Product description goes here</p>
253
+ <div class="price" id="product-price">$99.99</div>
254
+ </div>
255
+
256
+ <!-- Product selector -->
257
+ <div class="product-selector" id="product-selector">
258
+ <!-- Products will be loaded here -->
259
+ </div>
260
+
261
+ <!-- Controls -->
262
+ <div class="controls">
263
+ <div class="control-btn" id="capture-btn">
264
+ <i class="fas fa-camera"></i>
265
+ </div>
266
+ <div class="control-btn" id="toggle-mirror-btn">
267
+ <i class="fas fa-eye"></i>
268
+ </div>
269
+ <div class="control-btn" id="reset-btn">
270
+ <i class="fas fa-redo"></i>
271
+ </div>
272
+ </div>
273
+ </div>
274
+
275
+ <script>
276
+ // DOM elements
277
+ const video = document.getElementById('mirror-video');
278
+ const canvas = document.getElementById('mirror-canvas');
279
+ const ctx = canvas.getContext('2d');
280
+ const productOverlay = document.getElementById('product-overlay');
281
+ const productSelector = document.getElementById('product-selector');
282
+ const categoryButtons = document.querySelectorAll('.category-btn');
283
+ const captureBtn = document.getElementById('capture-btn');
284
+ const toggleMirrorBtn = document.getElementById('toggle-mirror-btn');
285
+ const resetBtn = document.getElementById('reset-btn');
286
+ const productInfo = document.getElementById('product-info');
287
+ const loadingOverlay = document.querySelector('.loading-overlay');
288
+
289
+ // App state
290
+ let net;
291
+ let currentCategory = 'tops';
292
+ let selectedProduct = null;
293
+ let products = {
294
+ tops: [
295
+ { id: 't1', name: 'Classic White Tee', price: '$29.99', description: '100% cotton crew neck t-shirt', image: 'https://images.unsplash.com/photo-1521572163474-6864f9cf17ab?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=200&q=80', overlay: 'https://i.ibb.co/7QpKsCX/image.png', position: { x: 50, y: 30, width: 40, rotation: 0 } },
296
+ { id: 't2', name: 'Striped Blouse', price: '$49.99', description: 'Silk blend striped blouse', image: 'https://images.unsplash.com/photo-1551232864-3f0890e580d9?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=200&q=80', overlay: 'https://i.ibb.co/7QpKsCX/image.png', position: { x: 50, y: 30, width: 40, rotation: 0 } },
297
+ { id: 't3', name: 'Black Crop Top', price: '$34.99', description: 'Stretchy black crop top', image: 'https://images.unsplash.com/photo-1576566588028-4147f3842f27?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=200&q=80', overlay: 'https://i.ibb.co/7QpKsCX/image.png', position: { x: 50, y: 30, width: 40, rotation: 0 } },
298
+ { id: 't4', name: 'Denim Shirt', price: '$59.99', description: 'Classic denim button-up', image: 'https://images.unsplash.com/photo-1598033129183-c4f50c736f10?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=200&q=80', overlay: 'https://i.ibb.co/7QpKsCX/image.png', position: { x: 50, y: 30, width: 40, rotation: 0 } },
299
+ { id: 't5', name: 'Pink Sweater', price: '$65.99', description: 'Cozy knit sweater', image: 'https://images.unsplash.com/photo-1520367445093-50dc08a59d9d?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=200&q=80', overlay: 'https://i.ibb.co/7QpKsCX/image.png', position: { x: 50, y: 30, width: 40, rotation: 0 } }
300
+ ],
301
+ bottoms: [
302
+ { id: 'b1', name: 'Skinny Jeans', price: '$79.99', description: 'Stretch denim skinny jeans', image: 'https://images.unsplash.com/photo-1541099649105-f69ad21a3246?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=200&q=80', overlay: 'https://i.ibb.co/7QpKsCX/image.png', position: { x: 50, y: 60, width: 40, rotation: 0 } },
303
+ { id: 'b2', name: 'Black Slacks', price: '$89.99', description: 'Tailored work pants', image: 'https://images.unsplash.com/photo-1595950653106-6c9ebd614d3a?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=200&q=80', overlay: 'https://i.ibb.co/7QpKsCX/image.png', position: { x: 50, y: 60, width: 40, rotation: 0 } },
304
+ { id: 'b3', name: 'Pleated Skirt', price: '$59.99', description: 'Mid-length pleated skirt', image: 'https://images.unsplash.com/photo-1551232864-3f0890e580d9?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=200&q=80', overlay: 'https://i.ibb.co/7QpKsCX/image.png', position: { x: 50, y: 60, width: 40, rotation: 0 } },
305
+ { id: 'b4', name: 'Denim Shorts', price: '$49.99', description: 'High-waisted denim shorts', image: 'https://images.unsplash.com/photo-1602810318383-e386cc2a3ccf?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=200&q=80', overlay: 'https://i.ibb.co/7QpKsCX/image.png', position: { x: 50, y: 60, width: 40, rotation: 0 } },
306
+ { id: 'b5', name: 'Yoga Pants', price: '$54.99', description: 'High-performance leggings', image: 'https://images.unsplash.com/photo-1583744949095-cf0f6a4e6c5a?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=200&q=80', overlay: 'https://i.ibb.co/7QpKsCX/image.png', position: { x: 50, y: 60, width: 40, rotation: 0 } }
307
+ ],
308
+ dresses: [
309
+ { id: 'd1', name: 'Little Black Dress', price: '$99.99', description: 'Classic A-line dress', image: 'https://images.unsplash.com/photo-1539533018447-63fcce2678e5?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=200&q=80', overlay: 'https://i.ibb.co/7QpKsCX/image.png', position: { x: 50, y: 40, width: 45, rotation: 0 } },
310
+ { id: 'd2', name: 'Floral Sundress', price: '$79.99', description: 'Lightweight summer dress', image: 'https://images.unsplash.com/photo-1591047139829-d91aecb6caea?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=200&q=80', overlay: 'https://i.ibb.co/7QpKsCX/image.png', position: { x: 50, y: 40, width: 45, rotation: 0 } },
311
+ { id: 'd3', name: 'Wrap Dress', price: '$89.99', description: 'Flattering wrap silhouette', image: 'https://images.unsplash.com/photo-1551232864-3f0890e580d9?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=200&q=80', overlay: 'https://i.ibb.co/7QpKsCX/image.png', position: { x: 50, y: 40, width: 45, rotation: 0 } },
312
+ { id: 'd4', name: 'Maxi Dress', price: '$109.99', description: 'Floor-length bohemian dress', image: 'https://images.unsplash.com/photo-1572804013309-59a88b7e92f1?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=200&q=80', overlay: 'https://i.ibb.co/7QpKsCX/image.png', position: { x: 50, y: 40, width: 45, rotation: 0 } },
313
+ { id: 'd5', name: 'Cocktail Dress', price: '$119.99', description: 'Sequin embellished party dress', image: 'https://images.unsplash.com/photo-1551232864-3f0890e580d9?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=200&q=80', overlay: 'https://i.ibb.co/7QpKsCX/image.png', position: { x: 50, y: 40, width: 45, rotation: 0 } }
314
+ ],
315
+ outerwear: [
316
+ { id: 'o1', name: 'Denim Jacket', price: '$89.99', description: 'Classic blue denim jacket', image: 'https://images.unsplash.com/photo-1591047139829-d91aecb6caea?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=200&q=80', overlay: 'https://i.ibb.co/7QpKsCX/image.png', position: { x: 50, y: 30, width: 45, rotation: 0 } },
317
+ { id: 'o2', name: 'Trench Coat', price: '$149.99', description: 'Water-resistant classic trench', image: 'https://images.unsplash.com/photo-1551232864-3f0890e580d9?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=200&q=80', overlay: 'https://i.ibb.co/7QpKsCX/image.png', position: { x: 50, y: 30, width: 45, rotation: 0 } },
318
+ { id: 'o3', name: 'Leather Jacket', price: '$199.99', description: 'Genuine leather biker jacket', image: 'https://images.unsplash.com/photo-1551232864-3f0890e580d9?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=200&q=80', overlay: 'https://i.ibb.co/7QpKsCX/image.png', position: { x: 50, y: 30, width: 45, rotation: 0 } },
319
+ { id: 'o4', name: 'Puffer Jacket', price: '$129.99', description: 'Warm winter puffer', image: 'https://images.unsplash.com/photo-1551232864-3f0890e580d9?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=200&q=80', overlay: 'https://i.ibb.co/7QpKsCX/image.png', position: { x: 50, y: 30, width: 45, rotation: 0 } },
320
+ { id: 'o5', name: 'Blazer', price: '$119.99', description: 'Tailored work blazer', image: 'https://images.unsplash.com/photo-1551232864-3f0890e580d9?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=200&q=80', overlay: 'https://i.ibb.co/7QpKsCX/image.png', position: { x: 50, y: 30, width: 45, rotation: 0 } }
321
+ ],
322
+ accessories: [
323
+ { id: 'a1', name: 'Silk Scarf', price: '$39.99', description: 'Printed silk scarf', image: 'https://images.unsplash.com/photo-1551232864-3f0890e580d9?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=200&q=80', overlay: 'https://i.ibb.co/7QpKsCX/image.png', position: { x: 50, y: 20, width: 30, rotation: 0 } },
324
+ { id: 'a2', name: 'Statement Necklace', price: '$59.99', description: 'Chunky gold-tone necklace', image: 'https://images.unsplash.com/photo-1551232864-3f0890e580d9?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=200&q=80', overlay: 'https://i.ibb.co/7QpKsCX/image.png', position: { x: 50, y: 20, width: 30, rotation: 0 } },
325
+ { id: 'a3', name: 'Wide Brim Hat', price: '$49.99', description: 'Straw sun hat', image: 'https://images.unsplash.com/photo-1551232864-3f0890e580d9?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=200&q=80', overlay: 'https://i.ibb.co/7QpKsCX/image.png', position: { x: 50, y: 10, width: 40, rotation: 0 } },
326
+ { id: 'a4', name: 'Designer Handbag', price: '$199.99', description: 'Leather crossbody bag', image: 'https://images.unsplash.com/photo-1551232864-3f0890e580d9?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=200&q=80', overlay: 'https://i.ibb.co/7QpKsCX/image.png', position: { x: 20, y: 60, width: 20, rotation: 0 } },
327
+ { id: 'a5', name: 'Oversized Sunglasses', price: '$79.99', description: 'UV-protective lenses', image: 'https://images.unsplash.com/photo-1551232864-3f0890e580d9?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=200&q=80', overlay: 'https://i.ibb.co/7QpKsCX/image.png', position: { x: 50, y: 25, width: 30, rotation: 0 } }
328
+ ]
329
+ };
330
+
331
+ // Initialize the app
332
+ async function init() {
333
+ try {
334
+ // Load body segmentation model
335
+ net = await bodyPix.load({
336
+ architecture: 'MobileNetV1',
337
+ outputStride: 16,
338
+ multiplier: 0.75,
339
+ quantBytes: 2
340
+ });
341
+
342
+ // Start camera
343
+ if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
344
+ const stream = await navigator.mediaDevices.getUserMedia({
345
+ video: {
346
+ facingMode: 'user',
347
+ width: { ideal: 1080 },
348
+ height: { ideal: 1920 }
349
+ },
350
+ audio: false
351
+ });
352
+ video.srcObject = stream;
353
+
354
+ // Wait for video to be ready
355
+ video.onloadedmetadata = () => {
356
+ // Set canvas dimensions to match video
357
+ canvas.width = video.videoWidth;
358
+ canvas.height = video.videoHeight;
359
+
360
+ // Hide loading overlay
361
+ setTimeout(() => {
362
+ loadingOverlay.style.display = 'none';
363
+ }, 1000);
364
+
365
+ // Start segmentation loop
366
+ segmentationLoop();
367
+ };
368
+ } else {
369
+ alert('Camera access is not supported by your browser');
370
+ }
371
+
372
+ // Load initial products
373
+ loadProducts(currentCategory);
374
+
375
+ // Set up event listeners
376
+ setupEventListeners();
377
+ } catch (error) {
378
+ console.error('Error initializing app:', error);
379
+ loadingOverlay.querySelector('div').textContent = 'Error initializing. Please refresh the page.';
380
+ }
381
+ }
382
+
383
+ // Set up event listeners
384
+ function setupEventListeners() {
385
+ // Category buttons
386
+ categoryButtons.forEach(btn => {
387
+ btn.addEventListener('click', () => {
388
+ // Update active button
389
+ categoryButtons.forEach(b => b.classList.remove('active'));
390
+ btn.classList.add('active');
391
+
392
+ // Load new category
393
+ currentCategory = btn.dataset.category;
394
+ loadProducts(currentCategory);
395
+ });
396
+ });
397
+
398
+ // Capture button
399
+ captureBtn.addEventListener('click', captureOutfit);
400
+
401
+ // Toggle mirror button
402
+ toggleMirrorBtn.addEventListener('click', toggleMirrorEffect);
403
+
404
+ // Reset button
405
+ resetBtn.addEventListener('click', resetProducts);
406
+ }
407
+
408
+ // Load products for a category
409
+ function loadProducts(category) {
410
+ productSelector.innerHTML = '';
411
+
412
+ products[category].forEach(product => {
413
+ const productEl = document.createElement('div');
414
+ productEl.className = 'product-thumbnail';
415
+ productEl.dataset.id = product.id;
416
+
417
+ const img = document.createElement('img');
418
+ img.src = product.image;
419
+ img.alt = product.name;
420
+
421
+ productEl.appendChild(img);
422
+
423
+ productEl.addEventListener('click', () => {
424
+ // Highlight selected product
425
+ document.querySelectorAll('.product-thumbnail').forEach(el => {
426
+ el.classList.remove('active');
427
+ });
428
+ productEl.classList.add('active');
429
+
430
+ // Show product on mirror
431
+ showProduct(product);
432
+ });
433
+
434
+ productSelector.appendChild(productEl);
435
+ });
436
+ }
437
+
438
+ // Show product on mirror
439
+ function showProduct(product) {
440
+ selectedProduct = product;
441
+
442
+ // Clear previous products
443
+ productOverlay.innerHTML = '';
444
+
445
+ // Create product element
446
+ const productEl = document.createElement('div');
447
+ productEl.className = 'product-item';
448
+ productEl.dataset.id = product.id;
449
+
450
+ const img = document.createElement('img');
451
+ img.src = product.overlay;
452
+ img.alt = product.name;
453
+ img.style.width = `${product.position.width}%`;
454
+ img.style.transform = `rotate(${product.position.rotation}deg)`;
455
+
456
+ productEl.appendChild(img);
457
+ productEl.style.left = `${product.position.x}%`;
458
+ productEl.style.top = `${product.position.y}%`;
459
+
460
+ productOverlay.appendChild(productEl);
461
+
462
+ // Show product info
463
+ document.getElementById('product-name').textContent = product.name;
464
+ document.getElementById('product-description').textContent = product.description;
465
+ document.getElementById('product-price').textContent = product.price;
466
+ productInfo.style.display = 'block';
467
+ }
468
+
469
+ // Segmentation loop
470
+ async function segmentationLoop() {
471
+ // Perform segmentation
472
+ const segmentation = await net.segmentPerson(video, {
473
+ flipHorizontal: true,
474
+ internalResolution: 'medium',
475
+ segmentationThreshold: 0.7
476
+ });
477
+
478
+ // Draw segmentation mask
479
+ const mask = bodyPix.toMask(segmentation);
480
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
481
+ ctx.putImageData(mask, 0, 0);
482
+
483
+ // Continue loop
484
+ requestAnimationFrame(segmentationLoop);
485
+ }
486
+
487
+ // Capture outfit
488
+ function captureOutfit() {
489
+ // Create temporary canvas
490
+ const tempCanvas = document.createElement('canvas');
491
+ tempCanvas.width = canvas.width;
492
+ tempCanvas.height = canvas.height;
493
+ const tempCtx = tempCanvas.getContext('2d');
494
+
495
+ // Draw video frame
496
+ tempCtx.save();
497
+ tempCtx.scale(-1, 1);
498
+ tempCtx.drawImage(video, -canvas.width, 0, canvas.width, canvas.height);
499
+ tempCtx.restore();
500
+
501
+ // Draw products
502
+ const productElements = document.querySelectorAll('.product-item');
503
+ productElements.forEach(el => {
504
+ const img = el.querySelector('img');
505
+ const rect = el.getBoundingClientRect();
506
+
507
+ tempCtx.save();
508
+ tempCtx.translate(rect.left + rect.width/2, rect.top + rect.height/2);
509
+ tempCtx.rotate((img.style.transform.match(/rotate\((\d+)deg\)/) || [0,0])[1] * Math.PI / 180);
510
+ tempCtx.drawImage(img, -rect.width/2, -rect.height/2, rect.width, rect.height);
511
+ tempCtx.restore();
512
+ });
513
+
514
+ // Create download link
515
+ const link = document.createElement('a');
516
+ link.download = 'virtual-mirror-outfit.png';
517
+ link.href = tempCanvas.toDataURL('image/png');
518
+ link.click();
519
+
520
+ // Show confirmation
521
+ const confirmation = document.createElement('div');
522
+ confirmation.className = 'fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-black bg-opacity-70 text-white px-6 py-3 rounded-lg z-50';
523
+ confirmation.textContent = 'Outfit captured!';
524
+ document.body.appendChild(confirmation);
525
+
526
+ setTimeout(() => {
527
+ document.body.removeChild(confirmation);
528
+ }, 2000);
529
+ }
530
+
531
+ // Toggle mirror effect
532
+ function toggleMirrorEffect() {
533
+ if (canvas.style.opacity === '0') {
534
+ canvas.style.opacity = '1';
535
+ toggleMirrorBtn.innerHTML = '<i class="fas fa-eye"></i>';
536
+ } else {
537
+ canvas.style.opacity = '0';
538
+ toggleMirrorBtn.innerHTML = '<i class="fas fa-eye-slash"></i>';
539
+ }
540
+ }
541
+
542
+ // Reset all products
543
+ function resetProducts() {
544
+ productOverlay.innerHTML = '';
545
+ productInfo.style.display = 'none';
546
+ document.querySelectorAll('.product-thumbnail').forEach(el => {
547
+ el.classList.remove('active');
548
+ });
549
+ selectedProduct = null;
550
+ }
551
+
552
+ // Initialize when DOM is loaded
553
+ document.addEventListener('DOMContentLoaded', init);
554
+ </script>
555
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=TDN-M/fitting-mirror" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
556
+ </html>