David Ko commited on
Commit
805a9b6
·
1 Parent(s): e89ea76

Update frontend build with fix for VectorDBActions.js (data.objects to data.detections)

Browse files
frontend/build/asset-manifest.json CHANGED
@@ -1,8 +1,8 @@
1
  {
2
  "files": {
3
  "main.css": "/static/css/main.59c2a54e.chunk.css",
4
- "main.js": "/static/js/main.06c7c845.chunk.js",
5
- "main.js.map": "/static/js/main.06c7c845.chunk.js.map",
6
  "runtime-main.js": "/static/js/runtime-main.25710301.js",
7
  "runtime-main.js.map": "/static/js/runtime-main.25710301.js.map",
8
  "static/js/2.252de3c4.chunk.js": "/static/js/2.252de3c4.chunk.js",
@@ -10,7 +10,7 @@
10
  "static/js/3.9013e23f.chunk.js": "/static/js/3.9013e23f.chunk.js",
11
  "static/js/3.9013e23f.chunk.js.map": "/static/js/3.9013e23f.chunk.js.map",
12
  "index.html": "/index.html",
13
- "precache-manifest.dcc3c32413232f3947b07b1749e190fa.js": "/precache-manifest.dcc3c32413232f3947b07b1749e190fa.js",
14
  "service-worker.js": "/service-worker.js",
15
  "static/css/main.59c2a54e.chunk.css.map": "/static/css/main.59c2a54e.chunk.css.map",
16
  "static/js/2.252de3c4.chunk.js.LICENSE.txt": "/static/js/2.252de3c4.chunk.js.LICENSE.txt"
@@ -19,6 +19,6 @@
19
  "static/js/runtime-main.25710301.js",
20
  "static/js/2.252de3c4.chunk.js",
21
  "static/css/main.59c2a54e.chunk.css",
22
- "static/js/main.06c7c845.chunk.js"
23
  ]
24
  }
 
1
  {
2
  "files": {
3
  "main.css": "/static/css/main.59c2a54e.chunk.css",
4
+ "main.js": "/static/js/main.1756d180.chunk.js",
5
+ "main.js.map": "/static/js/main.1756d180.chunk.js.map",
6
  "runtime-main.js": "/static/js/runtime-main.25710301.js",
7
  "runtime-main.js.map": "/static/js/runtime-main.25710301.js.map",
8
  "static/js/2.252de3c4.chunk.js": "/static/js/2.252de3c4.chunk.js",
 
10
  "static/js/3.9013e23f.chunk.js": "/static/js/3.9013e23f.chunk.js",
11
  "static/js/3.9013e23f.chunk.js.map": "/static/js/3.9013e23f.chunk.js.map",
12
  "index.html": "/index.html",
13
+ "precache-manifest.8a1e450c8ad569d0a69dec24accbc833.js": "/precache-manifest.8a1e450c8ad569d0a69dec24accbc833.js",
14
  "service-worker.js": "/service-worker.js",
15
  "static/css/main.59c2a54e.chunk.css.map": "/static/css/main.59c2a54e.chunk.css.map",
16
  "static/js/2.252de3c4.chunk.js.LICENSE.txt": "/static/js/2.252de3c4.chunk.js.LICENSE.txt"
 
19
  "static/js/runtime-main.25710301.js",
20
  "static/js/2.252de3c4.chunk.js",
21
  "static/css/main.59c2a54e.chunk.css",
22
+ "static/js/main.1756d180.chunk.js"
23
  ]
24
  }
frontend/build/index.html CHANGED
@@ -1 +1 @@
1
- <!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Multi-Model Object Detection Demo"/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>Vision Web App</title><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"/><link href="/static/css/main.59c2a54e.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,i,a=r[0],c=r[1],l=r[2],p=0,s=[];p<a.length;p++)i=a[p],Object.prototype.hasOwnProperty.call(o,i)&&o[i]&&s.push(o[i][0]),o[i]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(f&&f(r);s.length;)s.shift()();return u.push.apply(u,l||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,a=1;a<t.length;a++){var c=t[a];0!==o[c]&&(n=!1)}n&&(u.splice(r--,1),e=i(i.s=t[0]))}return e}var n={},o={1:0},u=[];function i(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,i),t.l=!0,t.exports}i.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var u,a=document.createElement("script");a.charset="utf-8",a.timeout=120,i.nc&&a.setAttribute("nonce",i.nc),a.src=function(e){return i.p+"static/js/"+({}[e]||e)+"."+{3:"9013e23f"}[e]+".chunk.js"}(e);var c=new Error;u=function(r){a.onerror=a.onload=null,clearTimeout(l);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),u=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,t[1](c)}o[e]=void 0}};var l=setTimeout((function(){u({type:"timeout",target:a})}),12e4);a.onerror=a.onload=u,document.head.appendChild(a)}return Promise.all(r)},i.m=e,i.c=n,i.d=function(e,r,t){i.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,r){if(1&r&&(e=i(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(i.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)i.d(t,n,function(r){return e[r]}.bind(null,n));return t},i.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(r,"a",r),r},i.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},i.p="/",i.oe=function(e){throw console.error(e),e};var a=this["webpackJsonpvision-web-app"]=this["webpackJsonpvision-web-app"]||[],c=a.push.bind(a);a.push=r,a=a.slice();for(var l=0;l<a.length;l++)r(a[l]);var f=c;t()}([])</script><script src="/static/js/2.252de3c4.chunk.js"></script><script src="/static/js/main.06c7c845.chunk.js"></script></body></html>
 
1
+ <!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Multi-Model Object Detection Demo"/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>Vision Web App</title><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"/><link href="/static/css/main.59c2a54e.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,i,a=r[0],c=r[1],l=r[2],p=0,s=[];p<a.length;p++)i=a[p],Object.prototype.hasOwnProperty.call(o,i)&&o[i]&&s.push(o[i][0]),o[i]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(f&&f(r);s.length;)s.shift()();return u.push.apply(u,l||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,a=1;a<t.length;a++){var c=t[a];0!==o[c]&&(n=!1)}n&&(u.splice(r--,1),e=i(i.s=t[0]))}return e}var n={},o={1:0},u=[];function i(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,i),t.l=!0,t.exports}i.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var u,a=document.createElement("script");a.charset="utf-8",a.timeout=120,i.nc&&a.setAttribute("nonce",i.nc),a.src=function(e){return i.p+"static/js/"+({}[e]||e)+"."+{3:"9013e23f"}[e]+".chunk.js"}(e);var c=new Error;u=function(r){a.onerror=a.onload=null,clearTimeout(l);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),u=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,t[1](c)}o[e]=void 0}};var l=setTimeout((function(){u({type:"timeout",target:a})}),12e4);a.onerror=a.onload=u,document.head.appendChild(a)}return Promise.all(r)},i.m=e,i.c=n,i.d=function(e,r,t){i.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,r){if(1&r&&(e=i(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(i.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)i.d(t,n,function(r){return e[r]}.bind(null,n));return t},i.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(r,"a",r),r},i.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},i.p="/",i.oe=function(e){throw console.error(e),e};var a=this["webpackJsonpvision-web-app"]=this["webpackJsonpvision-web-app"]||[],c=a.push.bind(a);a.push=r,a=a.slice();for(var l=0;l<a.length;l++)r(a[l]);var f=c;t()}([])</script><script src="/static/js/2.252de3c4.chunk.js"></script><script src="/static/js/main.1756d180.chunk.js"></script></body></html>
frontend/build/model-vector-db.html DELETED
@@ -1,580 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="ko">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>모델 결과 벡터 DB 저장 및 검색</title>
7
- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
8
- <style>
9
- body {
10
- padding: 20px;
11
- background-color: #f8f9fa;
12
- }
13
- .card {
14
- margin-bottom: 20px;
15
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
16
- }
17
- .result-image {
18
- max-width: 100%;
19
- height: auto;
20
- border-radius: 4px;
21
- }
22
- .detection-box {
23
- position: absolute;
24
- border: 2px solid;
25
- pointer-events: none;
26
- box-sizing: border-box;
27
- }
28
- .image-container {
29
- position: relative;
30
- margin-bottom: 15px;
31
- }
32
- .model-btn {
33
- margin-right: 10px;
34
- margin-bottom: 10px;
35
- }
36
- .result-card {
37
- transition: all 0.3s;
38
- }
39
- .result-card:hover {
40
- transform: translateY(-5px);
41
- }
42
- .nav-tabs .nav-link.active {
43
- font-weight: bold;
44
- border-bottom: 3px solid #0d6efd;
45
- }
46
- .loading {
47
- display: none;
48
- text-align: center;
49
- padding: 20px;
50
- }
51
- .search-result-item {
52
- border-bottom: 1px solid #dee2e6;
53
- padding-bottom: 15px;
54
- margin-bottom: 15px;
55
- }
56
- .search-result-item:last-child {
57
- border-bottom: none;
58
- }
59
- .similarity-badge {
60
- font-size: 0.9rem;
61
- }
62
- </style>
63
- </head>
64
- <body>
65
- <div class="container">
66
- <h1 class="my-4 text-center">모델 결과 벡터 DB 저장 및 검색</h1>
67
-
68
- <ul class="nav nav-tabs mb-4" id="myTab" role="tablist">
69
- <li class="nav-item" role="presentation">
70
- <button class="nav-link active" id="detect-tab" data-bs-toggle="tab" data-bs-target="#detect" type="button" role="tab" aria-controls="detect" aria-selected="true">객체 인식 및 저장</button>
71
- </li>
72
- <li class="nav-item" role="presentation">
73
- <button class="nav-link" id="search-tab" data-bs-toggle="tab" data-bs-target="#search" type="button" role="tab" aria-controls="search" aria-selected="false">벡터 DB 검색</button>
74
- </li>
75
- </ul>
76
-
77
- <div class="tab-content" id="myTabContent">
78
- <!-- 객체 인식 및 저장 탭 -->
79
- <div class="tab-pane fade show active" id="detect" role="tabpanel" aria-labelledby="detect-tab">
80
- <div class="row">
81
- <div class="col-md-6">
82
- <div class="card">
83
- <div class="card-header">
84
- <h5 class="card-title mb-0">이미지 업로드</h5>
85
- </div>
86
- <div class="card-body">
87
- <div class="mb-3">
88
- <label for="imageUpload" class="form-label">이미지 선택</label>
89
- <input class="form-control" type="file" id="imageUpload" accept="image/*">
90
- </div>
91
- <div class="image-container">
92
- <img id="uploadedImage" class="img-fluid result-image" style="display: none;">
93
- </div>
94
- <div class="d-flex flex-wrap">
95
- <button id="detectYolo" class="btn btn-primary model-btn">YOLOv8로 인식</button>
96
- <button id="detectDetr" class="btn btn-success model-btn">DETR로 인식</button>
97
- <button id="detectVit" class="btn btn-info model-btn">ViT로 분류</button>
98
- </div>
99
- </div>
100
- </div>
101
- </div>
102
-
103
- <div class="col-md-6">
104
- <div class="card">
105
- <div class="card-header">
106
- <h5 class="card-title mb-0">인식 결과</h5>
107
- </div>
108
- <div class="card-body">
109
- <div class="image-container">
110
- <img id="resultImage" class="img-fluid result-image" style="display: none;">
111
- <div id="detectionBoxes"></div>
112
- </div>
113
- <div class="mb-3">
114
- <label for="detectionResults" class="form-label">인식된 객체</label>
115
- <textarea class="form-control" id="detectionResults" rows="5" readonly></textarea>
116
- </div>
117
- <button id="saveToVectorDb" class="btn btn-warning" disabled>벡터 DB에 저장</button>
118
- <div class="mt-3" id="saveResult"></div>
119
- </div>
120
- </div>
121
- </div>
122
- </div>
123
-
124
- <div id="detectLoading" class="loading">
125
- <div class="spinner-border text-primary" role="status">
126
- <span class="visually-hidden">Loading...</span>
127
- </div>
128
- <p class="mt-2">처리 중...</p>
129
- </div>
130
- </div>
131
-
132
- <!-- 벡터 DB 검색 탭 -->
133
- <div class="tab-pane fade" id="search" role="tabpanel" aria-labelledby="search-tab">
134
- <div class="row">
135
- <div class="col-md-5">
136
- <div class="card">
137
- <div class="card-header">
138
- <h5 class="card-title mb-0">검색 옵션</h5>
139
- </div>
140
- <div class="card-body">
141
- <div class="mb-3">
142
- <label class="form-label">검색 방법</label>
143
- <div class="form-check">
144
- <input class="form-check-input" type="radio" name="searchType" id="searchByImage" value="image" checked>
145
- <label class="form-check-label" for="searchByImage">
146
- 이미지로 검색
147
- </label>
148
- </div>
149
- <div class="form-check">
150
- <input class="form-check-input" type="radio" name="searchType" id="searchByClass" value="class">
151
- <label class="form-check-label" for="searchByClass">
152
- 클래스로 검색
153
- </label>
154
- </div>
155
- </div>
156
-
157
- <div id="imageSearchOptions">
158
- <div class="mb-3">
159
- <label for="searchImageUpload" class="form-label">이미지 선택</label>
160
- <input class="form-control" type="file" id="searchImageUpload" accept="image/*">
161
- </div>
162
- <div class="image-container">
163
- <img id="searchImage" class="img-fluid result-image" style="display: none;">
164
- </div>
165
- </div>
166
-
167
- <div id="classSearchOptions" style="display: none;">
168
- <div class="mb-3">
169
- <label for="classInput" class="form-label">클래스 이름</label>
170
- <input type="text" class="form-control" id="classInput" placeholder="예: person, car, dog...">
171
- </div>
172
- </div>
173
-
174
- <div class="mb-3">
175
- <label for="resultCount" class="form-label">결과 개수</label>
176
- <select class="form-select" id="resultCount">
177
- <option value="5">5개</option>
178
- <option value="10">10개</option>
179
- <option value="20">20개</option>
180
- </select>
181
- </div>
182
-
183
- <button id="searchButton" class="btn btn-primary">검색하기</button>
184
- </div>
185
- </div>
186
- </div>
187
-
188
- <div class="col-md-7">
189
- <div class="card">
190
- <div class="card-header">
191
- <h5 class="card-title mb-0">검색 결과</h5>
192
- </div>
193
- <div class="card-body">
194
- <div id="searchResults"></div>
195
- </div>
196
- </div>
197
- </div>
198
- </div>
199
-
200
- <div id="searchLoading" class="loading">
201
- <div class="spinner-border text-primary" role="status">
202
- <span class="visually-hidden">Loading...</span>
203
- </div>
204
- <p class="mt-2">검색 중...</p>
205
- </div>
206
- </div>
207
- </div>
208
- </div>
209
-
210
- <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>
211
- <script>
212
- // 전역 변수
213
- let currentModel = null;
214
- let currentDetectionResults = null;
215
- let currentImageBase64 = null;
216
-
217
- // DOM 요소
218
- const imageUpload = document.getElementById('imageUpload');
219
- const uploadedImage = document.getElementById('uploadedImage');
220
- const resultImage = document.getElementById('resultImage');
221
- const detectionResults = document.getElementById('detectionResults');
222
- const detectionBoxes = document.getElementById('detectionBoxes');
223
- const saveToVectorDb = document.getElementById('saveToVectorDb');
224
- const saveResult = document.getElementById('saveResult');
225
- const detectLoading = document.getElementById('detectLoading');
226
-
227
- const searchImageUpload = document.getElementById('searchImageUpload');
228
- const searchImage = document.getElementById('searchImage');
229
- const searchByImage = document.getElementById('searchByImage');
230
- const searchByClass = document.getElementById('searchByClass');
231
- const imageSearchOptions = document.getElementById('imageSearchOptions');
232
- const classSearchOptions = document.getElementById('classSearchOptions');
233
- const classInput = document.getElementById('classInput');
234
- const resultCount = document.getElementById('resultCount');
235
- const searchButton = document.getElementById('searchButton');
236
- const searchResults = document.getElementById('searchResults');
237
- const searchLoading = document.getElementById('searchLoading');
238
-
239
- // 이벤트 리스너
240
- document.getElementById('detectYolo').addEventListener('click', () => detectObjects('yolo'));
241
- document.getElementById('detectDetr').addEventListener('click', () => detectObjects('detr'));
242
- document.getElementById('detectVit').addEventListener('click', () => detectObjects('vit'));
243
- saveToVectorDb.addEventListener('click', saveToVectorDatabase);
244
- searchButton.addEventListener('click', searchVectorDatabase);
245
-
246
- // 이미지 업로드 처리
247
- imageUpload.addEventListener('change', function(e) {
248
- if (e.target.files && e.target.files[0]) {
249
- const reader = new FileReader();
250
-
251
- reader.onload = function(e) {
252
- uploadedImage.src = e.target.result;
253
- uploadedImage.style.display = 'block';
254
- currentImageBase64 = e.target.result.split(',')[1];
255
-
256
- // 결과 초기화
257
- resultImage.style.display = 'none';
258
- detectionResults.value = '';
259
- detectionBoxes.innerHTML = '';
260
- saveToVectorDb.disabled = true;
261
- saveResult.innerHTML = '';
262
- };
263
-
264
- reader.readAsDataURL(e.target.files[0]);
265
- }
266
- });
267
-
268
- searchImageUpload.addEventListener('change', function(e) {
269
- if (e.target.files && e.target.files[0]) {
270
- const reader = new FileReader();
271
-
272
- reader.onload = function(e) {
273
- searchImage.src = e.target.result;
274
- searchImage.style.display = 'block';
275
- };
276
-
277
- reader.readAsDataURL(e.target.files[0]);
278
- }
279
- });
280
-
281
- // 검색 방법 변경 처리
282
- searchByImage.addEventListener('change', function() {
283
- if (this.checked) {
284
- imageSearchOptions.style.display = 'block';
285
- classSearchOptions.style.display = 'none';
286
- }
287
- });
288
-
289
- searchByClass.addEventListener('change', function() {
290
- if (this.checked) {
291
- imageSearchOptions.style.display = 'none';
292
- classSearchOptions.style.display = 'block';
293
- }
294
- });
295
-
296
- // 객체 인식 함수
297
- async function detectObjects(model) {
298
- if (!currentImageBase64) {
299
- alert('먼저 이미지를 업로드해주세요.');
300
- return;
301
- }
302
-
303
- currentModel = model;
304
- detectLoading.style.display = 'block';
305
-
306
- try {
307
- const response = await fetch(`/api/detect?model=${model}`, {
308
- method: 'POST',
309
- headers: {
310
- 'Content-Type': 'application/json',
311
- },
312
- body: JSON.stringify({
313
- image: currentImageBase64
314
- })
315
- });
316
-
317
- const result = await response.json();
318
-
319
- if (result.error) {
320
- throw new Error(result.error);
321
- }
322
-
323
- // 결과 표시
324
- if (model === 'vit') {
325
- // ViT는 분류만 제공
326
- resultImage.style.display = 'none';
327
- detectionBoxes.innerHTML = '';
328
- detectionResults.value = result.classifications.map(c => `${c.label}: ${c.score.toFixed(2)}`).join('\n');
329
-
330
- currentDetectionResults = {
331
- model: 'vit',
332
- classifications: result.classifications
333
- };
334
- } else {
335
- // YOLO, DETR은 객체 인식 결과 제공
336
- resultImage.src = `data:image/jpeg;base64,${result.image}`;
337
- resultImage.style.display = 'block';
338
-
339
- detectionResults.value = result.objects.map(obj =>
340
- `${obj.class}: ${obj.confidence.toFixed(2)} at [${obj.bbox.join(', ')}]`
341
- ).join('\n');
342
-
343
- // 바운딩 박스 표시
344
- displayBoundingBoxes(result.objects);
345
-
346
- currentDetectionResults = {
347
- model: model,
348
- objects: result.objects
349
- };
350
- }
351
-
352
- saveToVectorDb.disabled = false;
353
-
354
- } catch (error) {
355
- console.error('Error:', error);
356
- detectionResults.value = `오류 발생: ${error.message}`;
357
- } finally {
358
- detectLoading.style.display = 'none';
359
- }
360
- }
361
-
362
- // 바운딩 박스 표시 함수
363
- function displayBoundingBoxes(objects) {
364
- detectionBoxes.innerHTML = '';
365
-
366
- const imgWidth = resultImage.clientWidth;
367
- const imgHeight = resultImage.clientHeight;
368
-
369
- objects.forEach(obj => {
370
- const [x1, y1, x2, y2] = obj.bbox;
371
-
372
- const box = document.createElement('div');
373
- box.className = 'detection-box';
374
- box.style.left = `${x1 / resultImage.naturalWidth * 100}%`;
375
- box.style.top = `${y1 / resultImage.naturalHeight * 100}%`;
376
- box.style.width = `${(x2 - x1) / resultImage.naturalWidth * 100}%`;
377
- box.style.height = `${(y2 - y1) / resultImage.naturalHeight * 100}%`;
378
-
379
- // 클래스에 따라 색상 지정
380
- const colors = {
381
- 'person': 'red',
382
- 'car': 'blue',
383
- 'dog': 'green',
384
- 'cat': 'purple'
385
- };
386
-
387
- box.style.borderColor = colors[obj.class] || 'yellow';
388
-
389
- // 라벨 추가
390
- const label = document.createElement('div');
391
- label.style.position = 'absolute';
392
- label.style.top = '-20px';
393
- label.style.left = '0';
394
- label.style.backgroundColor = box.style.borderColor;
395
- label.style.color = 'white';
396
- label.style.padding = '2px 5px';
397
- label.style.borderRadius = '3px';
398
- label.style.fontSize = '12px';
399
- label.textContent = `${obj.class} ${obj.confidence.toFixed(2)}`;
400
-
401
- box.appendChild(label);
402
- detectionBoxes.appendChild(box);
403
- });
404
- }
405
-
406
- // 벡터 DB에 저장 함수
407
- async function saveToVectorDatabase() {
408
- if (!currentDetectionResults) {
409
- alert('먼저 객체를 인식해주세요.');
410
- return;
411
- }
412
-
413
- saveToVectorDb.disabled = true;
414
- detectLoading.style.display = 'block';
415
- saveResult.innerHTML = '';
416
-
417
- try {
418
- let response;
419
-
420
- if (currentDetectionResults.model === 'vit') {
421
- // ViT 분류 결과 저장 (이미지 전체 저장)
422
- response = await fetch('/api/add-image', {
423
- method: 'POST',
424
- headers: {
425
- 'Content-Type': 'application/json',
426
- },
427
- body: JSON.stringify({
428
- image: currentImageBase64,
429
- metadata: {
430
- model: 'vit',
431
- classifications: currentDetectionResults.classifications
432
- }
433
- })
434
- });
435
- } else {
436
- // YOLO, DETR 객체 인식 결과 저장
437
- response = await fetch('/api/add-detected-objects', {
438
- method: 'POST',
439
- headers: {
440
- 'Content-Type': 'application/json',
441
- },
442
- body: JSON.stringify({
443
- image: currentImageBase64,
444
- objects: currentDetectionResults.objects,
445
- image_id: generateUUID()
446
- })
447
- });
448
- }
449
-
450
- const result = await response.json();
451
-
452
- if (result.error) {
453
- throw new Error(result.error);
454
- }
455
-
456
- // 성공 메시지 표시
457
- if (currentDetectionResults.model === 'vit') {
458
- saveResult.innerHTML = `<div class="alert alert-success">이미지와 분류 결과가 벡터 DB에 성공적으로 저장되었습니다.</div>`;
459
- } else {
460
- saveResult.innerHTML = `<div class="alert alert-success">${result.object_ids.length}개의 객체가 벡터 DB에 성공적으로 저장되었습니다.</div>`;
461
- }
462
-
463
- } catch (error) {
464
- console.error('Error:', error);
465
- saveResult.innerHTML = `<div class="alert alert-danger">오류 발생: ${error.message}</div>`;
466
- } finally {
467
- detectLoading.style.display = 'none';
468
- saveToVectorDb.disabled = false;
469
- }
470
- }
471
-
472
- // 벡터 DB 검색 함수
473
- async function searchVectorDatabase() {
474
- searchLoading.style.display = 'block';
475
- searchResults.innerHTML = '';
476
-
477
- try {
478
- let response;
479
- const limit = parseInt(resultCount.value);
480
-
481
- if (searchByImage.checked) {
482
- // 이미지로 검색
483
- if (!searchImage.src || searchImage.src === '') {
484
- throw new Error('검색할 이미지를 업로드해주세요.');
485
- }
486
-
487
- const imageBase64 = searchImage.src.split(',')[1];
488
-
489
- response = await fetch('/api/search-similar-objects', {
490
- method: 'POST',
491
- headers: {
492
- 'Content-Type': 'application/json',
493
- },
494
- body: JSON.stringify({
495
- image: imageBase64,
496
- n_results: limit
497
- })
498
- });
499
- } else {
500
- // 클래스로 검색
501
- if (!classInput.value.trim()) {
502
- throw new Error('검색할 클래스 이름을 입력해주세요.');
503
- }
504
-
505
- response = await fetch('/api/search-similar-objects', {
506
- method: 'POST',
507
- headers: {
508
- 'Content-Type': 'application/json',
509
- },
510
- body: JSON.stringify({
511
- class_name: classInput.value.trim(),
512
- n_results: limit
513
- })
514
- });
515
- }
516
-
517
- const result = await response.json();
518
-
519
- if (result.error) {
520
- throw new Error(result.error);
521
- }
522
-
523
- // 검색 결과 표시
524
- displaySearchResults(result);
525
-
526
- } catch (error) {
527
- console.error('Error:', error);
528
- searchResults.innerHTML = `<div class="alert alert-danger">오류 발생: ${error.message}</div>`;
529
- } finally {
530
- searchLoading.style.display = 'none';
531
- }
532
- }
533
-
534
- // 검색 결과 표시 함수
535
- function displaySearchResults(results) {
536
- if (!results || results.length === 0) {
537
- searchResults.innerHTML = '<div class="alert alert-info">검색 결과가 없습니다.</div>';
538
- return;
539
- }
540
-
541
- let html = '';
542
-
543
- results.forEach((item, index) => {
544
- const similarity = (1 - item.distance) * 100;
545
-
546
- html += `
547
- <div class="search-result-item">
548
- <div class="d-flex align-items-center mb-2">
549
- <h5 class="mb-0 me-2">결과 #${index + 1}</h5>
550
- <span class="badge bg-primary similarity-badge">유사도: ${similarity.toFixed(2)}%</span>
551
- </div>
552
- <div class="row">
553
- <div class="col-md-6">
554
- <img src="data:image/jpeg;base64,${item.image}" class="img-fluid mb-2" alt="Result Image">
555
- </div>
556
- <div class="col-md-6">
557
- <p><strong>클래스:</strong> ${item.metadata.class || '정보 없음'}</p>
558
- <p><strong>신뢰도:</strong> ${item.metadata.confidence ? (item.metadata.confidence * 100).toFixed(2) + '%' : '정보 없음'}</p>
559
- <p><strong>원본 이미지 ID:</strong> ${item.metadata.image_id || '정보 없음'}</p>
560
- <p><strong>객체 ID:</strong> ${item.id}</p>
561
- </div>
562
- </div>
563
- </div>
564
- `;
565
- });
566
-
567
- searchResults.innerHTML = html;
568
- }
569
-
570
- // UUID 생성 함수
571
- function generateUUID() {
572
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
573
- const r = Math.random() * 16 | 0;
574
- const v = c === 'x' ? r : (r & 0x3 | 0x8);
575
- return v.toString(16);
576
- });
577
- }
578
- </script>
579
- </body>
580
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/build/object-detection-search.html DELETED
@@ -1,581 +0,0 @@
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>객체 인식 결과 벡터 검색</title>
7
- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
8
- <style>
9
- body {
10
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
11
- padding: 20px;
12
- background-color: #f8f9fa;
13
- }
14
- .container {
15
- max-width: 1200px;
16
- margin: 0 auto;
17
- background-color: white;
18
- padding: 20px;
19
- border-radius: 10px;
20
- box-shadow: 0 0 10px rgba(0,0,0,0.1);
21
- }
22
- .header {
23
- margin-bottom: 30px;
24
- text-align: center;
25
- }
26
- .upload-section, .detection-section, .search-section, .results-section {
27
- margin-bottom: 30px;
28
- padding: 20px;
29
- border: 1px solid #dee2e6;
30
- border-radius: 5px;
31
- }
32
- .canvas-container {
33
- position: relative;
34
- margin: 20px 0;
35
- }
36
- #imageCanvas {
37
- border: 1px solid #ddd;
38
- max-width: 100%;
39
- }
40
- .object-item {
41
- margin-bottom: 10px;
42
- padding: 10px;
43
- border: 1px solid #e9ecef;
44
- border-radius: 5px;
45
- background-color: #f8f9fa;
46
- }
47
- .result-item {
48
- display: flex;
49
- margin-bottom: 20px;
50
- padding: 15px;
51
- border: 1px solid #e9ecef;
52
- border-radius: 5px;
53
- background-color: #f8f9fa;
54
- }
55
- .result-image {
56
- width: 150px;
57
- height: 150px;
58
- object-fit: cover;
59
- margin-right: 15px;
60
- border: 1px solid #ddd;
61
- }
62
- .result-details {
63
- flex-grow: 1;
64
- }
65
- .badge {
66
- margin-right: 5px;
67
- }
68
- .nav-tabs {
69
- margin-bottom: 20px;
70
- }
71
- .tab-content {
72
- padding: 20px;
73
- border: 1px solid #dee2e6;
74
- border-top: none;
75
- border-radius: 0 0 5px 5px;
76
- }
77
- .bbox-overlay {
78
- position: absolute;
79
- border: 2px solid;
80
- background-color: rgba(255, 255, 255, 0.2);
81
- pointer-events: none;
82
- }
83
- .bbox-label {
84
- position: absolute;
85
- background-color: rgba(0, 0, 0, 0.7);
86
- color: white;
87
- padding: 2px 6px;
88
- border-radius: 3px;
89
- font-size: 12px;
90
- pointer-events: none;
91
- }
92
- .spinner-border {
93
- width: 1rem;
94
- height: 1rem;
95
- margin-right: 0.5rem;
96
- }
97
- .loading {
98
- display: none;
99
- align-items: center;
100
- margin-top: 10px;
101
- }
102
- </style>
103
- </head>
104
- <body>
105
- <div class="container">
106
- <div class="header">
107
- <h1>객체 인식 결과 벡터 검색</h1>
108
- <p class="lead">이미지에서 인식된 객체를 벡터 DB에 저장하고 유사한 객체를 검색합니다.</p>
109
- </div>
110
-
111
- <ul class="nav nav-tabs" id="myTab" role="tablist">
112
- <li class="nav-item" role="presentation">
113
- <button class="nav-link active" id="detect-tab" data-bs-toggle="tab" data-bs-target="#detect" type="button" role="tab" aria-controls="detect" aria-selected="true">객체 인식 및 저장</button>
114
- </li>
115
- <li class="nav-item" role="presentation">
116
- <button class="nav-link" id="search-tab" data-bs-toggle="tab" data-bs-target="#search" type="button" role="tab" aria-controls="search" aria-selected="false">객체 검색</button>
117
- </li>
118
- </ul>
119
-
120
- <div class="tab-content" id="myTabContent">
121
- <!-- 객체 인식 및 저장 탭 -->
122
- <div class="tab-pane fade show active" id="detect" role="tabpanel" aria-labelledby="detect-tab">
123
- <div class="upload-section">
124
- <h3>이미지 업로드</h3>
125
- <div class="mb-3">
126
- <input class="form-control" type="file" id="imageUpload" accept="image/*">
127
- </div>
128
- <button id="detectObjectsBtn" class="btn btn-primary" disabled>객체 인식하기</button>
129
- <div class="loading" id="detectLoading">
130
- <div class="spinner-border text-primary" role="status"></div>
131
- <span>객체 인식 중...</span>
132
- </div>
133
- </div>
134
-
135
- <div class="detection-section">
136
- <h3>인식 결과</h3>
137
- <div class="canvas-container">
138
- <canvas id="imageCanvas"></canvas>
139
- <!-- 바운딩 박스와 라벨은 자바스크립트로 추가됩니다 -->
140
- </div>
141
- <div id="detectedObjects" class="mt-3">
142
- <p>인식된 객체가 여기에 표시됩니다.</p>
143
- </div>
144
- <button id="saveToVectorDBBtn" class="btn btn-success mt-3" disabled>벡터 DB에 저장하기</button>
145
- <div class="loading" id="saveLoading">
146
- <div class="spinner-border text-success" role="status"></div>
147
- <span>벡터 DB에 저장 중...</span>
148
- </div>
149
- </div>
150
- </div>
151
-
152
- <!-- 객체 검색 탭 -->
153
- <div class="tab-pane fade" id="search" role="tabpanel" aria-labelledby="search-tab">
154
- <div class="search-section">
155
- <h3>검색 방법</h3>
156
- <div class="mb-3">
157
- <select class="form-select" id="searchType">
158
- <option value="image">이미지로 검색</option>
159
- <option value="class">클래스로 검색</option>
160
- </select>
161
- </div>
162
-
163
- <div id="imageSearchSection">
164
- <div class="mb-3">
165
- <label for="searchImageUpload" class="form-label">검색할 이미지 업로드</label>
166
- <input class="form-control" type="file" id="searchImageUpload" accept="image/*">
167
- </div>
168
- </div>
169
-
170
- <div id="classSearchSection" style="display: none;">
171
- <div class="mb-3">
172
- <label for="classNameInput" class="form-label">클래스 이름</label>
173
- <input type="text" class="form-control" id="classNameInput" placeholder="예: person, car, dog 등">
174
- </div>
175
- </div>
176
-
177
- <div class="mb-3">
178
- <label for="resultCount" class="form-label">검색 결과 수</label>
179
- <input type="number" class="form-control" id="resultCount" min="1" max="20" value="5">
180
- </div>
181
-
182
- <button id="searchBtn" class="btn btn-primary">검색하기</button>
183
- <div class="loading" id="searchLoading">
184
- <div class="spinner-border text-primary" role="status"></div>
185
- <span>검색 중...</span>
186
- </div>
187
- </div>
188
-
189
- <div class="results-section">
190
- <h3>검색 결과</h3>
191
- <div id="searchResults">
192
- <p>검색 결과가 여기에 표시됩니다.</p>
193
- </div>
194
- </div>
195
- </div>
196
- </div>
197
- </div>
198
-
199
- <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>
200
- <script>
201
- // 전역 변수
202
- let currentImage = null;
203
- let detectedObjects = [];
204
- let imageWidth = 0;
205
- let imageHeight = 0;
206
- const colors = ['#FF5733', '#33FF57', '#3357FF', '#F033FF', '#FF3333', '#33FFFF', '#FFFF33'];
207
-
208
- // DOM 요소
209
- const imageUpload = document.getElementById('imageUpload');
210
- const detectObjectsBtn = document.getElementById('detectObjectsBtn');
211
- const imageCanvas = document.getElementById('imageCanvas');
212
- const ctx = imageCanvas.getContext('2d');
213
- const detectedObjectsDiv = document.getElementById('detectedObjects');
214
- const saveToVectorDBBtn = document.getElementById('saveToVectorDBBtn');
215
- const searchType = document.getElementById('searchType');
216
- const imageSearchSection = document.getElementById('imageSearchSection');
217
- const classSearchSection = document.getElementById('classSearchSection');
218
- const searchImageUpload = document.getElementById('searchImageUpload');
219
- const classNameInput = document.getElementById('classNameInput');
220
- const resultCount = document.getElementById('resultCount');
221
- const searchBtn = document.getElementById('searchBtn');
222
- const searchResults = document.getElementById('searchResults');
223
-
224
- // 로딩 표시
225
- const detectLoading = document.getElementById('detectLoading');
226
- const saveLoading = document.getElementById('saveLoading');
227
- const searchLoading = document.getElementById('searchLoading');
228
-
229
- // 이미지 업로드 처리
230
- imageUpload.addEventListener('change', function(e) {
231
- const file = e.target.files[0];
232
- if (!file) return;
233
-
234
- const reader = new FileReader();
235
- reader.onload = function(event) {
236
- const img = new Image();
237
- img.onload = function() {
238
- // 캔버스 크기 설정
239
- imageWidth = img.width;
240
- imageHeight = img.height;
241
-
242
- // 캔버스 크기를 이미지에 맞게 조정하되, 최대 너비 제한
243
- const maxWidth = 800;
244
- let displayWidth = img.width;
245
- let displayHeight = img.height;
246
-
247
- if (displayWidth > maxWidth) {
248
- const ratio = maxWidth / displayWidth;
249
- displayWidth = maxWidth;
250
- displayHeight = displayHeight * ratio;
251
- }
252
-
253
- imageCanvas.width = displayWidth;
254
- imageCanvas.height = displayHeight;
255
-
256
- // 이미지 그리기
257
- ctx.drawImage(img, 0, 0, displayWidth, displayHeight);
258
-
259
- // 현재 이미지 저장
260
- currentImage = event.target.result;
261
-
262
- // 객체 인식 버튼 활성화
263
- detectObjectsBtn.disabled = false;
264
-
265
- // 이전 객체 인식 결과 초기화
266
- detectedObjects = [];
267
- detectedObjectsDiv.innerHTML = '<p>인식된 객체가 여기에 표시됩니다.</p>';
268
- saveToVectorDBBtn.disabled = true;
269
-
270
- // 바운딩 박스 제거
271
- clearBoundingBoxes();
272
- };
273
- img.src = event.target.result;
274
- };
275
- reader.readAsDataURL(file);
276
- });
277
-
278
- // 객체 인식 처리
279
- detectObjectsBtn.addEventListener('click', function() {
280
- if (!currentImage) return;
281
-
282
- // 로딩 표시
283
- detectLoading.style.display = 'flex';
284
- detectObjectsBtn.disabled = true;
285
-
286
- // 객체 인식 API 호출
287
- fetch('/api/detect', {
288
- method: 'POST',
289
- headers: {
290
- 'Content-Type': 'application/json'
291
- },
292
- body: JSON.stringify({
293
- image: currentImage,
294
- model: 'yolo' // 기본 모델로 YOLO 사용
295
- })
296
- })
297
- .then(response => response.json())
298
- .then(data => {
299
- // 로딩 숨기기
300
- detectLoading.style.display = 'none';
301
- detectObjectsBtn.disabled = false;
302
-
303
- if (data.error) {
304
- alert('객체 인식 중 오류가 발생했습니다: ' + data.error);
305
- return;
306
- }
307
-
308
- // 인식된 객체 저장
309
- detectedObjects = data.objects || [];
310
-
311
- // 결과가 없는 경우
312
- if (detectedObjects.length === 0) {
313
- detectedObjectsDiv.innerHTML = '<p>인식된 객체가 없습니다.</p>';
314
- saveToVectorDBBtn.disabled = true;
315
- return;
316
- }
317
-
318
- // 바운딩 박스 그리기
319
- drawBoundingBoxes();
320
-
321
- // 인식된 객체 목록 표시
322
- displayDetectedObjects();
323
-
324
- // 벡터 DB 저장 버튼 활성화
325
- saveToVectorDBBtn.disabled = false;
326
- })
327
- .catch(error => {
328
- detectLoading.style.display = 'none';
329
- detectObjectsBtn.disabled = false;
330
- console.error('Error:', error);
331
- alert('객체 인식 요청 중 오류가 발생했습니다.');
332
- });
333
- });
334
-
335
- // 바운딩 박스 그리기 함수
336
- function drawBoundingBoxes() {
337
- // 캔버스 크기 가져오기
338
- const canvasWidth = imageCanvas.width;
339
- const canvasHeight = imageCanvas.height;
340
-
341
- // 기존 바운딩 박스 제거
342
- clearBoundingBoxes();
343
-
344
- // 캔버스 컨테이너 가져오기
345
- const canvasContainer = document.querySelector('.canvas-container');
346
-
347
- // 각 객체에 대해 바운딩 박스 그리기
348
- detectedObjects.forEach((obj, index) => {
349
- const bbox = obj.bbox;
350
- const colorIndex = index % colors.length;
351
-
352
- // 상대 좌표를 캔버스 좌표로 변환
353
- const x = bbox.x * canvasWidth;
354
- const y = bbox.y * canvasHeight;
355
- const width = bbox.width * canvasWidth;
356
- const height = bbox.height * canvasHeight;
357
-
358
- // 바운딩 박스 요소 생성
359
- const boxElement = document.createElement('div');
360
- boxElement.className = 'bbox-overlay';
361
- boxElement.style.left = `${x}px`;
362
- boxElement.style.top = `${y}px`;
363
- boxElement.style.width = `${width}px`;
364
- boxElement.style.height = `${height}px`;
365
- boxElement.style.borderColor = colors[colorIndex];
366
-
367
- // 라벨 요소 생성
368
- const labelElement = document.createElement('div');
369
- labelElement.className = 'bbox-label';
370
- labelElement.style.left = `${x}px`;
371
- labelElement.style.top = `${y - 20}px`;
372
- labelElement.style.backgroundColor = colors[colorIndex];
373
- labelElement.textContent = `${obj.class} ${Math.round(obj.confidence * 100)}%`;
374
-
375
- // 요소를 캔버스 컨테이너에 추가
376
- canvasContainer.appendChild(boxElement);
377
- canvasContainer.appendChild(labelElement);
378
- });
379
- }
380
-
381
- // 바운딩 박스 제거 함수
382
- function clearBoundingBoxes() {
383
- const overlays = document.querySelectorAll('.bbox-overlay, .bbox-label');
384
- overlays.forEach(overlay => overlay.remove());
385
- }
386
-
387
- // 인식된 객체 목록 표시 함수
388
- function displayDetectedObjects() {
389
- let html = '<h4>인식된 객체 목록:</h4><ul class="list-group">';
390
-
391
- detectedObjects.forEach((obj, index) => {
392
- const colorIndex = index % colors.length;
393
- html += `
394
- <li class="list-group-item object-item">
395
- <div class="d-flex justify-content-between align-items-center">
396
- <div>
397
- <span class="badge bg-primary">${index + 1}</span>
398
- <span class="badge" style="background-color: ${colors[colorIndex]}">${obj.class}</span>
399
- <span>신뢰도: ${Math.round(obj.confidence * 100)}%</span>
400
- </div>
401
- </div>
402
- </li>
403
- `;
404
- });
405
-
406
- html += '</ul>';
407
- detectedObjectsDiv.innerHTML = html;
408
- }
409
-
410
- // 벡터 DB에 저장 처리
411
- saveToVectorDBBtn.addEventListener('click', function() {
412
- if (!currentImage || detectedObjects.length === 0) return;
413
-
414
- // 로딩 표시
415
- saveLoading.style.display = 'flex';
416
- saveToVectorDBBtn.disabled = true;
417
-
418
- // 벡터 DB 저장 API 호출
419
- fetch('/api/add-detected-objects', {
420
- method: 'POST',
421
- headers: {
422
- 'Content-Type': 'application/json'
423
- },
424
- body: JSON.stringify({
425
- image: currentImage,
426
- objects: detectedObjects
427
- })
428
- })
429
- .then(response => response.json())
430
- .then(data => {
431
- // 로딩 숨기기
432
- saveLoading.style.display = 'none';
433
- saveToVectorDBBtn.disabled = false;
434
-
435
- if (data.error) {
436
- alert('벡터 DB 저장 중 오류가 발생했습니다: ' + data.error);
437
- return;
438
- }
439
-
440
- alert(`성공적으로 ${data.object_count}개의 객체를 벡터 DB에 저장했습니다.`);
441
- })
442
- .catch(error => {
443
- saveLoading.style.display = 'none';
444
- saveToVectorDBBtn.disabled = false;
445
- console.error('Error:', error);
446
- alert('벡터 DB 저장 요청 중 오류가 발생했습니다.');
447
- });
448
- });
449
-
450
- // 검색 유형 변경 처리
451
- searchType.addEventListener('change', function() {
452
- if (this.value === 'image') {
453
- imageSearchSection.style.display = 'block';
454
- classSearchSection.style.display = 'none';
455
- } else if (this.value === 'class') {
456
- imageSearchSection.style.display = 'none';
457
- classSearchSection.style.display = 'block';
458
- }
459
- });
460
-
461
- // 검색 이미지 업로드 처리
462
- searchImageUpload.addEventListener('change', function(e) {
463
- const file = e.target.files[0];
464
- if (!file) return;
465
-
466
- const reader = new FileReader();
467
- reader.onload = function(event) {
468
- // 이미지 데이터 저장
469
- searchImageUpload.dataset.image = event.target.result;
470
- };
471
- reader.readAsDataURL(file);
472
- });
473
-
474
- // 검색 처리
475
- searchBtn.addEventListener('click', function() {
476
- // 로딩 표시
477
- searchLoading.style.display = 'flex';
478
- searchBtn.disabled = true;
479
-
480
- // 검색 유형에 따라 요청 데이터 구성
481
- const searchTypeValue = searchType.value;
482
- const nResults = parseInt(resultCount.value) || 5;
483
- let requestData = {
484
- searchType: searchTypeValue,
485
- nResults: nResults
486
- };
487
-
488
- if (searchTypeValue === 'image') {
489
- const imageData = searchImageUpload.dataset.image;
490
- if (!imageData) {
491
- alert('검색할 이미지를 업로드해주세요.');
492
- searchLoading.style.display = 'none';
493
- searchBtn.disabled = false;
494
- return;
495
- }
496
- requestData.image = imageData;
497
- } else if (searchTypeValue === 'class') {
498
- const className = classNameInput.value.trim();
499
- if (!className) {
500
- alert('검색할 클래스 이름을 입력해주세요.');
501
- searchLoading.style.display = 'none';
502
- searchBtn.disabled = false;
503
- return;
504
- }
505
- requestData.className = className;
506
- }
507
-
508
- // 검색 API 호출
509
- fetch('/api/search-similar-objects', {
510
- method: 'POST',
511
- headers: {
512
- 'Content-Type': 'application/json'
513
- },
514
- body: JSON.stringify(requestData)
515
- })
516
- .then(response => response.json())
517
- .then(data => {
518
- // 로딩 숨기기
519
- searchLoading.style.display = 'none';
520
- searchBtn.disabled = false;
521
-
522
- if (data.error) {
523
- alert('검색 중 오류가 발생했습니다: ' + data.error);
524
- return;
525
- }
526
-
527
- // 검색 결과 표시
528
- displaySearchResults(data.results);
529
- })
530
- .catch(error => {
531
- searchLoading.style.display = 'none';
532
- searchBtn.disabled = false;
533
- console.error('Error:', error);
534
- alert('검색 요청 중 오류가 발생했습니다.');
535
- });
536
- });
537
-
538
- // 검색 결과 표시 함수
539
- function displaySearchResults(results) {
540
- if (!results || results.length === 0) {
541
- searchResults.innerHTML = '<p>검색 결과가 없습니다.</p>';
542
- return;
543
- }
544
-
545
- let html = '<div class="row">';
546
-
547
- results.forEach((result, index) => {
548
- const metadata = result.metadata || {};
549
- const distance = result.distance ? (1 - result.distance).toFixed(2) * 100 : 0;
550
- const imageId = metadata.image_id || '';
551
- const className = metadata.class || '';
552
- const confidence = metadata.confidence ? Math.round(metadata.confidence * 100) : 0;
553
- const bbox = metadata.bbox || {};
554
-
555
- html += `
556
- <div class="col-md-6 mb-3">
557
- <div class="card">
558
- <div class="card-body">
559
- <h5 class="card-title">결과 #${index + 1}</h5>
560
- <div class="d-flex justify-content-between">
561
- <span class="badge bg-primary">${className}</span>
562
- <span class="badge bg-info">유사도: ${distance}%</span>
563
- </div>
564
- <p class="card-text mt-2">
565
- <small>객체 ID: ${result.id}</small><br>
566
- <small>이미지 ID: ${imageId}</small><br>
567
- <small>신뢰도: ${confidence}%</small><br>
568
- <small>위치: X=${(bbox.x * 100).toFixed(1)}%, Y=${(bbox.y * 100).toFixed(1)}%, W=${(bbox.width * 100).toFixed(1)}%, H=${(bbox.height * 100).toFixed(1)}%</small>
569
- </p>
570
- </div>
571
- </div>
572
- </div>
573
- `;
574
- });
575
-
576
- html += '</div>';
577
- searchResults.innerHTML = html;
578
- }
579
- </script>
580
- </body>
581
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/build/{precache-manifest.dcc3c32413232f3947b07b1749e190fa.js → precache-manifest.8a1e450c8ad569d0a69dec24accbc833.js} RENAMED
@@ -1,10 +1,10 @@
1
  self.__precacheManifest = (self.__precacheManifest || []).concat([
2
  {
3
- "revision": "0ba0e361ea42a45ddc58a633a94258f7",
4
  "url": "/index.html"
5
  },
6
  {
7
- "revision": "c6212c4de7d0d321df48",
8
  "url": "/static/css/main.59c2a54e.chunk.css"
9
  },
10
  {
@@ -20,8 +20,8 @@ self.__precacheManifest = (self.__precacheManifest || []).concat([
20
  "url": "/static/js/3.9013e23f.chunk.js"
21
  },
22
  {
23
- "revision": "c6212c4de7d0d321df48",
24
- "url": "/static/js/main.06c7c845.chunk.js"
25
  },
26
  {
27
  "revision": "d8c310b0ac7ffa6d8151",
 
1
  self.__precacheManifest = (self.__precacheManifest || []).concat([
2
  {
3
+ "revision": "67128ee0278f0489d4bdb77d6e184746",
4
  "url": "/index.html"
5
  },
6
  {
7
+ "revision": "e23f214e69e4b9e3e72a",
8
  "url": "/static/css/main.59c2a54e.chunk.css"
9
  },
10
  {
 
20
  "url": "/static/js/3.9013e23f.chunk.js"
21
  },
22
  {
23
+ "revision": "e23f214e69e4b9e3e72a",
24
+ "url": "/static/js/main.1756d180.chunk.js"
25
  },
26
  {
27
  "revision": "d8c310b0ac7ffa6d8151",
frontend/build/precache-manifest.e8825d818084296fa14f1b32e8815c1e.js DELETED
@@ -1,30 +0,0 @@
1
- self.__precacheManifest = (self.__precacheManifest || []).concat([
2
- {
3
- "revision": "d0bb3100f90d81918e1af551a295cc55",
4
- "url": "/index.html"
5
- },
6
- {
7
- "revision": "c40859d2e49da0b79907",
8
- "url": "/static/css/main.59c2a54e.chunk.css"
9
- },
10
- {
11
- "revision": "f417a8c7af9034db933e",
12
- "url": "/static/js/2.74e99ef6.chunk.js"
13
- },
14
- {
15
- "revision": "89a1b2dcd30c03705b2bceeb141b76b6",
16
- "url": "/static/js/2.74e99ef6.chunk.js.LICENSE.txt"
17
- },
18
- {
19
- "revision": "25f9bd0c0371bb013559",
20
- "url": "/static/js/3.0e3ce0f8.chunk.js"
21
- },
22
- {
23
- "revision": "c40859d2e49da0b79907",
24
- "url": "/static/js/main.3d1593c5.chunk.js"
25
- },
26
- {
27
- "revision": "97a891da203626eda40e",
28
- "url": "/static/js/runtime-main.ab7e4402.js"
29
- }
30
- ]);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/build/service-worker.js CHANGED
@@ -14,7 +14,7 @@
14
  importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");
15
 
16
  importScripts(
17
- "/precache-manifest.dcc3c32413232f3947b07b1749e190fa.js"
18
  );
19
 
20
  self.addEventListener('message', (event) => {
 
14
  importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");
15
 
16
  importScripts(
17
+ "/precache-manifest.8a1e450c8ad569d0a69dec24accbc833.js"
18
  );
19
 
20
  self.addEventListener('message', (event) => {
frontend/build/similar-images.html DELETED
@@ -1,279 +0,0 @@
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>유사 이미지 검색</title>
7
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
8
- <style>
9
- .image-container {
10
- display: flex;
11
- flex-wrap: wrap;
12
- gap: 15px;
13
- margin-top: 20px;
14
- }
15
- .image-card {
16
- border: 1px solid #ddd;
17
- border-radius: 8px;
18
- padding: 10px;
19
- width: 220px;
20
- }
21
- .image-preview {
22
- width: 200px;
23
- height: 200px;
24
- object-fit: cover;
25
- border-radius: 4px;
26
- margin-bottom: 10px;
27
- }
28
- .spinner-border {
29
- display: none;
30
- }
31
- .result-container {
32
- margin-top: 30px;
33
- }
34
- .similar-image {
35
- width: 150px;
36
- height: 150px;
37
- object-fit: cover;
38
- border-radius: 4px;
39
- }
40
- .similar-item {
41
- margin-bottom: 15px;
42
- }
43
- </style>
44
- </head>
45
- <body>
46
- <div class="container mt-5">
47
- <h1 class="mb-4">유사 이미지 검색</h1>
48
-
49
- <div class="row">
50
- <div class="col-md-6">
51
- <div class="card">
52
- <div class="card-header">
53
- <h5>이미지 업로드</h5>
54
- </div>
55
- <div class="card-body">
56
- <form id="uploadForm">
57
- <div class="mb-3">
58
- <label for="imageInput" class="form-label">이미지 선택</label>
59
- <input type="file" class="form-control" id="imageInput" accept="image/*">
60
- </div>
61
- <div class="mb-3">
62
- <div class="form-check">
63
- <input class="form-check-input" type="checkbox" id="addToCollection">
64
- <label class="form-check-label" for="addToCollection">
65
- 컬렉션에 이미지 추가
66
- </label>
67
- </div>
68
- </div>
69
- <button type="submit" class="btn btn-primary">
70
- <span class="spinner-border spinner-border-sm" id="searchSpinner" role="status" aria-hidden="true"></span>
71
- 유사 이미지 검색
72
- </button>
73
- </form>
74
-
75
- <div class="mt-3">
76
- <div id="previewContainer" style="display: none;">
77
- <h6>업로드된 이미지:</h6>
78
- <img id="imagePreview" class="image-preview" src="" alt="Preview">
79
- </div>
80
- </div>
81
- </div>
82
- </div>
83
-
84
- <div class="card mt-4">
85
- <div class="card-header">
86
- <h5>샘플 이미지 추가</h5>
87
- </div>
88
- <div class="card-body">
89
- <p>벡터 DB에 샘플 이미지를 추가합니다.</p>
90
- <button id="addSamplesBtn" class="btn btn-secondary">
91
- <span class="spinner-border spinner-border-sm" id="sampleSpinner" role="status" aria-hidden="true"></span>
92
- 샘플 이미지 추가
93
- </button>
94
- </div>
95
- </div>
96
- </div>
97
-
98
- <div class="col-md-6">
99
- <div class="card">
100
- <div class="card-header">
101
- <h5>검색 결과</h5>
102
- </div>
103
- <div class="card-body">
104
- <div id="resultsContainer">
105
- <p id="noResults">검색 결과가 여기에 표시됩니다.</p>
106
- <div id="similarImagesContainer" class="row"></div>
107
- </div>
108
- </div>
109
- </div>
110
- </div>
111
- </div>
112
- </div>
113
-
114
- <script>
115
- // 이미지 미리보기
116
- document.getElementById('imageInput').addEventListener('change', function(e) {
117
- const file = e.target.files[0];
118
- if (file) {
119
- const reader = new FileReader();
120
- reader.onload = function(event) {
121
- document.getElementById('imagePreview').src = event.target.result;
122
- document.getElementById('previewContainer').style.display = 'block';
123
- };
124
- reader.readAsDataURL(file);
125
- }
126
- });
127
-
128
- // 폼 제출 처리
129
- document.getElementById('uploadForm').addEventListener('submit', async function(e) {
130
- e.preventDefault();
131
-
132
- const fileInput = document.getElementById('imageInput');
133
- const addToCollection = document.getElementById('addToCollection').checked;
134
-
135
- if (!fileInput.files[0]) {
136
- alert('이미지를 선택해주세요.');
137
- return;
138
- }
139
-
140
- // 로딩 표시
141
- document.getElementById('searchSpinner').style.display = 'inline-block';
142
-
143
- const formData = new FormData();
144
- formData.append('image', fileInput.files[0]);
145
-
146
- try {
147
- // 컬렉션에 추가 옵션이 선택된 경우
148
- if (addToCollection) {
149
- const addResponse = await fetch('/api/add-to-collection', {
150
- method: 'POST',
151
- body: formData
152
- });
153
- const addResult = await addResponse.json();
154
- console.log('Add to collection result:', addResult);
155
- }
156
-
157
- // 유사 이미지 검색
158
- const searchResponse = await fetch('/api/similar-images', {
159
- method: 'POST',
160
- body: formData
161
- });
162
-
163
- const searchResult = await searchResponse.json();
164
- console.log('Search result:', searchResult);
165
-
166
- // 결과 표시
167
- displayResults(searchResult);
168
- } catch (error) {
169
- console.error('Error:', error);
170
- alert('오류가 발생했습니다: ' + error.message);
171
- } finally {
172
- // 로딩 표시 제거
173
- document.getElementById('searchSpinner').style.display = 'none';
174
- }
175
- });
176
-
177
- // 결과 표시 함수
178
- function displayResults(results) {
179
- const container = document.getElementById('similarImagesContainer');
180
- const noResults = document.getElementById('noResults');
181
-
182
- container.innerHTML = '';
183
-
184
- if (results.error) {
185
- noResults.textContent = '오류: ' + results.error;
186
- noResults.style.display = 'block';
187
- return;
188
- }
189
-
190
- if (!results.similar_images || results.similar_images.length === 0) {
191
- noResults.textContent = '유사한 이미지를 찾을 수 없습니다. 먼저 이미지를 컬렉션에 추가해보세요.';
192
- noResults.style.display = 'block';
193
- return;
194
- }
195
-
196
- noResults.style.display = 'none';
197
-
198
- results.similar_images.forEach((item, index) => {
199
- const col = document.createElement('div');
200
- col.className = 'col-6 similar-item';
201
-
202
- const card = document.createElement('div');
203
- card.className = 'card h-100';
204
-
205
- // 이미지 URL이 메타데이터에 있는 경우
206
- let imageUrl = '';
207
- if (item.metadata && item.metadata.url) {
208
- imageUrl = item.metadata.url;
209
- } else {
210
- // 실제 구현에서는 이미지 ID로 이미지를 가져오는 API가 필요할 수 있음
211
- imageUrl = 'https://via.placeholder.com/150?text=Image+' + (index + 1);
212
- }
213
-
214
- const distance = item.distance ? item.distance.toFixed(4) : 'N/A';
215
-
216
- card.innerHTML = `
217
- <img src="${imageUrl}" class="similar-image card-img-top" alt="Similar Image ${index + 1}">
218
- <div class="card-body">
219
- <h6 class="card-title">유사도: ${distance}</h6>
220
- <p class="card-text">ID: ${item.id.substring(0, 8)}...</p>
221
- </div>
222
- `;
223
-
224
- col.appendChild(card);
225
- container.appendChild(col);
226
- });
227
- }
228
-
229
- // 샘플 이미지 추가
230
- document.getElementById('addSamplesBtn').addEventListener('click', async function() {
231
- const spinner = document.getElementById('sampleSpinner');
232
- spinner.style.display = 'inline-block';
233
-
234
- try {
235
- // 샘플 이미지 URL 배열 (실제 구현에서는 적절한 이미지로 변경)
236
- const sampleImages = [
237
- { url: 'https://source.unsplash.com/random/300x300?cat', label: 'cat' },
238
- { url: 'https://source.unsplash.com/random/300x300?dog', label: 'dog' },
239
- { url: 'https://source.unsplash.com/random/300x300?bird', label: 'bird' },
240
- { url: 'https://source.unsplash.com/random/300x300?flower', label: 'flower' },
241
- { url: 'https://source.unsplash.com/random/300x300?car', label: 'car' }
242
- ];
243
-
244
- for (const sample of sampleImages) {
245
- // 이미지 가져오기
246
- const response = await fetch(sample.url);
247
- const blob = await response.blob();
248
-
249
- // FormData 생성
250
- const formData = new FormData();
251
- formData.append('image', blob, 'sample.jpg');
252
- formData.append('metadata', JSON.stringify({
253
- label: sample.label,
254
- url: sample.url
255
- }));
256
-
257
- // API 호출
258
- const addResponse = await fetch('/api/add-to-collection', {
259
- method: 'POST',
260
- body: formData
261
- });
262
-
263
- const result = await addResponse.json();
264
- console.log(`Added sample ${sample.label}:`, result);
265
- }
266
-
267
- alert('5개의 샘플 이미지가 컬렉션에 추가되었습니다.');
268
- } catch (error) {
269
- console.error('Error adding samples:', error);
270
- alert('샘플 이미지 추가 중 오류가 발생했습니다: ' + error.message);
271
- } finally {
272
- spinner.style.display = 'none';
273
- }
274
- });
275
- </script>
276
-
277
- <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
278
- </body>
279
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/build/static/js/2.74e99ef6.chunk.js DELETED
The diff for this file is too large to render. See raw diff
 
frontend/build/static/js/2.74e99ef6.chunk.js.LICENSE.txt DELETED
@@ -1,58 +0,0 @@
1
- /*
2
- object-assign
3
- (c) Sindre Sorhus
4
- @license MIT
5
- */
6
-
7
- /**
8
- * A better abstraction over CSS.
9
- *
10
- * @copyright Oleg Isonen (Slobodskoi) / Isonen 2014-present
11
- * @website https://github.com/cssinjs/jss
12
- * @license MIT
13
- */
14
-
15
- /** @license React v0.19.1
16
- * scheduler.production.min.js
17
- *
18
- * Copyright (c) Facebook, Inc. and its affiliates.
19
- *
20
- * This source code is licensed under the MIT license found in the
21
- * LICENSE file in the root directory of this source tree.
22
- */
23
-
24
- /** @license React v16.13.1
25
- * react-is.production.min.js
26
- *
27
- * Copyright (c) Facebook, Inc. and its affiliates.
28
- *
29
- * This source code is licensed under the MIT license found in the
30
- * LICENSE file in the root directory of this source tree.
31
- */
32
-
33
- /** @license React v16.14.0
34
- * react-dom.production.min.js
35
- *
36
- * Copyright (c) Facebook, Inc. and its affiliates.
37
- *
38
- * This source code is licensed under the MIT license found in the
39
- * LICENSE file in the root directory of this source tree.
40
- */
41
-
42
- /** @license React v16.14.0
43
- * react.production.min.js
44
- *
45
- * Copyright (c) Facebook, Inc. and its affiliates.
46
- *
47
- * This source code is licensed under the MIT license found in the
48
- * LICENSE file in the root directory of this source tree.
49
- */
50
-
51
- /** @license React v17.0.2
52
- * react-is.production.min.js
53
- *
54
- * Copyright (c) Facebook, Inc. and its affiliates.
55
- *
56
- * This source code is licensed under the MIT license found in the
57
- * LICENSE file in the root directory of this source tree.
58
- */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/build/static/js/2.74e99ef6.chunk.js.map DELETED
The diff for this file is too large to render. See raw diff
 
frontend/build/static/js/3.0e3ce0f8.chunk.js DELETED
@@ -1,2 +0,0 @@
1
- (this["webpackJsonpvision-web-app"]=this["webpackJsonpvision-web-app"]||[]).push([[3],{137:function(t,n,e){"use strict";e.r(n),e.d(n,"getCLS",(function(){return l})),e.d(n,"getFCP",(function(){return g})),e.d(n,"getFID",(function(){return h})),e.d(n,"getLCP",(function(){return y})),e.d(n,"getTTFB",(function(){return F}));var i,a,r=function(){return"".concat(Date.now(),"-").concat(Math.floor(8999999999999*Math.random())+1e12)},o=function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:-1;return{name:t,value:n,delta:0,entries:[],id:r(),isFinal:!1}},u=function(t,n){try{if(PerformanceObserver.supportedEntryTypes.includes(t)){var e=new PerformanceObserver((function(t){return t.getEntries().map(n)}));return e.observe({type:t,buffered:!0}),e}}catch(t){}},s=!1,c=!1,p=function(t){s=!t.persisted},d=function(){addEventListener("pagehide",p),addEventListener("beforeunload",(function(){}))},f=function(t){var n=arguments.length>1&&void 0!==arguments[1]&&arguments[1];c||(d(),c=!0),addEventListener("visibilitychange",(function(n){var e=n.timeStamp;"hidden"===document.visibilityState&&t({timeStamp:e,isUnloading:s})}),{capture:!0,once:n})},v=function(t,n,e,i){var a;return function(){e&&n.isFinal&&e.disconnect(),n.value>=0&&(i||n.isFinal||"hidden"===document.visibilityState)&&(n.delta=n.value-(a||0),(n.delta||n.isFinal||void 0===a)&&(t(n),a=n.value))}},l=function(t){var n,e=arguments.length>1&&void 0!==arguments[1]&&arguments[1],i=o("CLS",0),a=function(t){t.hadRecentInput||(i.value+=t.value,i.entries.push(t),n())},r=u("layout-shift",a);r&&(n=v(t,i,r,e),f((function(t){var e=t.isUnloading;r.takeRecords().map(a),e&&(i.isFinal=!0),n()})))},m=function(){return void 0===i&&(i="hidden"===document.visibilityState?0:1/0,f((function(t){var n=t.timeStamp;return i=n}),!0)),{get timeStamp(){return i}}},g=function(t){var n,e=o("FCP"),i=m(),a=u("paint",(function(t){"first-contentful-paint"===t.name&&t.startTime<i.timeStamp&&(e.value=t.startTime,e.isFinal=!0,e.entries.push(t),n())}));a&&(n=v(t,e,a))},h=function(t){var n=o("FID"),e=m(),i=function(t){t.startTime<e.timeStamp&&(n.value=t.processingStart-t.startTime,n.entries.push(t),n.isFinal=!0,r())},a=u("first-input",i),r=v(t,n,a);a?f((function(){a.takeRecords().map(i),a.disconnect()}),!0):window.perfMetrics&&window.perfMetrics.onFirstInputDelay&&window.perfMetrics.onFirstInputDelay((function(t,i){i.timeStamp<e.timeStamp&&(n.value=t,n.isFinal=!0,n.entries=[{entryType:"first-input",name:i.type,target:i.target,cancelable:i.cancelable,startTime:i.timeStamp,processingStart:i.timeStamp+t}],r())}))},S=function(){return a||(a=new Promise((function(t){return["scroll","keydown","pointerdown"].map((function(n){addEventListener(n,t,{once:!0,passive:!0,capture:!0})}))}))),a},y=function(t){var n,e=arguments.length>1&&void 0!==arguments[1]&&arguments[1],i=o("LCP"),a=m(),r=function(t){var e=t.startTime;e<a.timeStamp?(i.value=e,i.entries.push(t)):i.isFinal=!0,n()},s=u("largest-contentful-paint",r);if(s){n=v(t,i,s,e);var c=function(){i.isFinal||(s.takeRecords().map(r),i.isFinal=!0,n())};S().then(c),f(c,!0)}},F=function(t){var n,e=o("TTFB");n=function(){try{var n=performance.getEntriesByType("navigation")[0]||function(){var t=performance.timing,n={entryType:"navigation",startTime:0};for(var e in t)"navigationStart"!==e&&"toJSON"!==e&&(n[e]=Math.max(t[e]-t.navigationStart,0));return n}();e.value=e.delta=n.responseStart,e.entries=[n],e.isFinal=!0,t(e)}catch(t){}},"complete"===document.readyState?setTimeout(n,0):addEventListener("pageshow",n)}}}]);
2
- //# sourceMappingURL=3.0e3ce0f8.chunk.js.map
 
 
 
frontend/build/static/js/3.0e3ce0f8.chunk.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../node_modules/web-vitals/dist/web-vitals.es5.min.js"],"names":["v","t","n","e","concat","Date","now","Math","floor","random","i","arguments","length","name","value","delta","entries","id","isFinal","a","PerformanceObserver","supportedEntryTypes","includes","getEntries","map","observe","type","buffered","r","o","s","persisted","u","addEventListener","c","timeStamp","document","visibilityState","isUnloading","capture","once","l","disconnect","p","hadRecentInput","push","takeRecords","d","startTime","f","processingStart","window","perfMetrics","onFirstInputDelay","entryType","target","cancelable","m","Promise","passive","g","then","h","performance","getEntriesByType","timing","max","navigationStart","responseStart","readyState","setTimeout"],"mappings":"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","file":"static/js/3.0e3ce0f8.chunk.js","sourcesContent":["var t,n,e=function(){return\"\".concat(Date.now(),\"-\").concat(Math.floor(8999999999999*Math.random())+1e12)},i=function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:-1;return{name:t,value:n,delta:0,entries:[],id:e(),isFinal:!1}},a=function(t,n){try{if(PerformanceObserver.supportedEntryTypes.includes(t)){var e=new PerformanceObserver((function(t){return t.getEntries().map(n)}));return e.observe({type:t,buffered:!0}),e}}catch(t){}},r=!1,o=!1,s=function(t){r=!t.persisted},u=function(){addEventListener(\"pagehide\",s),addEventListener(\"beforeunload\",(function(){}))},c=function(t){var n=arguments.length>1&&void 0!==arguments[1]&&arguments[1];o||(u(),o=!0),addEventListener(\"visibilitychange\",(function(n){var e=n.timeStamp;\"hidden\"===document.visibilityState&&t({timeStamp:e,isUnloading:r})}),{capture:!0,once:n})},l=function(t,n,e,i){var a;return function(){e&&n.isFinal&&e.disconnect(),n.value>=0&&(i||n.isFinal||\"hidden\"===document.visibilityState)&&(n.delta=n.value-(a||0),(n.delta||n.isFinal||void 0===a)&&(t(n),a=n.value))}},p=function(t){var n,e=arguments.length>1&&void 0!==arguments[1]&&arguments[1],r=i(\"CLS\",0),o=function(t){t.hadRecentInput||(r.value+=t.value,r.entries.push(t),n())},s=a(\"layout-shift\",o);s&&(n=l(t,r,s,e),c((function(t){var e=t.isUnloading;s.takeRecords().map(o),e&&(r.isFinal=!0),n()})))},d=function(){return void 0===t&&(t=\"hidden\"===document.visibilityState?0:1/0,c((function(n){var e=n.timeStamp;return t=e}),!0)),{get timeStamp(){return t}}},v=function(t){var n,e=i(\"FCP\"),r=d(),o=a(\"paint\",(function(t){\"first-contentful-paint\"===t.name&&t.startTime<r.timeStamp&&(e.value=t.startTime,e.isFinal=!0,e.entries.push(t),n())}));o&&(n=l(t,e,o))},f=function(t){var n=i(\"FID\"),e=d(),r=function(t){t.startTime<e.timeStamp&&(n.value=t.processingStart-t.startTime,n.entries.push(t),n.isFinal=!0,s())},o=a(\"first-input\",r),s=l(t,n,o);o?c((function(){o.takeRecords().map(r),o.disconnect()}),!0):window.perfMetrics&&window.perfMetrics.onFirstInputDelay&&window.perfMetrics.onFirstInputDelay((function(t,i){i.timeStamp<e.timeStamp&&(n.value=t,n.isFinal=!0,n.entries=[{entryType:\"first-input\",name:i.type,target:i.target,cancelable:i.cancelable,startTime:i.timeStamp,processingStart:i.timeStamp+t}],s())}))},m=function(){return n||(n=new Promise((function(t){return[\"scroll\",\"keydown\",\"pointerdown\"].map((function(n){addEventListener(n,t,{once:!0,passive:!0,capture:!0})}))}))),n},g=function(t){var n,e=arguments.length>1&&void 0!==arguments[1]&&arguments[1],r=i(\"LCP\"),o=d(),s=function(t){var e=t.startTime;e<o.timeStamp?(r.value=e,r.entries.push(t)):r.isFinal=!0,n()},u=a(\"largest-contentful-paint\",s);if(u){n=l(t,r,u,e);var p=function(){r.isFinal||(u.takeRecords().map(s),r.isFinal=!0,n())};m().then(p),c(p,!0)}},h=function(t){var n,e=i(\"TTFB\");n=function(){try{var n=performance.getEntriesByType(\"navigation\")[0]||function(){var t=performance.timing,n={entryType:\"navigation\",startTime:0};for(var e in t)\"navigationStart\"!==e&&\"toJSON\"!==e&&(n[e]=Math.max(t[e]-t.navigationStart,0));return n}();e.value=e.delta=n.responseStart,e.entries=[n],e.isFinal=!0,t(e)}catch(t){}},\"complete\"===document.readyState?setTimeout(n,0):addEventListener(\"pageshow\",n)};export{p as getCLS,v as getFCP,f as getFID,g as getLCP,h as getTTFB};\n"],"sourceRoot":""}
 
 
frontend/build/static/js/main.06c7c845.chunk.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["components/ImageUploader.js","components/ModelSelector.js","components/VectorDBActions.js","components/ResultDisplay.js","components/LlmAnalysis.js","App.js","reportWebVitals.js","index.js"],"names":["useStyles","makeStyles","theme","paper","padding","spacing","display","flexDirection","alignItems","height","minHeight","transition","dragActive","border","backgroundColor","dragInactive","uploadBox","justifyContent","width","cursor","uploadIcon","fontSize","color","marginBottom","supportText","marginTop","previewBox","position","imageContainer","overflow","deleteButton","top","right","ImageUploader","_ref","onImageUpload","classes","previewUrl","setPreviewUrl","useState","setDragActive","fileInputRef","useRef","handleDrag","e","preventDefault","stopPropagation","type","handleFiles","file","startsWith","URL","createObjectURL","alert","React","createElement","Paper","className","concat","onDragEnter","onDragLeave","onDragOver","onDrop","dataTransfer","files","ref","accept","onChange","target","style","Box","Typography","variant","gutterBottom","src","alt","IconButton","aria-label","onClick","handleRemoveImage","current","value","DeleteIcon","onButtonClick","click","CloudUploadIcon","Button","component","startIcon","card","selectedCard","unavailableCard","opacity","cardContent","flexGrow","chipContainer","successChip","errorChip","modelType","processButton","textAlign","ModelSelector","onModelSelect","onProcess","isProcessing","modelsStatus","selectedModel","imageSelected","models","id","name","description","icon","VisibilityIcon","available","yolo","detr","CategoryIcon","vit","handleModelClick","modelId","find","m","sx","p","Grid","container","map","model","item","xs","sm","key","Card","CardContent","mb","Chip","label","size","CardActions","disabled","fullWidth","PlayArrowIcon","root","borderRadius","shape","button","marginRight","searchDialog","minWidth","formControl","searchResults","resultCard","resultImage","objectFit","chip","margin","similarityChip","palette","primary","main","VectorDBActions","results","isSaving","setIsSaving","saveSuccess","setSaveSuccess","saveError","setSaveError","openSearchDialog","setOpenSearchDialog","searchType","setSearchType","searchClass","setSearchClass","setSearchResults","isSearching","setIsSearching","searchError","setSearchError","data","handleCloseSearchDialog","generateUUID","replace","c","r","Math","random","toString","async","response","fetch","method","headers","body","JSON","stringify","image","metadata","classifications","objects","image_id","ok","Error","status","result","json","error","setTimeout","err","console","message","Fragment","CircularProgress","handleOpenSearchDialog","Alert","severity","Snackbar","open","autoHideDuration","onClose","Dialog","maxWidth","DialogTitle","DialogContent","FormControl","InputLabel","Select","labelId","event","MenuItem","TextField","placeholder","marginLeft","length","index","similarity","distance","CardMedia","title","toFixed","class","confidence","DialogActions","requestBody","n_results","trim","class_name","maxHeight","dividerMargin","gap","flexWrap","ResultDisplay","bgcolor","renderPerformanceInfo","performance","Divider","ms","undefined","isNaN","num","Number","formatTime","inference_time","device","md","detections","List","detection","ListItem","ListItemText","secondary","bbox","join","top_predictions","prediction","rank","probability","responseBox","whiteSpace","buttonProgress","LlmAnalysis","visionResults","userQuery","setUserQuery","isAnalyzing","setIsAnalyzing","analysisResult","setAnalysisResult","setError","mt","createMuiTheme","typography","fontFamily","App","selectedImage","setSelectedImage","setSelectedModel","setIsProcessing","setResults","setModelsStatus","useEffect","then","catch","ThemeProvider","AppBar","Toolbar","Container","paragraph","formData","FormData","append","endpoint","reportWebVitals","onPerfEntry","Function","getCLS","getFID","getFCP","getLCP","getTTFB","ReactDOM","render","StrictMode","document","getElementById"],"mappings":"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","file":"static/js/main.06c7c845.chunk.js","sourcesContent":["import React, { useState, useRef } from 'react';\nimport { \n Paper, \n Typography, \n Box, \n Button, \n IconButton \n} from '@material-ui/core';\nimport CloudUploadIcon from '@material-ui/icons/CloudUpload';\nimport DeleteIcon from '@material-ui/icons/Delete';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n paper: {\n padding: theme.spacing(2),\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n height: '100%',\n minHeight: 300,\n transition: 'all 0.3s ease'\n },\n dragActive: {\n border: '2px dashed #3f51b5',\n backgroundColor: 'rgba(63, 81, 181, 0.05)'\n },\n dragInactive: {\n border: '2px dashed #ccc',\n backgroundColor: 'white'\n },\n uploadBox: {\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n height: '100%',\n width: '100%',\n cursor: 'pointer'\n },\n uploadIcon: {\n fontSize: 60,\n color: '#3f51b5',\n marginBottom: theme.spacing(2)\n },\n supportText: {\n marginTop: theme.spacing(2)\n },\n previewBox: {\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n width: '100%',\n height: '100%',\n position: 'relative'\n },\n imageContainer: {\n position: 'relative',\n width: '100%',\n height: '100%',\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n overflow: 'hidden',\n marginTop: theme.spacing(2)\n },\n deleteButton: {\n position: 'absolute',\n top: 0,\n right: 0,\n backgroundColor: 'rgba(255, 255, 255, 0.7)',\n '&:hover': {\n backgroundColor: 'rgba(255, 255, 255, 0.9)',\n }\n }\n}));\n\nconst ImageUploader = ({ onImageUpload }) => {\n const classes = useStyles();\n const [previewUrl, setPreviewUrl] = useState(null);\n const [dragActive, setDragActive] = useState(false);\n const fileInputRef = useRef(null);\n\n const handleDrag = (e) => {\n e.preventDefault();\n e.stopPropagation();\n if (e.type === 'dragenter' || e.type === 'dragover') {\n setDragActive(true);\n } else if (e.type === 'dragleave') {\n setDragActive(false);\n }\n };\n\n const handleDrop = (e) => {\n e.preventDefault();\n e.stopPropagation();\n setDragActive(false);\n if (e.dataTransfer.files && e.dataTransfer.files[0]) {\n handleFiles(e.dataTransfer.files[0]);\n }\n };\n\n const handleChange = (e) => {\n e.preventDefault();\n if (e.target.files && e.target.files[0]) {\n handleFiles(e.target.files[0]);\n }\n };\n\n const handleFiles = (file) => {\n if (file.type.startsWith('image/')) {\n setPreviewUrl(URL.createObjectURL(file));\n onImageUpload(file);\n } else {\n alert('Please upload an image file');\n }\n };\n\n const onButtonClick = () => {\n fileInputRef.current.click();\n };\n\n const handleRemoveImage = () => {\n setPreviewUrl(null);\n onImageUpload(null);\n fileInputRef.current.value = \"\";\n };\n\n return (\n <Paper \n className={`${classes.paper} ${dragActive ? classes.dragActive : classes.dragInactive}`}\n onDragEnter={handleDrag}\n onDragLeave={handleDrag}\n onDragOver={handleDrag}\n onDrop={handleDrop}\n >\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*\"\n onChange={handleChange}\n style={{ display: 'none' }}\n />\n\n {!previewUrl ? (\n <Box \n className={classes.uploadBox}\n onClick={onButtonClick}\n >\n <CloudUploadIcon className={classes.uploadIcon} />\n <Typography variant=\"h6\" gutterBottom>\n Drag & Drop an image here\n </Typography>\n <Typography variant=\"body2\" color=\"textSecondary\" gutterBottom>\n or\n </Typography>\n <Button\n variant=\"contained\"\n color=\"primary\"\n component=\"span\"\n startIcon={<CloudUploadIcon />}\n >\n Browse Files\n </Button>\n <Typography variant=\"body2\" color=\"textSecondary\" className={classes.supportText}>\n Supported formats: JPG, PNG, GIF\n </Typography>\n </Box>\n ) : (\n <Box className={classes.previewBox}>\n <Typography variant=\"h6\" gutterBottom>\n Preview\n </Typography>\n <Box className={classes.imageContainer}>\n <img\n src={previewUrl}\n alt=\"Preview\"\n className=\"preview-image\"\n />\n <IconButton\n aria-label=\"delete\"\n className={classes.deleteButton}\n onClick={handleRemoveImage}\n >\n <DeleteIcon />\n </IconButton>\n </Box>\n </Box>\n )}\n </Paper>\n );\n};\n\nexport default ImageUploader;\n","import React from 'react';\nimport { \n Grid, \n Card, \n CardContent, \n CardActions, \n Typography, \n Button, \n Chip,\n Box\n} from '@material-ui/core';\nimport VisibilityIcon from '@material-ui/icons/Visibility';\nimport CategoryIcon from '@material-ui/icons/Category';\nimport PlayArrowIcon from '@material-ui/icons/PlayArrow';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n card: {\n height: '100%',\n display: 'flex',\n flexDirection: 'column',\n },\n selectedCard: {\n border: '2px solid #3f51b5',\n },\n unavailableCard: {\n opacity: 0.6,\n },\n cardContent: {\n flexGrow: 1,\n },\n chipContainer: {\n marginBottom: theme.spacing(1.5),\n },\n successChip: {\n backgroundColor: '#34C759',\n color: '#fff',\n },\n errorChip: {\n backgroundColor: '#FF3B3F',\n color: '#fff',\n },\n modelType: {\n marginTop: theme.spacing(1),\n },\n processButton: {\n marginTop: theme.spacing(3),\n textAlign: 'center',\n }\n}));\n\nconst ModelSelector = ({ \n onModelSelect, \n onProcess, \n isProcessing, \n modelsStatus, \n selectedModel,\n imageSelected \n}) => {\n const classes = useStyles();\n \n const models = [\n {\n id: 'yolo',\n name: 'YOLOv8',\n description: 'Fast and accurate object detection',\n icon: <VisibilityIcon />,\n available: modelsStatus.yolo\n },\n {\n id: 'detr',\n name: 'DETR',\n description: 'DEtection TRansformer for object detection',\n icon: <VisibilityIcon />,\n available: modelsStatus.detr\n },\n {\n id: 'vit',\n name: 'ViT',\n description: 'Vision Transformer for image classification',\n icon: <CategoryIcon />,\n available: modelsStatus.vit\n }\n ];\n\n const handleModelClick = (modelId) => {\n if (models.find(m => m.id === modelId).available) {\n onModelSelect(modelId);\n }\n };\n\n return (\n <Box sx={{ p: 2, height: '100%' }}>\n <Typography variant=\"h6\" gutterBottom>\n Select Model\n </Typography>\n \n <Grid container spacing={2}>\n {models.map((model) => (\n <Grid item xs={12} sm={4} key={model.id}>\n <Card \n className={`\n ${classes.card} \n ${selectedModel === model.id ? classes.selectedCard : ''} \n ${!model.available ? classes.unavailableCard : ''}\n `}\n onClick={() => handleModelClick(model.id)}\n >\n <CardContent className={classes.cardContent}>\n <Box sx={{ mb: 2, color: 'primary' }}>\n {model.icon}\n </Box>\n <Typography variant=\"h5\" component=\"div\" gutterBottom>\n {model.name}\n </Typography>\n <div className={classes.chipContainer}>\n {model.available ? (\n <Chip \n label=\"Available\" \n className={classes.successChip}\n size=\"small\" \n />\n ) : (\n <Chip \n label=\"Not Available\" \n className={classes.errorChip}\n size=\"small\" \n />\n )}\n </div>\n <Typography variant=\"body2\" color=\"textSecondary\">\n {model.description}\n </Typography>\n </CardContent>\n <CardActions>\n <Button \n size=\"small\" \n onClick={() => handleModelClick(model.id)}\n disabled={!model.available}\n color={selectedModel === model.id ? \"primary\" : \"default\"}\n variant={selectedModel === model.id ? \"contained\" : \"outlined\"}\n fullWidth\n >\n {selectedModel === model.id ? 'Selected' : 'Select'}\n </Button>\n </CardActions>\n </Card>\n </Grid>\n ))}\n </Grid>\n\n <div className={classes.processButton}>\n <Button\n variant=\"contained\"\n color=\"primary\"\n size=\"large\"\n startIcon={<PlayArrowIcon />}\n onClick={onProcess}\n disabled={!selectedModel || !imageSelected || isProcessing}\n >\n {isProcessing ? 'Processing...' : 'Process Image'}\n </Button>\n </div>\n </Box>\n );\n};\n\nexport default ModelSelector;\n","import React, { useState } from 'react';\nimport { \n Button, \n Box, \n Typography, \n CircularProgress, \n Snackbar,\n Dialog,\n DialogTitle,\n DialogContent,\n DialogActions,\n TextField,\n FormControl,\n InputLabel,\n Select,\n MenuItem,\n Grid,\n Card,\n CardMedia,\n CardContent,\n Chip\n} from '@material-ui/core';\nimport { Alert } from '@material-ui/lab';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n root: {\n marginTop: theme.spacing(2),\n marginBottom: theme.spacing(2),\n padding: theme.spacing(2),\n backgroundColor: '#f5f5f5',\n borderRadius: theme.shape.borderRadius,\n },\n button: {\n marginRight: theme.spacing(2),\n },\n searchDialog: {\n minWidth: '500px',\n },\n formControl: {\n marginBottom: theme.spacing(2),\n minWidth: '100%',\n },\n searchResults: {\n marginTop: theme.spacing(2),\n },\n resultCard: {\n marginBottom: theme.spacing(2),\n },\n resultImage: {\n height: 140,\n objectFit: 'contain',\n },\n chip: {\n margin: theme.spacing(0.5),\n },\n similarityChip: {\n backgroundColor: theme.palette.primary.main,\n color: 'white',\n }\n}));\n\nconst VectorDBActions = ({ results }) => {\n const classes = useStyles();\n const [isSaving, setIsSaving] = useState(false);\n const [saveSuccess, setSaveSuccess] = useState(false);\n const [saveError, setSaveError] = useState(null);\n const [openSearchDialog, setOpenSearchDialog] = useState(false);\n const [searchType, setSearchType] = useState('image');\n const [searchClass, setSearchClass] = useState('');\n const [searchResults, setSearchResults] = useState([]);\n const [isSearching, setIsSearching] = useState(false);\n const [searchError, setSearchError] = useState(null);\n \n // Extract model and data from results\n const { model, data } = results;\n \n // Handle saving to vector DB\n const handleSaveToVectorDB = async () => {\n setIsSaving(true);\n setSaveError(null);\n \n try {\n let response;\n \n if (model === 'vit') {\n // For ViT, save the whole image with classifications\n response = await fetch('/api/add-image', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n image: data.image,\n metadata: {\n model: 'vit',\n classifications: data.classifications\n }\n })\n });\n } else {\n // For YOLO and DETR, save detected objects\n response = await fetch('/api/add-detected-objects', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n image: data.image,\n objects: data.objects,\n image_id: generateUUID()\n })\n });\n }\n \n if (!response.ok) {\n throw new Error(`HTTP error! Status: ${response.status}`);\n }\n \n const result = await response.json();\n \n if (result.error) {\n throw new Error(result.error);\n }\n \n setSaveSuccess(true);\n setTimeout(() => setSaveSuccess(false), 5000);\n } catch (err) {\n console.error('Error saving to vector DB:', err);\n setSaveError(`Error saving to vector DB: ${err.message}`);\n } finally {\n setIsSaving(false);\n }\n };\n \n // Handle opening search dialog\n const handleOpenSearchDialog = () => {\n setOpenSearchDialog(true);\n setSearchResults([]);\n setSearchError(null);\n };\n \n // Handle closing search dialog\n const handleCloseSearchDialog = () => {\n setOpenSearchDialog(false);\n };\n \n // Handle search type change\n const handleSearchTypeChange = (event) => {\n setSearchType(event.target.value);\n setSearchResults([]);\n setSearchError(null);\n };\n \n // Handle search class change\n const handleSearchClassChange = (event) => {\n setSearchClass(event.target.value);\n };\n \n // Handle search\n const handleSearch = async () => {\n setIsSearching(true);\n setSearchError(null);\n \n try {\n let requestBody = {};\n \n if (searchType === 'image') {\n // Search by current image\n requestBody = {\n image: data.image,\n n_results: 5\n };\n } else {\n // Search by class name\n if (!searchClass.trim()) {\n throw new Error('Please enter a class name');\n }\n \n requestBody = {\n class_name: searchClass.trim(),\n n_results: 5\n };\n }\n \n const response = await fetch('/api/search-similar-objects', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(requestBody)\n });\n \n if (!response.ok) {\n throw new Error(`HTTP error! Status: ${response.status}`);\n }\n \n const result = await response.json();\n \n if (result.error) {\n throw new Error(result.error);\n }\n \n setSearchResults(result);\n } catch (err) {\n console.error('Error searching vector DB:', err);\n setSearchError(`Error searching vector DB: ${err.message}`);\n } finally {\n setIsSearching(false);\n }\n };\n \n // Generate UUID for image ID\n const generateUUID = () => {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n const r = Math.random() * 16 | 0;\n const v = c === 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n };\n \n // Render search results\n const renderSearchResults = () => {\n if (searchResults.length === 0) {\n return (\n <Typography variant=\"body1\">No results found.</Typography>\n );\n }\n \n return (\n <Grid container spacing={2}>\n {searchResults.map((result, index) => {\n const similarity = (1 - result.distance) * 100;\n \n return (\n <Grid item xs={12} sm={6} key={index}>\n <Card className={classes.resultCard}>\n <CardMedia\n className={classes.resultImage}\n image={`data:image/jpeg;base64,${result.image}`}\n title={`Result ${index + 1}`}\n />\n <CardContent>\n <Box display=\"flex\" justifyContent=\"space-between\" alignItems=\"center\" mb={1}>\n <Typography variant=\"subtitle1\">Result #{index + 1}</Typography>\n <Chip \n label={`Similarity: ${similarity.toFixed(2)}%`}\n className={classes.similarityChip}\n size=\"small\"\n />\n </Box>\n <Typography variant=\"body2\" color=\"textSecondary\">\n <strong>Class:</strong> {result.metadata.class || 'N/A'}\n </Typography>\n {result.metadata.confidence && (\n <Typography variant=\"body2\" color=\"textSecondary\">\n <strong>Confidence:</strong> {(result.metadata.confidence * 100).toFixed(2)}%\n </Typography>\n )}\n <Typography variant=\"body2\" color=\"textSecondary\">\n <strong>Object ID:</strong> {result.id}\n </Typography>\n </CardContent>\n </Card>\n </Grid>\n );\n })}\n </Grid>\n );\n };\n \n return (\n <Box className={classes.root}>\n <Typography variant=\"h6\" gutterBottom>\n Vector Database Actions\n </Typography>\n \n <Box display=\"flex\" alignItems=\"center\" mb={2}>\n <Button\n variant=\"contained\"\n color=\"primary\"\n onClick={handleSaveToVectorDB}\n disabled={isSaving}\n className={classes.button}\n >\n {isSaving ? (\n <>\n <CircularProgress size={20} color=\"inherit\" style={{ marginRight: 8 }} />\n Saving...\n </>\n ) : (\n 'Save to Vector DB'\n )}\n </Button>\n \n <Button\n variant=\"outlined\"\n color=\"primary\"\n onClick={handleOpenSearchDialog}\n className={classes.button}\n >\n Search Similar\n </Button>\n </Box>\n \n {saveError && (\n <Alert severity=\"error\" style={{ marginTop: 8 }}>\n {saveError}\n </Alert>\n )}\n \n <Snackbar open={saveSuccess} autoHideDuration={5000} onClose={() => setSaveSuccess(false)}>\n <Alert severity=\"success\">\n {model === 'vit' ? (\n 'Image and classifications successfully saved to vector DB!'\n ) : (\n 'Detected objects successfully saved to vector DB!'\n )}\n </Alert>\n </Snackbar>\n \n {/* Search Dialog */}\n <Dialog\n open={openSearchDialog}\n onClose={handleCloseSearchDialog}\n maxWidth=\"md\"\n fullWidth\n >\n <DialogTitle>Search Vector Database</DialogTitle>\n <DialogContent>\n <FormControl className={classes.formControl}>\n <InputLabel id=\"search-type-label\">Search Type</InputLabel>\n <Select\n labelId=\"search-type-label\"\n id=\"search-type\"\n value={searchType}\n onChange={handleSearchTypeChange}\n >\n <MenuItem value=\"image\">Search by Current Image</MenuItem>\n <MenuItem value=\"class\">Search by Class Name</MenuItem>\n </Select>\n </FormControl>\n \n {searchType === 'class' && (\n <FormControl className={classes.formControl}>\n <TextField\n label=\"Class Name\"\n value={searchClass}\n onChange={handleSearchClassChange}\n placeholder=\"e.g. person, car, dog...\"\n fullWidth\n />\n </FormControl>\n )}\n \n {searchError && (\n <Alert severity=\"error\" style={{ marginBottom: 16 }}>\n {searchError}\n </Alert>\n )}\n \n <Box className={classes.searchResults}>\n {isSearching ? (\n <Box display=\"flex\" justifyContent=\"center\" alignItems=\"center\" p={4}>\n <CircularProgress />\n <Typography variant=\"body1\" style={{ marginLeft: 16 }}>\n Searching...\n </Typography>\n </Box>\n ) : (\n searchResults.length > 0 && renderSearchResults()\n )}\n </Box>\n </DialogContent>\n <DialogActions>\n <Button onClick={handleCloseSearchDialog} color=\"default\">\n Close\n </Button>\n <Button \n onClick={handleSearch} \n color=\"primary\" \n variant=\"contained\"\n disabled={isSearching || (searchType === 'class' && !searchClass.trim())}\n >\n Search\n </Button>\n </DialogActions>\n </Dialog>\n </Box>\n );\n};\n\nexport default VectorDBActions;\n","import React from 'react';\nimport { \n Paper, \n Typography, \n Box, \n List, \n ListItem, \n ListItemText, \n Divider,\n Grid,\n Chip\n} from '@material-ui/core';\nimport VectorDBActions from './VectorDBActions';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n paper: {\n padding: theme.spacing(2)\n },\n marginBottom: {\n marginBottom: theme.spacing(2)\n },\n resultImage: {\n maxWidth: '100%',\n maxHeight: '400px',\n objectFit: 'contain'\n },\n dividerMargin: {\n margin: `${theme.spacing(2)}px 0`\n },\n chipContainer: {\n display: 'flex',\n gap: theme.spacing(1),\n flexWrap: 'wrap'\n }\n}));\n\nconst ResultDisplay = ({ results }) => {\n const classes = useStyles();\n if (!results) return null;\n \n const { model, data } = results;\n \n // Helper to format times nicely\n const formatTime = (ms) => {\n if (ms === undefined || ms === null || isNaN(ms)) return '-';\n const num = Number(ms);\n if (num < 1000) return `${num.toFixed(2)} ms`;\n return `${(num / 1000).toFixed(2)} s`;\n };\n \n // Check if there's an error\n if (data.error) {\n return (\n <Paper sx={{ p: 2, bgcolor: '#ffebee' }}>\n <Typography color=\"error\">{data.error}</Typography>\n </Paper>\n );\n }\n\n // Display performance info\n const renderPerformanceInfo = () => {\n if (!data.performance) return null;\n \n return (\n <Box className=\"performance-info\">\n <Divider className={classes.dividerMargin} />\n <Typography variant=\"body2\">\n Inference time: {formatTime(data.performance.inference_time)} on {data.performance.device}\n </Typography>\n </Box>\n );\n };\n\n // Render for YOLO and DETR (object detection)\n if (model === 'yolo' || model === 'detr') {\n return (\n <Paper className={classes.paper}>\n <Typography variant=\"h6\" gutterBottom>\n {model === 'yolo' ? 'YOLOv8' : 'DETR'} Detection Results\n </Typography>\n \n <Grid container spacing={3}>\n <Grid item xs={12} md={6}>\n {data.image && (\n <Box className={classes.marginBottom}>\n <Typography variant=\"subtitle1\" gutterBottom>\n Detection Result\n </Typography>\n <img \n src={`data:image/png;base64,${data.image}`} \n alt=\"Detection Result\" \n className={classes.resultImage}\n />\n </Box>\n )}\n </Grid>\n \n <Grid item xs={12} md={6}>\n <Box className={classes.marginBottom}>\n <Typography variant=\"subtitle1\" gutterBottom>\n Detected Objects:\n </Typography>\n \n {data.detections && data.detections.length > 0 ? (\n <List>\n {data.detections.map((detection, index) => (\n <React.Fragment key={index}>\n <ListItem>\n <ListItemText \n primary={\n <Box style={{ display: 'flex', alignItems: 'center' }}>\n <Typography variant=\"body1\" component=\"span\">\n {detection.class}\n </Typography>\n <Chip \n label={`${(detection.confidence * 100).toFixed(0)}%`}\n size=\"small\"\n color=\"primary\"\n style={{ marginLeft: 8 }}\n />\n </Box>\n } \n secondary={`Bounding Box: [${detection.bbox.join(', ')}]`} \n />\n </ListItem>\n {index < data.detections.length - 1 && <Divider />}\n </React.Fragment>\n ))}\n </List>\n ) : (\n <Typography variant=\"body1\">No objects detected</Typography>\n )}\n </Box>\n </Grid>\n </Grid>\n \n {renderPerformanceInfo()}\n \n {/* Vector DB Actions for Object Detection */}\n <VectorDBActions results={results} />\n </Paper>\n );\n }\n \n // Render for ViT (classification)\n if (model === 'vit') {\n return (\n <Paper className={classes.paper}>\n <Typography variant=\"h6\" gutterBottom>\n ViT Classification Results\n </Typography>\n \n <Typography variant=\"subtitle1\" gutterBottom>\n Top Predictions:\n </Typography>\n \n {data.top_predictions && data.top_predictions.length > 0 ? (\n <List>\n {data.top_predictions.map((prediction, index) => (\n <React.Fragment key={index}>\n <ListItem>\n <ListItemText \n primary={\n <Box style={{ display: 'flex', alignItems: 'center' }}>\n <Typography variant=\"body1\" component=\"span\">\n {prediction.rank}. {prediction.class}\n </Typography>\n <Chip \n label={`${(prediction.probability * 100).toFixed(1)}%`}\n size=\"small\"\n color={index === 0 ? \"primary\" : \"default\"}\n style={{ marginLeft: 8 }}\n />\n </Box>\n } \n />\n </ListItem>\n {index < data.top_predictions.length - 1 && <Divider />}\n </React.Fragment>\n ))}\n </List>\n ) : (\n <Typography variant=\"body1\">No classifications available</Typography>\n )}\n \n {renderPerformanceInfo()}\n \n {/* Vector DB Actions for ViT Classification */}\n <VectorDBActions results={results} />\n </Paper>\n );\n }\n \n return null;\n};\n\nexport default ResultDisplay;\n","import React, { useState } from 'react';\nimport { \n Paper, \n Typography, \n Box, \n TextField, \n Button, \n CircularProgress,\n Divider\n} from '@material-ui/core';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n paper: {\n padding: theme.spacing(2),\n marginTop: theme.spacing(2)\n },\n marginBottom: {\n marginBottom: theme.spacing(2)\n },\n dividerMargin: {\n margin: `${theme.spacing(2)}px 0`\n },\n responseBox: {\n padding: theme.spacing(2),\n backgroundColor: '#f5f5f5',\n borderRadius: theme.shape.borderRadius,\n marginTop: theme.spacing(2),\n whiteSpace: 'pre-wrap'\n },\n buttonProgress: {\n marginLeft: theme.spacing(1)\n }\n}));\n\nconst LlmAnalysis = ({ visionResults, model }) => {\n const classes = useStyles();\n const [userQuery, setUserQuery] = useState('');\n const [isAnalyzing, setIsAnalyzing] = useState(false);\n const [analysisResult, setAnalysisResult] = useState(null);\n const [error, setError] = useState(null);\n\n // Format time for display\n const formatTime = (ms) => {\n if (ms === undefined || ms === null || isNaN(ms)) return '-';\n const num = Number(ms);\n if (num < 1000) return `${num.toFixed(2)} ms`;\n return `${(num / 1000).toFixed(2)} s`;\n };\n\n const handleAnalyze = async () => {\n if (!userQuery.trim()) return;\n \n setIsAnalyzing(true);\n setError(null);\n \n try {\n const response = await fetch('/api/analyze', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n visionResults: visionResults,\n userQuery: userQuery\n }),\n });\n\n if (!response.ok) {\n throw new Error(`HTTP error! Status: ${response.status}`);\n }\n\n const data = await response.json();\n \n if (data.error) {\n setError(data.error);\n } else {\n setAnalysisResult(data);\n }\n } catch (err) {\n console.error('Error analyzing with LLM:', err);\n setError(`Error analyzing with LLM: ${err.message}`);\n } finally {\n setIsAnalyzing(false);\n }\n };\n\n if (!visionResults) return null;\n\n return (\n <Paper className={classes.paper}>\n <Typography variant=\"h6\" gutterBottom>\n Ask AI about the {model === 'vit' ? 'Classification' : 'Detection'} Results\n </Typography>\n \n <Typography variant=\"body2\" className={classes.marginBottom}>\n Ask a question about the detected objects or classifications to get an AI-powered analysis.\n </Typography>\n \n <TextField\n fullWidth\n label=\"Your question about the image\"\n variant=\"outlined\"\n value={userQuery}\n onChange={(e) => setUserQuery(e.target.value)}\n disabled={isAnalyzing}\n className={classes.marginBottom}\n placeholder={model === 'vit' \n ? \"E.g., What category does this image belong to?\" \n : \"E.g., How many people are in this image?\"}\n />\n \n <Button \n variant=\"contained\" \n color=\"primary\"\n onClick={handleAnalyze}\n disabled={isAnalyzing || !userQuery.trim()}\n >\n Analyze with AI\n {isAnalyzing && <CircularProgress size={24} className={classes.buttonProgress} />}\n </Button>\n \n {error && (\n <Box mt={2}>\n <Typography color=\"error\">{error}</Typography>\n </Box>\n )}\n \n {analysisResult && (\n <>\n <Divider className={classes.dividerMargin} />\n \n <Typography variant=\"subtitle1\" gutterBottom>\n AI Analysis:\n </Typography>\n \n <Box className={classes.responseBox}>\n <Typography variant=\"body1\">\n {analysisResult.response}\n </Typography>\n </Box>\n \n {analysisResult.performance && (\n <Box mt={1}>\n <Typography variant=\"body2\" color=\"textSecondary\">\n Analysis time: {formatTime(analysisResult.performance.inference_time)} on {analysisResult.performance.device}\n </Typography>\n </Box>\n )}\n </>\n )}\n </Paper>\n );\n};\n\nexport default LlmAnalysis;\n","import React, { useState, useEffect } from 'react';\nimport { \n Container, \n Typography, \n Box, \n Paper, \n Grid, \n CircularProgress,\n AppBar,\n Toolbar,\n ThemeProvider,\n createMuiTheme\n} from '@material-ui/core';\nimport ImageUploader from './components/ImageUploader';\nimport ModelSelector from './components/ModelSelector';\nimport ResultDisplay from './components/ResultDisplay';\nimport LlmAnalysis from './components/LlmAnalysis';\nimport './App.css';\n\n// Create a theme\nconst theme = createMuiTheme({\n palette: {\n primary: {\n main: '#3f51b5',\n },\n secondary: {\n main: '#f50057',\n },\n },\n typography: {\n fontFamily: 'Roboto, Arial, sans-serif',\n },\n});\n\nfunction App() {\n const [selectedImage, setSelectedImage] = useState(null);\n const [selectedModel, setSelectedModel] = useState('');\n const [isProcessing, setIsProcessing] = useState(false);\n const [results, setResults] = useState(null);\n const [error, setError] = useState(null);\n const [modelsStatus, setModelsStatus] = useState({\n yolo: false,\n detr: false,\n vit: false\n });\n\n // Check API status on component mount\n useEffect(() => {\n fetch('/api/status')\n .then(response => response.json())\n .then(data => {\n setModelsStatus(data.models);\n })\n .catch(err => {\n console.error('Error checking API status:', err);\n setError('Error connecting to the backend API. Please make sure the server is running.');\n });\n }, []);\n\n const handleImageUpload = (image) => {\n setSelectedImage(image);\n setResults(null);\n setError(null);\n };\n\n const handleModelSelect = (model) => {\n setSelectedModel(model);\n setResults(null);\n setError(null);\n };\n\n const processImage = async () => {\n if (!selectedImage || !selectedModel) {\n setError('Please select both an image and a model');\n return;\n }\n\n setIsProcessing(true);\n setError(null);\n\n // Create form data for the image\n const formData = new FormData();\n formData.append('image', selectedImage);\n\n let endpoint = '';\n switch (selectedModel) {\n case 'yolo':\n endpoint = '/api/detect/yolo';\n break;\n case 'detr':\n endpoint = '/api/detect/detr';\n break;\n case 'vit':\n endpoint = '/api/classify/vit';\n break;\n default:\n setError('Invalid model selection');\n setIsProcessing(false);\n return;\n }\n\n try {\n const response = await fetch(endpoint, {\n method: 'POST',\n body: formData,\n });\n\n if (!response.ok) {\n throw new Error(`HTTP error! Status: ${response.status}`);\n }\n\n const data = await response.json();\n setResults({ model: selectedModel, data });\n } catch (err) {\n console.error('Error processing image:', err);\n setError(`Error processing image: ${err.message}`);\n } finally {\n setIsProcessing(false);\n }\n };\n\n return (\n <ThemeProvider theme={theme}>\n <Box style={{ flexGrow: 1 }}>\n <AppBar position=\"static\">\n <Toolbar>\n <Typography variant=\"h6\" style={{ flexGrow: 1 }}>\n Multi-Model Object Detection Demo\n </Typography>\n </Toolbar>\n </AppBar>\n <Container maxWidth=\"lg\" style={{ marginTop: theme.spacing(4), marginBottom: theme.spacing(4) }}>\n <Grid container spacing={3}>\n <Grid item xs={12}>\n <Paper style={{ padding: theme.spacing(2) }}>\n <Typography variant=\"h5\" gutterBottom>\n Upload an image to see how each model performs!\n </Typography>\n <Typography variant=\"body1\" paragraph>\n This demo showcases three different object detection and image classification models:\n </Typography>\n <Typography variant=\"body1\" component=\"div\">\n <ul>\n <li><strong>YOLOv8</strong>: Fast and accurate object detection</li>\n <li><strong>DETR</strong>: DEtection TRansformer for object detection</li>\n <li><strong>ViT</strong>: Vision Transformer for image classification</li>\n </ul>\n </Typography>\n </Paper>\n </Grid>\n \n <Grid item xs={12} md={6}>\n <ImageUploader onImageUpload={handleImageUpload} />\n </Grid>\n \n <Grid item xs={12} md={6}>\n <ModelSelector \n onModelSelect={handleModelSelect} \n onProcess={processImage}\n isProcessing={isProcessing}\n modelsStatus={modelsStatus}\n selectedModel={selectedModel}\n imageSelected={!!selectedImage}\n />\n </Grid>\n \n {error && (\n <Grid item xs={12}>\n <Paper style={{ padding: theme.spacing(2), backgroundColor: '#ffebee' }}>\n <Typography color=\"error\">{error}</Typography>\n </Paper>\n </Grid>\n )}\n \n {isProcessing && (\n <Grid item xs={12} style={{ textAlign: 'center', margin: `${theme.spacing(4)}px 0` }}>\n <CircularProgress />\n <Typography variant=\"h6\" style={{ marginTop: theme.spacing(2) }}>\n Processing image...\n </Typography>\n </Grid>\n )}\n \n {results && (\n <>\n <Grid item xs={12}>\n <ResultDisplay results={results} />\n </Grid>\n <Grid item xs={12}>\n <LlmAnalysis visionResults={results.data} model={results.model} />\n </Grid>\n </>\n )}\n </Grid>\n </Container>\n </Box>\n </ThemeProvider>\n );\n}\n\nexport default App;\n","const reportWebVitals = (onPerfEntry) => {\n if (onPerfEntry && onPerfEntry instanceof Function) {\n import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {\n getCLS(onPerfEntry);\n getFID(onPerfEntry);\n getFCP(onPerfEntry);\n getLCP(onPerfEntry);\n getTTFB(onPerfEntry);\n });\n }\n};\n\nexport default reportWebVitals;\n","import React from 'react';\nimport ReactDOM from 'react-dom';\nimport './index.css';\nimport App from './App';\nimport reportWebVitals from './reportWebVitals';\n\nReactDOM.render(\n <React.StrictMode>\n <App />\n </React.StrictMode>,\n document.getElementById('root')\n);\n\n// If you want to start measuring performance in your app, pass a function\n// to log results (for example: reportWebVitals(console.log))\n// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals\nreportWebVitals();\n"],"sourceRoot":""}
 
 
frontend/build/static/js/{main.06c7c845.chunk.js → main.1756d180.chunk.js} RENAMED
@@ -1,2 +1,2 @@
1
- (this["webpackJsonpvision-web-app"]=this["webpackJsonpvision-web-app"]||[]).push([[0],{102:function(e,a,t){},103:function(e,a,t){"use strict";t.r(a);var n=t(0),r=t.n(n),l=t(11),o=t.n(l),c=(t(97),t(78)),i=t(155),s=t(159),m=t(156),d=t(157),g=t(48),u=t(158),p=t(138),E=t(81),b=t(143),f=t(136),h=t(137),y=t(63),v=t.n(y),x=t(75),C=t.n(x),S=t(133);const j=Object(S.a)(e=>({paper:{padding:e.spacing(2),display:"flex",flexDirection:"column",alignItems:"center",height:"100%",minHeight:300,transition:"all 0.3s ease"},dragActive:{border:"2px dashed #3f51b5",backgroundColor:"rgba(63, 81, 181, 0.05)"},dragInactive:{border:"2px dashed #ccc",backgroundColor:"white"},uploadBox:{display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",height:"100%",width:"100%",cursor:"pointer"},uploadIcon:{fontSize:60,color:"#3f51b5",marginBottom:e.spacing(2)},supportText:{marginTop:e.spacing(2)},previewBox:{display:"flex",flexDirection:"column",alignItems:"center",width:"100%",height:"100%",position:"relative"},imageContainer:{position:"relative",width:"100%",height:"100%",display:"flex",justifyContent:"center",alignItems:"center",overflow:"hidden",marginTop:e.spacing(2)},deleteButton:{position:"absolute",top:0,right:0,backgroundColor:"rgba(255, 255, 255, 0.7)","&:hover":{backgroundColor:"rgba(255, 255, 255, 0.9)"}}}));var w=e=>{let{onImageUpload:a}=e;const t=j(),[l,o]=Object(n.useState)(null),[c,i]=Object(n.useState)(!1),m=Object(n.useRef)(null),d=e=>{e.preventDefault(),e.stopPropagation(),"dragenter"===e.type||"dragover"===e.type?i(!0):"dragleave"===e.type&&i(!1)},u=e=>{e.type.startsWith("image/")?(o(URL.createObjectURL(e)),a(e)):alert("Please upload an image file")};return r.a.createElement(E.a,{className:"".concat(t.paper," ").concat(c?t.dragActive:t.dragInactive),onDragEnter:d,onDragLeave:d,onDragOver:d,onDrop:e=>{e.preventDefault(),e.stopPropagation(),i(!1),e.dataTransfer.files&&e.dataTransfer.files[0]&&u(e.dataTransfer.files[0])}},r.a.createElement("input",{ref:m,type:"file",accept:"image/*",onChange:e=>{e.preventDefault(),e.target.files&&e.target.files[0]&&u(e.target.files[0])},style:{display:"none"}}),l?r.a.createElement(s.a,{className:t.previewBox},r.a.createElement(g.a,{variant:"h6",gutterBottom:!0},"Preview"),r.a.createElement(s.a,{className:t.imageContainer},r.a.createElement("img",{src:l,alt:"Preview",className:"preview-image"}),r.a.createElement(h.a,{"aria-label":"delete",className:t.deleteButton,onClick:()=>{o(null),a(null),m.current.value=""}},r.a.createElement(C.a,null)))):r.a.createElement(s.a,{className:t.uploadBox,onClick:()=>{m.current.click()}},r.a.createElement(v.a,{className:t.uploadIcon}),r.a.createElement(g.a,{variant:"h6",gutterBottom:!0},"Drag & Drop an image here"),r.a.createElement(g.a,{variant:"body2",color:"textSecondary",gutterBottom:!0},"or"),r.a.createElement(f.a,{variant:"contained",color:"primary",component:"span",startIcon:r.a.createElement(v.a,null)},"Browse Files"),r.a.createElement(g.a,{variant:"body2",color:"textSecondary",className:t.supportText},"Supported formats: JPG, PNG, GIF")))},N=t(139),B=t(140),T=t(166),O=t(141),k=t(64),D=t.n(k),I=t(76),P=t.n(I),F=t(77),R=t.n(F);const A=Object(S.a)(e=>({card:{height:"100%",display:"flex",flexDirection:"column"},selectedCard:{border:"2px solid #3f51b5"},unavailableCard:{opacity:.6},cardContent:{flexGrow:1},chipContainer:{marginBottom:e.spacing(1.5)},successChip:{backgroundColor:"#34C759",color:"#fff"},errorChip:{backgroundColor:"#FF3B3F",color:"#fff"},modelType:{marginTop:e.spacing(1)},processButton:{marginTop:e.spacing(3),textAlign:"center"}}));var L=e=>{let{onModelSelect:a,onProcess:t,isProcessing:n,modelsStatus:l,selectedModel:o,imageSelected:c}=e;const i=A(),m=[{id:"yolo",name:"YOLOv8",description:"Fast and accurate object detection",icon:r.a.createElement(D.a,null),available:l.yolo},{id:"detr",name:"DETR",description:"DEtection TRansformer for object detection",icon:r.a.createElement(D.a,null),available:l.detr},{id:"vit",name:"ViT",description:"Vision Transformer for image classification",icon:r.a.createElement(P.a,null),available:l.vit}],d=e=>{m.find(a=>a.id===e).available&&a(e)};return r.a.createElement(s.a,{sx:{p:2,height:"100%"}},r.a.createElement(g.a,{variant:"h6",gutterBottom:!0},"Select Model"),r.a.createElement(p.a,{container:!0,spacing:2},m.map(e=>r.a.createElement(p.a,{item:!0,xs:12,sm:4,key:e.id},r.a.createElement(N.a,{className:"\n ".concat(i.card," \n ").concat(o===e.id?i.selectedCard:""," \n ").concat(e.available?"":i.unavailableCard,"\n "),onClick:()=>d(e.id)},r.a.createElement(B.a,{className:i.cardContent},r.a.createElement(s.a,{sx:{mb:2,color:"primary"}},e.icon),r.a.createElement(g.a,{variant:"h5",component:"div",gutterBottom:!0},e.name),r.a.createElement("div",{className:i.chipContainer},e.available?r.a.createElement(T.a,{label:"Available",className:i.successChip,size:"small"}):r.a.createElement(T.a,{label:"Not Available",className:i.errorChip,size:"small"})),r.a.createElement(g.a,{variant:"body2",color:"textSecondary"},e.description)),r.a.createElement(O.a,null,r.a.createElement(f.a,{size:"small",onClick:()=>d(e.id),disabled:!e.available,color:o===e.id?"primary":"default",variant:o===e.id?"contained":"outlined",fullWidth:!0},o===e.id?"Selected":"Select")))))),r.a.createElement("div",{className:i.processButton},r.a.createElement(f.a,{variant:"contained",color:"primary",size:"large",startIcon:r.a.createElement(R.a,null),onClick:t,disabled:!o||!c||n},n?"Processing...":"Process Image")))},M=t(153),z=t(150),W=t(105),_=t(154),H=t(142),V=t(164),J=t(163),G=t(145),U=t(146),Y=t(147),q=t(167),Q=t(160),K=t(151),X=t(169),Z=t(152),$=t(161);const ee=Object(S.a)(e=>({root:{marginTop:e.spacing(2),marginBottom:e.spacing(2),padding:e.spacing(2),backgroundColor:"#f5f5f5",borderRadius:e.shape.borderRadius},button:{marginRight:e.spacing(2)},searchDialog:{minWidth:"500px"},formControl:{marginBottom:e.spacing(2),minWidth:"100%"},searchResults:{marginTop:e.spacing(2)},resultCard:{marginBottom:e.spacing(2)},resultImage:{height:140,objectFit:"contain"},chip:{margin:e.spacing(.5)},similarityChip:{backgroundColor:e.palette.primary.main,color:"white"}}));var ae=e=>{let{results:a}=e;const t=ee(),[l,o]=Object(n.useState)(!1),[c,i]=Object(n.useState)(!1),[m,d]=Object(n.useState)(null),[u,E]=Object(n.useState)(!1),[h,y]=Object(n.useState)("image"),[v,x]=Object(n.useState)(""),[C,S]=Object(n.useState)([]),[j,w]=Object(n.useState)(!1),[O,k]=Object(n.useState)(null),{model:D,data:I}=a,P=()=>{E(!1)},F=()=>"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,(function(e){const a=16*Math.random()|0;return("x"===e?a:3&a|8).toString(16)}));return r.a.createElement(s.a,{className:t.root},r.a.createElement(g.a,{variant:"h6",gutterBottom:!0},"Vector Database Actions"),r.a.createElement(s.a,{display:"flex",alignItems:"center",mb:2},r.a.createElement(f.a,{variant:"contained",color:"primary",onClick:async()=>{o(!0),d(null);try{let e;if(e="vit"===D?await fetch("/api/add-image",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({image:I.image,metadata:{model:"vit",classifications:I.classifications}})}):await fetch("/api/add-detected-objects",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({image:I.image,objects:I.objects,image_id:F()})}),!e.ok)throw new Error("HTTP error! Status: ".concat(e.status));const a=await e.json();if(a.error)throw new Error(a.error);i(!0),setTimeout(()=>i(!1),5e3)}catch(e){console.error("Error saving to vector DB:",e),d("Error saving to vector DB: ".concat(e.message))}finally{o(!1)}},disabled:l,className:t.button},l?r.a.createElement(r.a.Fragment,null,r.a.createElement(b.a,{size:20,color:"inherit",style:{marginRight:8}}),"Saving..."):"Save to Vector DB"),r.a.createElement(f.a,{variant:"outlined",color:"primary",onClick:()=>{E(!0),S([]),k(null)},className:t.button},"Search Similar")),m&&r.a.createElement($.a,{severity:"error",style:{marginTop:8}},m),r.a.createElement(V.a,{open:c,autoHideDuration:5e3,onClose:()=>i(!1)},r.a.createElement($.a,{severity:"success"},"vit"===D?"Image and classifications successfully saved to vector DB!":"Detected objects successfully saved to vector DB!")),r.a.createElement(J.a,{open:u,onClose:P,maxWidth:"md",fullWidth:!0},r.a.createElement(G.a,null,"Search Vector Database"),r.a.createElement(U.a,null,r.a.createElement(Y.a,{className:t.formControl},r.a.createElement(q.a,{id:"search-type-label"},"Search Type"),r.a.createElement(Q.a,{labelId:"search-type-label",id:"search-type",value:h,onChange:e=>{y(e.target.value),S([]),k(null)}},r.a.createElement(K.a,{value:"image"},"Search by Current Image"),r.a.createElement(K.a,{value:"class"},"Search by Class Name"))),"class"===h&&r.a.createElement(Y.a,{className:t.formControl},r.a.createElement(X.a,{label:"Class Name",value:v,onChange:e=>{x(e.target.value)},placeholder:"e.g. person, car, dog...",fullWidth:!0})),O&&r.a.createElement($.a,{severity:"error",style:{marginBottom:16}},O),r.a.createElement(s.a,{className:t.searchResults},j?r.a.createElement(s.a,{display:"flex",justifyContent:"center",alignItems:"center",p:4},r.a.createElement(b.a,null),r.a.createElement(g.a,{variant:"body1",style:{marginLeft:16}},"Searching...")):C.length>0&&(0===C.length?r.a.createElement(g.a,{variant:"body1"},"No results found."):r.a.createElement(p.a,{container:!0,spacing:2},C.map((e,a)=>{const n=100*(1-e.distance);return r.a.createElement(p.a,{item:!0,xs:12,sm:6,key:a},r.a.createElement(N.a,{className:t.resultCard},r.a.createElement(H.a,{className:t.resultImage,image:"data:image/jpeg;base64,".concat(e.image),title:"Result ".concat(a+1)}),r.a.createElement(B.a,null,r.a.createElement(s.a,{display:"flex",justifyContent:"space-between",alignItems:"center",mb:1},r.a.createElement(g.a,{variant:"subtitle1"},"Result #",a+1),r.a.createElement(T.a,{label:"Similarity: ".concat(n.toFixed(2),"%"),className:t.similarityChip,size:"small"})),r.a.createElement(g.a,{variant:"body2",color:"textSecondary"},r.a.createElement("strong",null,"Class:")," ",e.metadata.class||"N/A"),e.metadata.confidence&&r.a.createElement(g.a,{variant:"body2",color:"textSecondary"},r.a.createElement("strong",null,"Confidence:")," ",(100*e.metadata.confidence).toFixed(2),"%"),r.a.createElement(g.a,{variant:"body2",color:"textSecondary"},r.a.createElement("strong",null,"Object ID:")," ",e.id))))}))))),r.a.createElement(Z.a,null,r.a.createElement(f.a,{onClick:P,color:"default"},"Close"),r.a.createElement(f.a,{onClick:async()=>{w(!0),k(null);try{let e={};if("image"===h)e={image:I.image,n_results:5};else{if(!v.trim())throw new Error("Please enter a class name");e={class_name:v.trim(),n_results:5}}const a=await fetch("/api/search-similar-objects",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!a.ok)throw new Error("HTTP error! Status: ".concat(a.status));const t=await a.json();if(t.error)throw new Error(t.error);S(t)}catch(e){console.error("Error searching vector DB:",e),k("Error searching vector DB: ".concat(e.message))}finally{w(!1)}},color:"primary",variant:"contained",disabled:j||"class"===h&&!v.trim()},"Search"))))};const te=Object(S.a)(e=>({paper:{padding:e.spacing(2)},marginBottom:{marginBottom:e.spacing(2)},resultImage:{maxWidth:"100%",maxHeight:"400px",objectFit:"contain"},dividerMargin:{margin:"".concat(e.spacing(2),"px 0")},chipContainer:{display:"flex",gap:e.spacing(1),flexWrap:"wrap"}}));var ne=e=>{let{results:a}=e;const t=te();if(!a)return null;const{model:n,data:l}=a;if(l.error)return r.a.createElement(E.a,{sx:{p:2,bgcolor:"#ffebee"}},r.a.createElement(g.a,{color:"error"},l.error));const o=()=>l.performance?r.a.createElement(s.a,{className:"performance-info"},r.a.createElement(M.a,{className:t.dividerMargin}),r.a.createElement(g.a,{variant:"body2"},"Inference time: ",(e=>{if(void 0===e||null===e||isNaN(e))return"-";const a=Number(e);return a<1e3?"".concat(a.toFixed(2)," ms"):"".concat((a/1e3).toFixed(2)," s")})(l.performance.inference_time)," on ",l.performance.device)):null;return"yolo"===n||"detr"===n?r.a.createElement(E.a,{className:t.paper},r.a.createElement(g.a,{variant:"h6",gutterBottom:!0},"yolo"===n?"YOLOv8":"DETR"," Detection Results"),r.a.createElement(p.a,{container:!0,spacing:3},r.a.createElement(p.a,{item:!0,xs:12,md:6},l.image&&r.a.createElement(s.a,{className:t.marginBottom},r.a.createElement(g.a,{variant:"subtitle1",gutterBottom:!0},"Detection Result"),r.a.createElement("img",{src:"data:image/png;base64,".concat(l.image),alt:"Detection Result",className:t.resultImage}))),r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(s.a,{className:t.marginBottom},r.a.createElement(g.a,{variant:"subtitle1",gutterBottom:!0},"Detected Objects:"),l.detections&&l.detections.length>0?r.a.createElement(z.a,null,l.detections.map((e,a)=>r.a.createElement(r.a.Fragment,{key:a},r.a.createElement(W.a,null,r.a.createElement(_.a,{primary:r.a.createElement(s.a,{style:{display:"flex",alignItems:"center"}},r.a.createElement(g.a,{variant:"body1",component:"span"},e.class),r.a.createElement(T.a,{label:"".concat((100*e.confidence).toFixed(0),"%"),size:"small",color:"primary",style:{marginLeft:8}})),secondary:"Bounding Box: [".concat(e.bbox.join(", "),"]")})),a<l.detections.length-1&&r.a.createElement(M.a,null)))):r.a.createElement(g.a,{variant:"body1"},"No objects detected")))),o(),r.a.createElement(ae,{results:a})):"vit"===n?r.a.createElement(E.a,{className:t.paper},r.a.createElement(g.a,{variant:"h6",gutterBottom:!0},"ViT Classification Results"),r.a.createElement(g.a,{variant:"subtitle1",gutterBottom:!0},"Top Predictions:"),l.top_predictions&&l.top_predictions.length>0?r.a.createElement(z.a,null,l.top_predictions.map((e,a)=>r.a.createElement(r.a.Fragment,{key:a},r.a.createElement(W.a,null,r.a.createElement(_.a,{primary:r.a.createElement(s.a,{style:{display:"flex",alignItems:"center"}},r.a.createElement(g.a,{variant:"body1",component:"span"},e.rank,". ",e.class),r.a.createElement(T.a,{label:"".concat((100*e.probability).toFixed(1),"%"),size:"small",color:0===a?"primary":"default",style:{marginLeft:8}}))})),a<l.top_predictions.length-1&&r.a.createElement(M.a,null)))):r.a.createElement(g.a,{variant:"body1"},"No classifications available"),o(),r.a.createElement(ae,{results:a})):null};const re=Object(S.a)(e=>({paper:{padding:e.spacing(2),marginTop:e.spacing(2)},marginBottom:{marginBottom:e.spacing(2)},dividerMargin:{margin:"".concat(e.spacing(2),"px 0")},responseBox:{padding:e.spacing(2),backgroundColor:"#f5f5f5",borderRadius:e.shape.borderRadius,marginTop:e.spacing(2),whiteSpace:"pre-wrap"},buttonProgress:{marginLeft:e.spacing(1)}}));var le=e=>{let{visionResults:a,model:t}=e;const l=re(),[o,c]=Object(n.useState)(""),[i,m]=Object(n.useState)(!1),[d,u]=Object(n.useState)(null),[p,h]=Object(n.useState)(null);return a?r.a.createElement(E.a,{className:l.paper},r.a.createElement(g.a,{variant:"h6",gutterBottom:!0},"Ask AI about the ","vit"===t?"Classification":"Detection"," Results"),r.a.createElement(g.a,{variant:"body2",className:l.marginBottom},"Ask a question about the detected objects or classifications to get an AI-powered analysis."),r.a.createElement(X.a,{fullWidth:!0,label:"Your question about the image",variant:"outlined",value:o,onChange:e=>c(e.target.value),disabled:i,className:l.marginBottom,placeholder:"vit"===t?"E.g., What category does this image belong to?":"E.g., How many people are in this image?"}),r.a.createElement(f.a,{variant:"contained",color:"primary",onClick:async()=>{if(o.trim()){m(!0),h(null);try{const e=await fetch("/api/analyze",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({visionResults:a,userQuery:o})});if(!e.ok)throw new Error("HTTP error! Status: ".concat(e.status));const t=await e.json();t.error?h(t.error):u(t)}catch(e){console.error("Error analyzing with LLM:",e),h("Error analyzing with LLM: ".concat(e.message))}finally{m(!1)}}},disabled:i||!o.trim()},"Analyze with AI",i&&r.a.createElement(b.a,{size:24,className:l.buttonProgress})),p&&r.a.createElement(s.a,{mt:2},r.a.createElement(g.a,{color:"error"},p)),d&&r.a.createElement(r.a.Fragment,null,r.a.createElement(M.a,{className:l.dividerMargin}),r.a.createElement(g.a,{variant:"subtitle1",gutterBottom:!0},"AI Analysis:"),r.a.createElement(s.a,{className:l.responseBox},r.a.createElement(g.a,{variant:"body1"},d.response)),d.performance&&r.a.createElement(s.a,{mt:1},r.a.createElement(g.a,{variant:"body2",color:"textSecondary"},"Analysis time: ",(e=>{if(void 0===e||null===e||isNaN(e))return"-";const a=Number(e);return a<1e3?"".concat(a.toFixed(2)," ms"):"".concat((a/1e3).toFixed(2)," s")})(d.performance.inference_time)," on ",d.performance.device)))):null};t(102);const oe=Object(c.a)({palette:{primary:{main:"#3f51b5"},secondary:{main:"#f50057"}},typography:{fontFamily:"Roboto, Arial, sans-serif"}});var ce=function(){const[e,a]=Object(n.useState)(null),[t,l]=Object(n.useState)(""),[o,c]=Object(n.useState)(!1),[f,h]=Object(n.useState)(null),[y,v]=Object(n.useState)(null),[x,C]=Object(n.useState)({yolo:!1,detr:!1,vit:!1});return Object(n.useEffect)(()=>{fetch("/api/status").then(e=>e.json()).then(e=>{C(e.models)}).catch(e=>{console.error("Error checking API status:",e),v("Error connecting to the backend API. Please make sure the server is running.")})},[]),r.a.createElement(i.a,{theme:oe},r.a.createElement(s.a,{style:{flexGrow:1}},r.a.createElement(m.a,{position:"static"},r.a.createElement(d.a,null,r.a.createElement(g.a,{variant:"h6",style:{flexGrow:1}},"Multi-Model Object Detection Demo"))),r.a.createElement(u.a,{maxWidth:"lg",style:{marginTop:oe.spacing(4),marginBottom:oe.spacing(4)}},r.a.createElement(p.a,{container:!0,spacing:3},r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(E.a,{style:{padding:oe.spacing(2)}},r.a.createElement(g.a,{variant:"h5",gutterBottom:!0},"Upload an image to see how each model performs!"),r.a.createElement(g.a,{variant:"body1",paragraph:!0},"This demo showcases three different object detection and image classification models:"),r.a.createElement(g.a,{variant:"body1",component:"div"},r.a.createElement("ul",null,r.a.createElement("li",null,r.a.createElement("strong",null,"YOLOv8"),": Fast and accurate object detection"),r.a.createElement("li",null,r.a.createElement("strong",null,"DETR"),": DEtection TRansformer for object detection"),r.a.createElement("li",null,r.a.createElement("strong",null,"ViT"),": Vision Transformer for image classification"))))),r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(w,{onImageUpload:e=>{a(e),h(null),v(null)}})),r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(L,{onModelSelect:e=>{l(e),h(null),v(null)},onProcess:async()=>{if(!e||!t)return void v("Please select both an image and a model");c(!0),v(null);const a=new FormData;a.append("image",e);let n="";switch(t){case"yolo":n="/api/detect/yolo";break;case"detr":n="/api/detect/detr";break;case"vit":n="/api/classify/vit";break;default:return v("Invalid model selection"),void c(!1)}try{const e=await fetch(n,{method:"POST",body:a});if(!e.ok)throw new Error("HTTP error! Status: ".concat(e.status));const r=await e.json();h({model:t,data:r})}catch(r){console.error("Error processing image:",r),v("Error processing image: ".concat(r.message))}finally{c(!1)}},isProcessing:o,modelsStatus:x,selectedModel:t,imageSelected:!!e})),y&&r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(E.a,{style:{padding:oe.spacing(2),backgroundColor:"#ffebee"}},r.a.createElement(g.a,{color:"error"},y))),o&&r.a.createElement(p.a,{item:!0,xs:12,style:{textAlign:"center",margin:"".concat(oe.spacing(4),"px 0")}},r.a.createElement(b.a,null),r.a.createElement(g.a,{variant:"h6",style:{marginTop:oe.spacing(2)}},"Processing image...")),f&&r.a.createElement(r.a.Fragment,null,r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(ne,{results:f})),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(le,{visionResults:f.data,model:f.model})))))))};var ie=e=>{e&&e instanceof Function&&t.e(3).then(t.bind(null,170)).then(a=>{let{getCLS:t,getFID:n,getFCP:r,getLCP:l,getTTFB:o}=a;t(e),n(e),r(e),l(e),o(e)})};o.a.render(r.a.createElement(r.a.StrictMode,null,r.a.createElement(ce,null)),document.getElementById("root")),ie()},92:function(e,a,t){e.exports=t(103)},97:function(e,a,t){}},[[92,1,2]]]);
2
- //# sourceMappingURL=main.06c7c845.chunk.js.map
 
1
+ (this["webpackJsonpvision-web-app"]=this["webpackJsonpvision-web-app"]||[]).push([[0],{102:function(e,a,t){},103:function(e,a,t){"use strict";t.r(a);var n=t(0),r=t.n(n),l=t(11),o=t.n(l),c=(t(97),t(78)),i=t(155),s=t(159),m=t(156),d=t(157),g=t(48),u=t(158),p=t(138),E=t(81),b=t(143),f=t(136),h=t(137),y=t(63),v=t.n(y),x=t(75),C=t.n(x),S=t(133);const j=Object(S.a)(e=>({paper:{padding:e.spacing(2),display:"flex",flexDirection:"column",alignItems:"center",height:"100%",minHeight:300,transition:"all 0.3s ease"},dragActive:{border:"2px dashed #3f51b5",backgroundColor:"rgba(63, 81, 181, 0.05)"},dragInactive:{border:"2px dashed #ccc",backgroundColor:"white"},uploadBox:{display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",height:"100%",width:"100%",cursor:"pointer"},uploadIcon:{fontSize:60,color:"#3f51b5",marginBottom:e.spacing(2)},supportText:{marginTop:e.spacing(2)},previewBox:{display:"flex",flexDirection:"column",alignItems:"center",width:"100%",height:"100%",position:"relative"},imageContainer:{position:"relative",width:"100%",height:"100%",display:"flex",justifyContent:"center",alignItems:"center",overflow:"hidden",marginTop:e.spacing(2)},deleteButton:{position:"absolute",top:0,right:0,backgroundColor:"rgba(255, 255, 255, 0.7)","&:hover":{backgroundColor:"rgba(255, 255, 255, 0.9)"}}}));var w=e=>{let{onImageUpload:a}=e;const t=j(),[l,o]=Object(n.useState)(null),[c,i]=Object(n.useState)(!1),m=Object(n.useRef)(null),d=e=>{e.preventDefault(),e.stopPropagation(),"dragenter"===e.type||"dragover"===e.type?i(!0):"dragleave"===e.type&&i(!1)},u=e=>{e.type.startsWith("image/")?(o(URL.createObjectURL(e)),a(e)):alert("Please upload an image file")};return r.a.createElement(E.a,{className:"".concat(t.paper," ").concat(c?t.dragActive:t.dragInactive),onDragEnter:d,onDragLeave:d,onDragOver:d,onDrop:e=>{e.preventDefault(),e.stopPropagation(),i(!1),e.dataTransfer.files&&e.dataTransfer.files[0]&&u(e.dataTransfer.files[0])}},r.a.createElement("input",{ref:m,type:"file",accept:"image/*",onChange:e=>{e.preventDefault(),e.target.files&&e.target.files[0]&&u(e.target.files[0])},style:{display:"none"}}),l?r.a.createElement(s.a,{className:t.previewBox},r.a.createElement(g.a,{variant:"h6",gutterBottom:!0},"Preview"),r.a.createElement(s.a,{className:t.imageContainer},r.a.createElement("img",{src:l,alt:"Preview",className:"preview-image"}),r.a.createElement(h.a,{"aria-label":"delete",className:t.deleteButton,onClick:()=>{o(null),a(null),m.current.value=""}},r.a.createElement(C.a,null)))):r.a.createElement(s.a,{className:t.uploadBox,onClick:()=>{m.current.click()}},r.a.createElement(v.a,{className:t.uploadIcon}),r.a.createElement(g.a,{variant:"h6",gutterBottom:!0},"Drag & Drop an image here"),r.a.createElement(g.a,{variant:"body2",color:"textSecondary",gutterBottom:!0},"or"),r.a.createElement(f.a,{variant:"contained",color:"primary",component:"span",startIcon:r.a.createElement(v.a,null)},"Browse Files"),r.a.createElement(g.a,{variant:"body2",color:"textSecondary",className:t.supportText},"Supported formats: JPG, PNG, GIF")))},N=t(139),B=t(140),T=t(166),O=t(141),k=t(64),D=t.n(k),I=t(76),P=t.n(I),F=t(77),R=t.n(F);const A=Object(S.a)(e=>({card:{height:"100%",display:"flex",flexDirection:"column"},selectedCard:{border:"2px solid #3f51b5"},unavailableCard:{opacity:.6},cardContent:{flexGrow:1},chipContainer:{marginBottom:e.spacing(1.5)},successChip:{backgroundColor:"#34C759",color:"#fff"},errorChip:{backgroundColor:"#FF3B3F",color:"#fff"},modelType:{marginTop:e.spacing(1)},processButton:{marginTop:e.spacing(3),textAlign:"center"}}));var L=e=>{let{onModelSelect:a,onProcess:t,isProcessing:n,modelsStatus:l,selectedModel:o,imageSelected:c}=e;const i=A(),m=[{id:"yolo",name:"YOLOv8",description:"Fast and accurate object detection",icon:r.a.createElement(D.a,null),available:l.yolo},{id:"detr",name:"DETR",description:"DEtection TRansformer for object detection",icon:r.a.createElement(D.a,null),available:l.detr},{id:"vit",name:"ViT",description:"Vision Transformer for image classification",icon:r.a.createElement(P.a,null),available:l.vit}],d=e=>{m.find(a=>a.id===e).available&&a(e)};return r.a.createElement(s.a,{sx:{p:2,height:"100%"}},r.a.createElement(g.a,{variant:"h6",gutterBottom:!0},"Select Model"),r.a.createElement(p.a,{container:!0,spacing:2},m.map(e=>r.a.createElement(p.a,{item:!0,xs:12,sm:4,key:e.id},r.a.createElement(N.a,{className:"\n ".concat(i.card," \n ").concat(o===e.id?i.selectedCard:""," \n ").concat(e.available?"":i.unavailableCard,"\n "),onClick:()=>d(e.id)},r.a.createElement(B.a,{className:i.cardContent},r.a.createElement(s.a,{sx:{mb:2,color:"primary"}},e.icon),r.a.createElement(g.a,{variant:"h5",component:"div",gutterBottom:!0},e.name),r.a.createElement("div",{className:i.chipContainer},e.available?r.a.createElement(T.a,{label:"Available",className:i.successChip,size:"small"}):r.a.createElement(T.a,{label:"Not Available",className:i.errorChip,size:"small"})),r.a.createElement(g.a,{variant:"body2",color:"textSecondary"},e.description)),r.a.createElement(O.a,null,r.a.createElement(f.a,{size:"small",onClick:()=>d(e.id),disabled:!e.available,color:o===e.id?"primary":"default",variant:o===e.id?"contained":"outlined",fullWidth:!0},o===e.id?"Selected":"Select")))))),r.a.createElement("div",{className:i.processButton},r.a.createElement(f.a,{variant:"contained",color:"primary",size:"large",startIcon:r.a.createElement(R.a,null),onClick:t,disabled:!o||!c||n},n?"Processing...":"Process Image")))},M=t(153),z=t(150),W=t(105),_=t(154),H=t(142),V=t(164),J=t(163),G=t(145),U=t(146),Y=t(147),q=t(167),Q=t(160),K=t(151),X=t(169),Z=t(152),$=t(161);const ee=Object(S.a)(e=>({root:{marginTop:e.spacing(2),marginBottom:e.spacing(2),padding:e.spacing(2),backgroundColor:"#f5f5f5",borderRadius:e.shape.borderRadius},button:{marginRight:e.spacing(2)},searchDialog:{minWidth:"500px"},formControl:{marginBottom:e.spacing(2),minWidth:"100%"},searchResults:{marginTop:e.spacing(2)},resultCard:{marginBottom:e.spacing(2)},resultImage:{height:140,objectFit:"contain"},chip:{margin:e.spacing(.5)},similarityChip:{backgroundColor:e.palette.primary.main,color:"white"}}));var ae=e=>{let{results:a}=e;const t=ee(),[l,o]=Object(n.useState)(!1),[c,i]=Object(n.useState)(!1),[m,d]=Object(n.useState)(null),[u,E]=Object(n.useState)(!1),[h,y]=Object(n.useState)("image"),[v,x]=Object(n.useState)(""),[C,S]=Object(n.useState)([]),[j,w]=Object(n.useState)(!1),[O,k]=Object(n.useState)(null),{model:D,data:I}=a,P=()=>{E(!1)},F=()=>"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,(function(e){const a=16*Math.random()|0;return("x"===e?a:3&a|8).toString(16)}));return r.a.createElement(s.a,{className:t.root},r.a.createElement(g.a,{variant:"h6",gutterBottom:!0},"Vector Database Actions"),r.a.createElement(s.a,{display:"flex",alignItems:"center",mb:2},r.a.createElement(f.a,{variant:"contained",color:"primary",onClick:async()=>{o(!0),d(null);try{let e;if(e="vit"===D?await fetch("/api/add-image",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({image:I.image,metadata:{model:"vit",classifications:I.classifications}})}):await fetch("/api/add-detected-objects",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({image:I.image,objects:I.detections,image_id:F()})}),!e.ok)throw new Error("HTTP error! Status: ".concat(e.status));const a=await e.json();if(a.error)throw new Error(a.error);i(!0),setTimeout(()=>i(!1),5e3)}catch(e){console.error("Error saving to vector DB:",e),d("Error saving to vector DB: ".concat(e.message))}finally{o(!1)}},disabled:l,className:t.button},l?r.a.createElement(r.a.Fragment,null,r.a.createElement(b.a,{size:20,color:"inherit",style:{marginRight:8}}),"Saving..."):"Save to Vector DB"),r.a.createElement(f.a,{variant:"outlined",color:"primary",onClick:()=>{E(!0),S([]),k(null)},className:t.button},"Search Similar")),m&&r.a.createElement($.a,{severity:"error",style:{marginTop:8}},m),r.a.createElement(V.a,{open:c,autoHideDuration:5e3,onClose:()=>i(!1)},r.a.createElement($.a,{severity:"success"},"vit"===D?"Image and classifications successfully saved to vector DB!":"Detected objects successfully saved to vector DB!")),r.a.createElement(J.a,{open:u,onClose:P,maxWidth:"md",fullWidth:!0},r.a.createElement(G.a,null,"Search Vector Database"),r.a.createElement(U.a,null,r.a.createElement(Y.a,{className:t.formControl},r.a.createElement(q.a,{id:"search-type-label"},"Search Type"),r.a.createElement(Q.a,{labelId:"search-type-label",id:"search-type",value:h,onChange:e=>{y(e.target.value),S([]),k(null)}},r.a.createElement(K.a,{value:"image"},"Search by Current Image"),r.a.createElement(K.a,{value:"class"},"Search by Class Name"))),"class"===h&&r.a.createElement(Y.a,{className:t.formControl},r.a.createElement(X.a,{label:"Class Name",value:v,onChange:e=>{x(e.target.value)},placeholder:"e.g. person, car, dog...",fullWidth:!0})),O&&r.a.createElement($.a,{severity:"error",style:{marginBottom:16}},O),r.a.createElement(s.a,{className:t.searchResults},j?r.a.createElement(s.a,{display:"flex",justifyContent:"center",alignItems:"center",p:4},r.a.createElement(b.a,null),r.a.createElement(g.a,{variant:"body1",style:{marginLeft:16}},"Searching...")):C.length>0&&(0===C.length?r.a.createElement(g.a,{variant:"body1"},"No results found."):r.a.createElement(p.a,{container:!0,spacing:2},C.map((e,a)=>{const n=100*(1-e.distance);return r.a.createElement(p.a,{item:!0,xs:12,sm:6,key:a},r.a.createElement(N.a,{className:t.resultCard},r.a.createElement(H.a,{className:t.resultImage,image:"data:image/jpeg;base64,".concat(e.image),title:"Result ".concat(a+1)}),r.a.createElement(B.a,null,r.a.createElement(s.a,{display:"flex",justifyContent:"space-between",alignItems:"center",mb:1},r.a.createElement(g.a,{variant:"subtitle1"},"Result #",a+1),r.a.createElement(T.a,{label:"Similarity: ".concat(n.toFixed(2),"%"),className:t.similarityChip,size:"small"})),r.a.createElement(g.a,{variant:"body2",color:"textSecondary"},r.a.createElement("strong",null,"Class:")," ",e.metadata.class||"N/A"),e.metadata.confidence&&r.a.createElement(g.a,{variant:"body2",color:"textSecondary"},r.a.createElement("strong",null,"Confidence:")," ",(100*e.metadata.confidence).toFixed(2),"%"),r.a.createElement(g.a,{variant:"body2",color:"textSecondary"},r.a.createElement("strong",null,"Object ID:")," ",e.id))))}))))),r.a.createElement(Z.a,null,r.a.createElement(f.a,{onClick:P,color:"default"},"Close"),r.a.createElement(f.a,{onClick:async()=>{w(!0),k(null);try{let e={};if("image"===h)e={image:I.image,n_results:5};else{if(!v.trim())throw new Error("Please enter a class name");e={class_name:v.trim(),n_results:5}}const a=await fetch("/api/search-similar-objects",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!a.ok)throw new Error("HTTP error! Status: ".concat(a.status));const t=await a.json();if(t.error)throw new Error(t.error);S(t)}catch(e){console.error("Error searching vector DB:",e),k("Error searching vector DB: ".concat(e.message))}finally{w(!1)}},color:"primary",variant:"contained",disabled:j||"class"===h&&!v.trim()},"Search"))))};const te=Object(S.a)(e=>({paper:{padding:e.spacing(2)},marginBottom:{marginBottom:e.spacing(2)},resultImage:{maxWidth:"100%",maxHeight:"400px",objectFit:"contain"},dividerMargin:{margin:"".concat(e.spacing(2),"px 0")},chipContainer:{display:"flex",gap:e.spacing(1),flexWrap:"wrap"}}));var ne=e=>{let{results:a}=e;const t=te();if(!a)return null;const{model:n,data:l}=a;if(l.error)return r.a.createElement(E.a,{sx:{p:2,bgcolor:"#ffebee"}},r.a.createElement(g.a,{color:"error"},l.error));const o=()=>l.performance?r.a.createElement(s.a,{className:"performance-info"},r.a.createElement(M.a,{className:t.dividerMargin}),r.a.createElement(g.a,{variant:"body2"},"Inference time: ",(e=>{if(void 0===e||null===e||isNaN(e))return"-";const a=Number(e);return a<1e3?"".concat(a.toFixed(2)," ms"):"".concat((a/1e3).toFixed(2)," s")})(l.performance.inference_time)," on ",l.performance.device)):null;return"yolo"===n||"detr"===n?r.a.createElement(E.a,{className:t.paper},r.a.createElement(g.a,{variant:"h6",gutterBottom:!0},"yolo"===n?"YOLOv8":"DETR"," Detection Results"),r.a.createElement(p.a,{container:!0,spacing:3},r.a.createElement(p.a,{item:!0,xs:12,md:6},l.image&&r.a.createElement(s.a,{className:t.marginBottom},r.a.createElement(g.a,{variant:"subtitle1",gutterBottom:!0},"Detection Result"),r.a.createElement("img",{src:"data:image/png;base64,".concat(l.image),alt:"Detection Result",className:t.resultImage}))),r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(s.a,{className:t.marginBottom},r.a.createElement(g.a,{variant:"subtitle1",gutterBottom:!0},"Detected Objects:"),l.detections&&l.detections.length>0?r.a.createElement(z.a,null,l.detections.map((e,a)=>r.a.createElement(r.a.Fragment,{key:a},r.a.createElement(W.a,null,r.a.createElement(_.a,{primary:r.a.createElement(s.a,{style:{display:"flex",alignItems:"center"}},r.a.createElement(g.a,{variant:"body1",component:"span"},e.class),r.a.createElement(T.a,{label:"".concat((100*e.confidence).toFixed(0),"%"),size:"small",color:"primary",style:{marginLeft:8}})),secondary:"Bounding Box: [".concat(e.bbox.join(", "),"]")})),a<l.detections.length-1&&r.a.createElement(M.a,null)))):r.a.createElement(g.a,{variant:"body1"},"No objects detected")))),o(),r.a.createElement(ae,{results:a})):"vit"===n?r.a.createElement(E.a,{className:t.paper},r.a.createElement(g.a,{variant:"h6",gutterBottom:!0},"ViT Classification Results"),r.a.createElement(g.a,{variant:"subtitle1",gutterBottom:!0},"Top Predictions:"),l.top_predictions&&l.top_predictions.length>0?r.a.createElement(z.a,null,l.top_predictions.map((e,a)=>r.a.createElement(r.a.Fragment,{key:a},r.a.createElement(W.a,null,r.a.createElement(_.a,{primary:r.a.createElement(s.a,{style:{display:"flex",alignItems:"center"}},r.a.createElement(g.a,{variant:"body1",component:"span"},e.rank,". ",e.class),r.a.createElement(T.a,{label:"".concat((100*e.probability).toFixed(1),"%"),size:"small",color:0===a?"primary":"default",style:{marginLeft:8}}))})),a<l.top_predictions.length-1&&r.a.createElement(M.a,null)))):r.a.createElement(g.a,{variant:"body1"},"No classifications available"),o(),r.a.createElement(ae,{results:a})):null};const re=Object(S.a)(e=>({paper:{padding:e.spacing(2),marginTop:e.spacing(2)},marginBottom:{marginBottom:e.spacing(2)},dividerMargin:{margin:"".concat(e.spacing(2),"px 0")},responseBox:{padding:e.spacing(2),backgroundColor:"#f5f5f5",borderRadius:e.shape.borderRadius,marginTop:e.spacing(2),whiteSpace:"pre-wrap"},buttonProgress:{marginLeft:e.spacing(1)}}));var le=e=>{let{visionResults:a,model:t}=e;const l=re(),[o,c]=Object(n.useState)(""),[i,m]=Object(n.useState)(!1),[d,u]=Object(n.useState)(null),[p,h]=Object(n.useState)(null);return a?r.a.createElement(E.a,{className:l.paper},r.a.createElement(g.a,{variant:"h6",gutterBottom:!0},"Ask AI about the ","vit"===t?"Classification":"Detection"," Results"),r.a.createElement(g.a,{variant:"body2",className:l.marginBottom},"Ask a question about the detected objects or classifications to get an AI-powered analysis."),r.a.createElement(X.a,{fullWidth:!0,label:"Your question about the image",variant:"outlined",value:o,onChange:e=>c(e.target.value),disabled:i,className:l.marginBottom,placeholder:"vit"===t?"E.g., What category does this image belong to?":"E.g., How many people are in this image?"}),r.a.createElement(f.a,{variant:"contained",color:"primary",onClick:async()=>{if(o.trim()){m(!0),h(null);try{const e=await fetch("/api/analyze",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({visionResults:a,userQuery:o})});if(!e.ok)throw new Error("HTTP error! Status: ".concat(e.status));const t=await e.json();t.error?h(t.error):u(t)}catch(e){console.error("Error analyzing with LLM:",e),h("Error analyzing with LLM: ".concat(e.message))}finally{m(!1)}}},disabled:i||!o.trim()},"Analyze with AI",i&&r.a.createElement(b.a,{size:24,className:l.buttonProgress})),p&&r.a.createElement(s.a,{mt:2},r.a.createElement(g.a,{color:"error"},p)),d&&r.a.createElement(r.a.Fragment,null,r.a.createElement(M.a,{className:l.dividerMargin}),r.a.createElement(g.a,{variant:"subtitle1",gutterBottom:!0},"AI Analysis:"),r.a.createElement(s.a,{className:l.responseBox},r.a.createElement(g.a,{variant:"body1"},d.response)),d.performance&&r.a.createElement(s.a,{mt:1},r.a.createElement(g.a,{variant:"body2",color:"textSecondary"},"Analysis time: ",(e=>{if(void 0===e||null===e||isNaN(e))return"-";const a=Number(e);return a<1e3?"".concat(a.toFixed(2)," ms"):"".concat((a/1e3).toFixed(2)," s")})(d.performance.inference_time)," on ",d.performance.device)))):null};t(102);const oe=Object(c.a)({palette:{primary:{main:"#3f51b5"},secondary:{main:"#f50057"}},typography:{fontFamily:"Roboto, Arial, sans-serif"}});var ce=function(){const[e,a]=Object(n.useState)(null),[t,l]=Object(n.useState)(""),[o,c]=Object(n.useState)(!1),[f,h]=Object(n.useState)(null),[y,v]=Object(n.useState)(null),[x,C]=Object(n.useState)({yolo:!1,detr:!1,vit:!1});return Object(n.useEffect)(()=>{fetch("/api/status").then(e=>e.json()).then(e=>{C(e.models)}).catch(e=>{console.error("Error checking API status:",e),v("Error connecting to the backend API. Please make sure the server is running.")})},[]),r.a.createElement(i.a,{theme:oe},r.a.createElement(s.a,{style:{flexGrow:1}},r.a.createElement(m.a,{position:"static"},r.a.createElement(d.a,null,r.a.createElement(g.a,{variant:"h6",style:{flexGrow:1}},"Multi-Model Object Detection Demo"))),r.a.createElement(u.a,{maxWidth:"lg",style:{marginTop:oe.spacing(4),marginBottom:oe.spacing(4)}},r.a.createElement(p.a,{container:!0,spacing:3},r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(E.a,{style:{padding:oe.spacing(2)}},r.a.createElement(g.a,{variant:"h5",gutterBottom:!0},"Upload an image to see how each model performs!"),r.a.createElement(g.a,{variant:"body1",paragraph:!0},"This demo showcases three different object detection and image classification models:"),r.a.createElement(g.a,{variant:"body1",component:"div"},r.a.createElement("ul",null,r.a.createElement("li",null,r.a.createElement("strong",null,"YOLOv8"),": Fast and accurate object detection"),r.a.createElement("li",null,r.a.createElement("strong",null,"DETR"),": DEtection TRansformer for object detection"),r.a.createElement("li",null,r.a.createElement("strong",null,"ViT"),": Vision Transformer for image classification"))))),r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(w,{onImageUpload:e=>{a(e),h(null),v(null)}})),r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(L,{onModelSelect:e=>{l(e),h(null),v(null)},onProcess:async()=>{if(!e||!t)return void v("Please select both an image and a model");c(!0),v(null);const a=new FormData;a.append("image",e);let n="";switch(t){case"yolo":n="/api/detect/yolo";break;case"detr":n="/api/detect/detr";break;case"vit":n="/api/classify/vit";break;default:return v("Invalid model selection"),void c(!1)}try{const e=await fetch(n,{method:"POST",body:a});if(!e.ok)throw new Error("HTTP error! Status: ".concat(e.status));const r=await e.json();h({model:t,data:r})}catch(r){console.error("Error processing image:",r),v("Error processing image: ".concat(r.message))}finally{c(!1)}},isProcessing:o,modelsStatus:x,selectedModel:t,imageSelected:!!e})),y&&r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(E.a,{style:{padding:oe.spacing(2),backgroundColor:"#ffebee"}},r.a.createElement(g.a,{color:"error"},y))),o&&r.a.createElement(p.a,{item:!0,xs:12,style:{textAlign:"center",margin:"".concat(oe.spacing(4),"px 0")}},r.a.createElement(b.a,null),r.a.createElement(g.a,{variant:"h6",style:{marginTop:oe.spacing(2)}},"Processing image...")),f&&r.a.createElement(r.a.Fragment,null,r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(ne,{results:f})),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(le,{visionResults:f.data,model:f.model})))))))};var ie=e=>{e&&e instanceof Function&&t.e(3).then(t.bind(null,170)).then(a=>{let{getCLS:t,getFID:n,getFCP:r,getLCP:l,getTTFB:o}=a;t(e),n(e),r(e),l(e),o(e)})};o.a.render(r.a.createElement(r.a.StrictMode,null,r.a.createElement(ce,null)),document.getElementById("root")),ie()},92:function(e,a,t){e.exports=t(103)},97:function(e,a,t){}},[[92,1,2]]]);
2
+ //# sourceMappingURL=main.1756d180.chunk.js.map
frontend/build/static/js/main.1756d180.chunk.js.map ADDED
@@ -0,0 +1 @@
 
 
1
+ {"version":3,"sources":["components/ImageUploader.js","components/ModelSelector.js","components/VectorDBActions.js","components/ResultDisplay.js","components/LlmAnalysis.js","App.js","reportWebVitals.js","index.js"],"names":["useStyles","makeStyles","theme","paper","padding","spacing","display","flexDirection","alignItems","height","minHeight","transition","dragActive","border","backgroundColor","dragInactive","uploadBox","justifyContent","width","cursor","uploadIcon","fontSize","color","marginBottom","supportText","marginTop","previewBox","position","imageContainer","overflow","deleteButton","top","right","ImageUploader","_ref","onImageUpload","classes","previewUrl","setPreviewUrl","useState","setDragActive","fileInputRef","useRef","handleDrag","e","preventDefault","stopPropagation","type","handleFiles","file","startsWith","URL","createObjectURL","alert","React","createElement","Paper","className","concat","onDragEnter","onDragLeave","onDragOver","onDrop","dataTransfer","files","ref","accept","onChange","target","style","Box","Typography","variant","gutterBottom","src","alt","IconButton","aria-label","onClick","handleRemoveImage","current","value","DeleteIcon","onButtonClick","click","CloudUploadIcon","Button","component","startIcon","card","selectedCard","unavailableCard","opacity","cardContent","flexGrow","chipContainer","successChip","errorChip","modelType","processButton","textAlign","ModelSelector","onModelSelect","onProcess","isProcessing","modelsStatus","selectedModel","imageSelected","models","id","name","description","icon","VisibilityIcon","available","yolo","detr","CategoryIcon","vit","handleModelClick","modelId","find","m","sx","p","Grid","container","map","model","item","xs","sm","key","Card","CardContent","mb","Chip","label","size","CardActions","disabled","fullWidth","PlayArrowIcon","root","borderRadius","shape","button","marginRight","searchDialog","minWidth","formControl","searchResults","resultCard","resultImage","objectFit","chip","margin","similarityChip","palette","primary","main","VectorDBActions","results","isSaving","setIsSaving","saveSuccess","setSaveSuccess","saveError","setSaveError","openSearchDialog","setOpenSearchDialog","searchType","setSearchType","searchClass","setSearchClass","setSearchResults","isSearching","setIsSearching","searchError","setSearchError","data","handleCloseSearchDialog","generateUUID","replace","c","r","Math","random","toString","async","response","fetch","method","headers","body","JSON","stringify","image","metadata","classifications","objects","detections","image_id","ok","Error","status","result","json","error","setTimeout","err","console","message","Fragment","CircularProgress","handleOpenSearchDialog","Alert","severity","Snackbar","open","autoHideDuration","onClose","Dialog","maxWidth","DialogTitle","DialogContent","FormControl","InputLabel","Select","labelId","event","MenuItem","TextField","placeholder","marginLeft","length","index","similarity","distance","CardMedia","title","toFixed","class","confidence","DialogActions","requestBody","n_results","trim","class_name","maxHeight","dividerMargin","gap","flexWrap","ResultDisplay","bgcolor","renderPerformanceInfo","performance","Divider","ms","undefined","isNaN","num","Number","formatTime","inference_time","device","md","List","detection","ListItem","ListItemText","secondary","bbox","join","top_predictions","prediction","rank","probability","responseBox","whiteSpace","buttonProgress","LlmAnalysis","visionResults","userQuery","setUserQuery","isAnalyzing","setIsAnalyzing","analysisResult","setAnalysisResult","setError","mt","createMuiTheme","typography","fontFamily","App","selectedImage","setSelectedImage","setSelectedModel","setIsProcessing","setResults","setModelsStatus","useEffect","then","catch","ThemeProvider","AppBar","Toolbar","Container","paragraph","formData","FormData","append","endpoint","reportWebVitals","onPerfEntry","Function","getCLS","getFID","getFCP","getLCP","getTTFB","ReactDOM","render","StrictMode","document","getElementById"],"mappings":"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","file":"static/js/main.1756d180.chunk.js","sourcesContent":["import React, { useState, useRef } from 'react';\nimport { \n Paper, \n Typography, \n Box, \n Button, \n IconButton \n} from '@material-ui/core';\nimport CloudUploadIcon from '@material-ui/icons/CloudUpload';\nimport DeleteIcon from '@material-ui/icons/Delete';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n paper: {\n padding: theme.spacing(2),\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n height: '100%',\n minHeight: 300,\n transition: 'all 0.3s ease'\n },\n dragActive: {\n border: '2px dashed #3f51b5',\n backgroundColor: 'rgba(63, 81, 181, 0.05)'\n },\n dragInactive: {\n border: '2px dashed #ccc',\n backgroundColor: 'white'\n },\n uploadBox: {\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n height: '100%',\n width: '100%',\n cursor: 'pointer'\n },\n uploadIcon: {\n fontSize: 60,\n color: '#3f51b5',\n marginBottom: theme.spacing(2)\n },\n supportText: {\n marginTop: theme.spacing(2)\n },\n previewBox: {\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n width: '100%',\n height: '100%',\n position: 'relative'\n },\n imageContainer: {\n position: 'relative',\n width: '100%',\n height: '100%',\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n overflow: 'hidden',\n marginTop: theme.spacing(2)\n },\n deleteButton: {\n position: 'absolute',\n top: 0,\n right: 0,\n backgroundColor: 'rgba(255, 255, 255, 0.7)',\n '&:hover': {\n backgroundColor: 'rgba(255, 255, 255, 0.9)',\n }\n }\n}));\n\nconst ImageUploader = ({ onImageUpload }) => {\n const classes = useStyles();\n const [previewUrl, setPreviewUrl] = useState(null);\n const [dragActive, setDragActive] = useState(false);\n const fileInputRef = useRef(null);\n\n const handleDrag = (e) => {\n e.preventDefault();\n e.stopPropagation();\n if (e.type === 'dragenter' || e.type === 'dragover') {\n setDragActive(true);\n } else if (e.type === 'dragleave') {\n setDragActive(false);\n }\n };\n\n const handleDrop = (e) => {\n e.preventDefault();\n e.stopPropagation();\n setDragActive(false);\n if (e.dataTransfer.files && e.dataTransfer.files[0]) {\n handleFiles(e.dataTransfer.files[0]);\n }\n };\n\n const handleChange = (e) => {\n e.preventDefault();\n if (e.target.files && e.target.files[0]) {\n handleFiles(e.target.files[0]);\n }\n };\n\n const handleFiles = (file) => {\n if (file.type.startsWith('image/')) {\n setPreviewUrl(URL.createObjectURL(file));\n onImageUpload(file);\n } else {\n alert('Please upload an image file');\n }\n };\n\n const onButtonClick = () => {\n fileInputRef.current.click();\n };\n\n const handleRemoveImage = () => {\n setPreviewUrl(null);\n onImageUpload(null);\n fileInputRef.current.value = \"\";\n };\n\n return (\n <Paper \n className={`${classes.paper} ${dragActive ? classes.dragActive : classes.dragInactive}`}\n onDragEnter={handleDrag}\n onDragLeave={handleDrag}\n onDragOver={handleDrag}\n onDrop={handleDrop}\n >\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*\"\n onChange={handleChange}\n style={{ display: 'none' }}\n />\n\n {!previewUrl ? (\n <Box \n className={classes.uploadBox}\n onClick={onButtonClick}\n >\n <CloudUploadIcon className={classes.uploadIcon} />\n <Typography variant=\"h6\" gutterBottom>\n Drag & Drop an image here\n </Typography>\n <Typography variant=\"body2\" color=\"textSecondary\" gutterBottom>\n or\n </Typography>\n <Button\n variant=\"contained\"\n color=\"primary\"\n component=\"span\"\n startIcon={<CloudUploadIcon />}\n >\n Browse Files\n </Button>\n <Typography variant=\"body2\" color=\"textSecondary\" className={classes.supportText}>\n Supported formats: JPG, PNG, GIF\n </Typography>\n </Box>\n ) : (\n <Box className={classes.previewBox}>\n <Typography variant=\"h6\" gutterBottom>\n Preview\n </Typography>\n <Box className={classes.imageContainer}>\n <img\n src={previewUrl}\n alt=\"Preview\"\n className=\"preview-image\"\n />\n <IconButton\n aria-label=\"delete\"\n className={classes.deleteButton}\n onClick={handleRemoveImage}\n >\n <DeleteIcon />\n </IconButton>\n </Box>\n </Box>\n )}\n </Paper>\n );\n};\n\nexport default ImageUploader;\n","import React from 'react';\nimport { \n Grid, \n Card, \n CardContent, \n CardActions, \n Typography, \n Button, \n Chip,\n Box\n} from '@material-ui/core';\nimport VisibilityIcon from '@material-ui/icons/Visibility';\nimport CategoryIcon from '@material-ui/icons/Category';\nimport PlayArrowIcon from '@material-ui/icons/PlayArrow';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n card: {\n height: '100%',\n display: 'flex',\n flexDirection: 'column',\n },\n selectedCard: {\n border: '2px solid #3f51b5',\n },\n unavailableCard: {\n opacity: 0.6,\n },\n cardContent: {\n flexGrow: 1,\n },\n chipContainer: {\n marginBottom: theme.spacing(1.5),\n },\n successChip: {\n backgroundColor: '#34C759',\n color: '#fff',\n },\n errorChip: {\n backgroundColor: '#FF3B3F',\n color: '#fff',\n },\n modelType: {\n marginTop: theme.spacing(1),\n },\n processButton: {\n marginTop: theme.spacing(3),\n textAlign: 'center',\n }\n}));\n\nconst ModelSelector = ({ \n onModelSelect, \n onProcess, \n isProcessing, \n modelsStatus, \n selectedModel,\n imageSelected \n}) => {\n const classes = useStyles();\n \n const models = [\n {\n id: 'yolo',\n name: 'YOLOv8',\n description: 'Fast and accurate object detection',\n icon: <VisibilityIcon />,\n available: modelsStatus.yolo\n },\n {\n id: 'detr',\n name: 'DETR',\n description: 'DEtection TRansformer for object detection',\n icon: <VisibilityIcon />,\n available: modelsStatus.detr\n },\n {\n id: 'vit',\n name: 'ViT',\n description: 'Vision Transformer for image classification',\n icon: <CategoryIcon />,\n available: modelsStatus.vit\n }\n ];\n\n const handleModelClick = (modelId) => {\n if (models.find(m => m.id === modelId).available) {\n onModelSelect(modelId);\n }\n };\n\n return (\n <Box sx={{ p: 2, height: '100%' }}>\n <Typography variant=\"h6\" gutterBottom>\n Select Model\n </Typography>\n \n <Grid container spacing={2}>\n {models.map((model) => (\n <Grid item xs={12} sm={4} key={model.id}>\n <Card \n className={`\n ${classes.card} \n ${selectedModel === model.id ? classes.selectedCard : ''} \n ${!model.available ? classes.unavailableCard : ''}\n `}\n onClick={() => handleModelClick(model.id)}\n >\n <CardContent className={classes.cardContent}>\n <Box sx={{ mb: 2, color: 'primary' }}>\n {model.icon}\n </Box>\n <Typography variant=\"h5\" component=\"div\" gutterBottom>\n {model.name}\n </Typography>\n <div className={classes.chipContainer}>\n {model.available ? (\n <Chip \n label=\"Available\" \n className={classes.successChip}\n size=\"small\" \n />\n ) : (\n <Chip \n label=\"Not Available\" \n className={classes.errorChip}\n size=\"small\" \n />\n )}\n </div>\n <Typography variant=\"body2\" color=\"textSecondary\">\n {model.description}\n </Typography>\n </CardContent>\n <CardActions>\n <Button \n size=\"small\" \n onClick={() => handleModelClick(model.id)}\n disabled={!model.available}\n color={selectedModel === model.id ? \"primary\" : \"default\"}\n variant={selectedModel === model.id ? \"contained\" : \"outlined\"}\n fullWidth\n >\n {selectedModel === model.id ? 'Selected' : 'Select'}\n </Button>\n </CardActions>\n </Card>\n </Grid>\n ))}\n </Grid>\n\n <div className={classes.processButton}>\n <Button\n variant=\"contained\"\n color=\"primary\"\n size=\"large\"\n startIcon={<PlayArrowIcon />}\n onClick={onProcess}\n disabled={!selectedModel || !imageSelected || isProcessing}\n >\n {isProcessing ? 'Processing...' : 'Process Image'}\n </Button>\n </div>\n </Box>\n );\n};\n\nexport default ModelSelector;\n","import React, { useState } from 'react';\nimport { \n Button, \n Box, \n Typography, \n CircularProgress, \n Snackbar,\n Dialog,\n DialogTitle,\n DialogContent,\n DialogActions,\n TextField,\n FormControl,\n InputLabel,\n Select,\n MenuItem,\n Grid,\n Card,\n CardMedia,\n CardContent,\n Chip\n} from '@material-ui/core';\nimport { Alert } from '@material-ui/lab';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n root: {\n marginTop: theme.spacing(2),\n marginBottom: theme.spacing(2),\n padding: theme.spacing(2),\n backgroundColor: '#f5f5f5',\n borderRadius: theme.shape.borderRadius,\n },\n button: {\n marginRight: theme.spacing(2),\n },\n searchDialog: {\n minWidth: '500px',\n },\n formControl: {\n marginBottom: theme.spacing(2),\n minWidth: '100%',\n },\n searchResults: {\n marginTop: theme.spacing(2),\n },\n resultCard: {\n marginBottom: theme.spacing(2),\n },\n resultImage: {\n height: 140,\n objectFit: 'contain',\n },\n chip: {\n margin: theme.spacing(0.5),\n },\n similarityChip: {\n backgroundColor: theme.palette.primary.main,\n color: 'white',\n }\n}));\n\nconst VectorDBActions = ({ results }) => {\n const classes = useStyles();\n const [isSaving, setIsSaving] = useState(false);\n const [saveSuccess, setSaveSuccess] = useState(false);\n const [saveError, setSaveError] = useState(null);\n const [openSearchDialog, setOpenSearchDialog] = useState(false);\n const [searchType, setSearchType] = useState('image');\n const [searchClass, setSearchClass] = useState('');\n const [searchResults, setSearchResults] = useState([]);\n const [isSearching, setIsSearching] = useState(false);\n const [searchError, setSearchError] = useState(null);\n \n // Extract model and data from results\n const { model, data } = results;\n \n // Handle saving to vector DB\n const handleSaveToVectorDB = async () => {\n setIsSaving(true);\n setSaveError(null);\n \n try {\n let response;\n \n if (model === 'vit') {\n // For ViT, save the whole image with classifications\n response = await fetch('/api/add-image', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n image: data.image,\n metadata: {\n model: 'vit',\n classifications: data.classifications\n }\n })\n });\n } else {\n // For YOLO and DETR, save detected objects\n response = await fetch('/api/add-detected-objects', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n image: data.image,\n objects: data.detections,\n image_id: generateUUID()\n })\n });\n }\n \n if (!response.ok) {\n throw new Error(`HTTP error! Status: ${response.status}`);\n }\n \n const result = await response.json();\n \n if (result.error) {\n throw new Error(result.error);\n }\n \n setSaveSuccess(true);\n setTimeout(() => setSaveSuccess(false), 5000);\n } catch (err) {\n console.error('Error saving to vector DB:', err);\n setSaveError(`Error saving to vector DB: ${err.message}`);\n } finally {\n setIsSaving(false);\n }\n };\n \n // Handle opening search dialog\n const handleOpenSearchDialog = () => {\n setOpenSearchDialog(true);\n setSearchResults([]);\n setSearchError(null);\n };\n \n // Handle closing search dialog\n const handleCloseSearchDialog = () => {\n setOpenSearchDialog(false);\n };\n \n // Handle search type change\n const handleSearchTypeChange = (event) => {\n setSearchType(event.target.value);\n setSearchResults([]);\n setSearchError(null);\n };\n \n // Handle search class change\n const handleSearchClassChange = (event) => {\n setSearchClass(event.target.value);\n };\n \n // Handle search\n const handleSearch = async () => {\n setIsSearching(true);\n setSearchError(null);\n \n try {\n let requestBody = {};\n \n if (searchType === 'image') {\n // Search by current image\n requestBody = {\n image: data.image,\n n_results: 5\n };\n } else {\n // Search by class name\n if (!searchClass.trim()) {\n throw new Error('Please enter a class name');\n }\n \n requestBody = {\n class_name: searchClass.trim(),\n n_results: 5\n };\n }\n \n const response = await fetch('/api/search-similar-objects', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(requestBody)\n });\n \n if (!response.ok) {\n throw new Error(`HTTP error! Status: ${response.status}`);\n }\n \n const result = await response.json();\n \n if (result.error) {\n throw new Error(result.error);\n }\n \n setSearchResults(result);\n } catch (err) {\n console.error('Error searching vector DB:', err);\n setSearchError(`Error searching vector DB: ${err.message}`);\n } finally {\n setIsSearching(false);\n }\n };\n \n // Generate UUID for image ID\n const generateUUID = () => {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n const r = Math.random() * 16 | 0;\n const v = c === 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n };\n \n // Render search results\n const renderSearchResults = () => {\n if (searchResults.length === 0) {\n return (\n <Typography variant=\"body1\">No results found.</Typography>\n );\n }\n \n return (\n <Grid container spacing={2}>\n {searchResults.map((result, index) => {\n const similarity = (1 - result.distance) * 100;\n \n return (\n <Grid item xs={12} sm={6} key={index}>\n <Card className={classes.resultCard}>\n <CardMedia\n className={classes.resultImage}\n image={`data:image/jpeg;base64,${result.image}`}\n title={`Result ${index + 1}`}\n />\n <CardContent>\n <Box display=\"flex\" justifyContent=\"space-between\" alignItems=\"center\" mb={1}>\n <Typography variant=\"subtitle1\">Result #{index + 1}</Typography>\n <Chip \n label={`Similarity: ${similarity.toFixed(2)}%`}\n className={classes.similarityChip}\n size=\"small\"\n />\n </Box>\n <Typography variant=\"body2\" color=\"textSecondary\">\n <strong>Class:</strong> {result.metadata.class || 'N/A'}\n </Typography>\n {result.metadata.confidence && (\n <Typography variant=\"body2\" color=\"textSecondary\">\n <strong>Confidence:</strong> {(result.metadata.confidence * 100).toFixed(2)}%\n </Typography>\n )}\n <Typography variant=\"body2\" color=\"textSecondary\">\n <strong>Object ID:</strong> {result.id}\n </Typography>\n </CardContent>\n </Card>\n </Grid>\n );\n })}\n </Grid>\n );\n };\n \n return (\n <Box className={classes.root}>\n <Typography variant=\"h6\" gutterBottom>\n Vector Database Actions\n </Typography>\n \n <Box display=\"flex\" alignItems=\"center\" mb={2}>\n <Button\n variant=\"contained\"\n color=\"primary\"\n onClick={handleSaveToVectorDB}\n disabled={isSaving}\n className={classes.button}\n >\n {isSaving ? (\n <>\n <CircularProgress size={20} color=\"inherit\" style={{ marginRight: 8 }} />\n Saving...\n </>\n ) : (\n 'Save to Vector DB'\n )}\n </Button>\n \n <Button\n variant=\"outlined\"\n color=\"primary\"\n onClick={handleOpenSearchDialog}\n className={classes.button}\n >\n Search Similar\n </Button>\n </Box>\n \n {saveError && (\n <Alert severity=\"error\" style={{ marginTop: 8 }}>\n {saveError}\n </Alert>\n )}\n \n <Snackbar open={saveSuccess} autoHideDuration={5000} onClose={() => setSaveSuccess(false)}>\n <Alert severity=\"success\">\n {model === 'vit' ? (\n 'Image and classifications successfully saved to vector DB!'\n ) : (\n 'Detected objects successfully saved to vector DB!'\n )}\n </Alert>\n </Snackbar>\n \n {/* Search Dialog */}\n <Dialog\n open={openSearchDialog}\n onClose={handleCloseSearchDialog}\n maxWidth=\"md\"\n fullWidth\n >\n <DialogTitle>Search Vector Database</DialogTitle>\n <DialogContent>\n <FormControl className={classes.formControl}>\n <InputLabel id=\"search-type-label\">Search Type</InputLabel>\n <Select\n labelId=\"search-type-label\"\n id=\"search-type\"\n value={searchType}\n onChange={handleSearchTypeChange}\n >\n <MenuItem value=\"image\">Search by Current Image</MenuItem>\n <MenuItem value=\"class\">Search by Class Name</MenuItem>\n </Select>\n </FormControl>\n \n {searchType === 'class' && (\n <FormControl className={classes.formControl}>\n <TextField\n label=\"Class Name\"\n value={searchClass}\n onChange={handleSearchClassChange}\n placeholder=\"e.g. person, car, dog...\"\n fullWidth\n />\n </FormControl>\n )}\n \n {searchError && (\n <Alert severity=\"error\" style={{ marginBottom: 16 }}>\n {searchError}\n </Alert>\n )}\n \n <Box className={classes.searchResults}>\n {isSearching ? (\n <Box display=\"flex\" justifyContent=\"center\" alignItems=\"center\" p={4}>\n <CircularProgress />\n <Typography variant=\"body1\" style={{ marginLeft: 16 }}>\n Searching...\n </Typography>\n </Box>\n ) : (\n searchResults.length > 0 && renderSearchResults()\n )}\n </Box>\n </DialogContent>\n <DialogActions>\n <Button onClick={handleCloseSearchDialog} color=\"default\">\n Close\n </Button>\n <Button \n onClick={handleSearch} \n color=\"primary\" \n variant=\"contained\"\n disabled={isSearching || (searchType === 'class' && !searchClass.trim())}\n >\n Search\n </Button>\n </DialogActions>\n </Dialog>\n </Box>\n );\n};\n\nexport default VectorDBActions;\n","import React from 'react';\nimport { \n Paper, \n Typography, \n Box, \n List, \n ListItem, \n ListItemText, \n Divider,\n Grid,\n Chip\n} from '@material-ui/core';\nimport VectorDBActions from './VectorDBActions';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n paper: {\n padding: theme.spacing(2)\n },\n marginBottom: {\n marginBottom: theme.spacing(2)\n },\n resultImage: {\n maxWidth: '100%',\n maxHeight: '400px',\n objectFit: 'contain'\n },\n dividerMargin: {\n margin: `${theme.spacing(2)}px 0`\n },\n chipContainer: {\n display: 'flex',\n gap: theme.spacing(1),\n flexWrap: 'wrap'\n }\n}));\n\nconst ResultDisplay = ({ results }) => {\n const classes = useStyles();\n if (!results) return null;\n \n const { model, data } = results;\n \n // Helper to format times nicely\n const formatTime = (ms) => {\n if (ms === undefined || ms === null || isNaN(ms)) return '-';\n const num = Number(ms);\n if (num < 1000) return `${num.toFixed(2)} ms`;\n return `${(num / 1000).toFixed(2)} s`;\n };\n \n // Check if there's an error\n if (data.error) {\n return (\n <Paper sx={{ p: 2, bgcolor: '#ffebee' }}>\n <Typography color=\"error\">{data.error}</Typography>\n </Paper>\n );\n }\n\n // Display performance info\n const renderPerformanceInfo = () => {\n if (!data.performance) return null;\n \n return (\n <Box className=\"performance-info\">\n <Divider className={classes.dividerMargin} />\n <Typography variant=\"body2\">\n Inference time: {formatTime(data.performance.inference_time)} on {data.performance.device}\n </Typography>\n </Box>\n );\n };\n\n // Render for YOLO and DETR (object detection)\n if (model === 'yolo' || model === 'detr') {\n return (\n <Paper className={classes.paper}>\n <Typography variant=\"h6\" gutterBottom>\n {model === 'yolo' ? 'YOLOv8' : 'DETR'} Detection Results\n </Typography>\n \n <Grid container spacing={3}>\n <Grid item xs={12} md={6}>\n {data.image && (\n <Box className={classes.marginBottom}>\n <Typography variant=\"subtitle1\" gutterBottom>\n Detection Result\n </Typography>\n <img \n src={`data:image/png;base64,${data.image}`} \n alt=\"Detection Result\" \n className={classes.resultImage}\n />\n </Box>\n )}\n </Grid>\n \n <Grid item xs={12} md={6}>\n <Box className={classes.marginBottom}>\n <Typography variant=\"subtitle1\" gutterBottom>\n Detected Objects:\n </Typography>\n \n {data.detections && data.detections.length > 0 ? (\n <List>\n {data.detections.map((detection, index) => (\n <React.Fragment key={index}>\n <ListItem>\n <ListItemText \n primary={\n <Box style={{ display: 'flex', alignItems: 'center' }}>\n <Typography variant=\"body1\" component=\"span\">\n {detection.class}\n </Typography>\n <Chip \n label={`${(detection.confidence * 100).toFixed(0)}%`}\n size=\"small\"\n color=\"primary\"\n style={{ marginLeft: 8 }}\n />\n </Box>\n } \n secondary={`Bounding Box: [${detection.bbox.join(', ')}]`} \n />\n </ListItem>\n {index < data.detections.length - 1 && <Divider />}\n </React.Fragment>\n ))}\n </List>\n ) : (\n <Typography variant=\"body1\">No objects detected</Typography>\n )}\n </Box>\n </Grid>\n </Grid>\n \n {renderPerformanceInfo()}\n \n {/* Vector DB Actions for Object Detection */}\n <VectorDBActions results={results} />\n </Paper>\n );\n }\n \n // Render for ViT (classification)\n if (model === 'vit') {\n return (\n <Paper className={classes.paper}>\n <Typography variant=\"h6\" gutterBottom>\n ViT Classification Results\n </Typography>\n \n <Typography variant=\"subtitle1\" gutterBottom>\n Top Predictions:\n </Typography>\n \n {data.top_predictions && data.top_predictions.length > 0 ? (\n <List>\n {data.top_predictions.map((prediction, index) => (\n <React.Fragment key={index}>\n <ListItem>\n <ListItemText \n primary={\n <Box style={{ display: 'flex', alignItems: 'center' }}>\n <Typography variant=\"body1\" component=\"span\">\n {prediction.rank}. {prediction.class}\n </Typography>\n <Chip \n label={`${(prediction.probability * 100).toFixed(1)}%`}\n size=\"small\"\n color={index === 0 ? \"primary\" : \"default\"}\n style={{ marginLeft: 8 }}\n />\n </Box>\n } \n />\n </ListItem>\n {index < data.top_predictions.length - 1 && <Divider />}\n </React.Fragment>\n ))}\n </List>\n ) : (\n <Typography variant=\"body1\">No classifications available</Typography>\n )}\n \n {renderPerformanceInfo()}\n \n {/* Vector DB Actions for ViT Classification */}\n <VectorDBActions results={results} />\n </Paper>\n );\n }\n \n return null;\n};\n\nexport default ResultDisplay;\n","import React, { useState } from 'react';\nimport { \n Paper, \n Typography, \n Box, \n TextField, \n Button, \n CircularProgress,\n Divider\n} from '@material-ui/core';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n paper: {\n padding: theme.spacing(2),\n marginTop: theme.spacing(2)\n },\n marginBottom: {\n marginBottom: theme.spacing(2)\n },\n dividerMargin: {\n margin: `${theme.spacing(2)}px 0`\n },\n responseBox: {\n padding: theme.spacing(2),\n backgroundColor: '#f5f5f5',\n borderRadius: theme.shape.borderRadius,\n marginTop: theme.spacing(2),\n whiteSpace: 'pre-wrap'\n },\n buttonProgress: {\n marginLeft: theme.spacing(1)\n }\n}));\n\nconst LlmAnalysis = ({ visionResults, model }) => {\n const classes = useStyles();\n const [userQuery, setUserQuery] = useState('');\n const [isAnalyzing, setIsAnalyzing] = useState(false);\n const [analysisResult, setAnalysisResult] = useState(null);\n const [error, setError] = useState(null);\n\n // Format time for display\n const formatTime = (ms) => {\n if (ms === undefined || ms === null || isNaN(ms)) return '-';\n const num = Number(ms);\n if (num < 1000) return `${num.toFixed(2)} ms`;\n return `${(num / 1000).toFixed(2)} s`;\n };\n\n const handleAnalyze = async () => {\n if (!userQuery.trim()) return;\n \n setIsAnalyzing(true);\n setError(null);\n \n try {\n const response = await fetch('/api/analyze', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n visionResults: visionResults,\n userQuery: userQuery\n }),\n });\n\n if (!response.ok) {\n throw new Error(`HTTP error! Status: ${response.status}`);\n }\n\n const data = await response.json();\n \n if (data.error) {\n setError(data.error);\n } else {\n setAnalysisResult(data);\n }\n } catch (err) {\n console.error('Error analyzing with LLM:', err);\n setError(`Error analyzing with LLM: ${err.message}`);\n } finally {\n setIsAnalyzing(false);\n }\n };\n\n if (!visionResults) return null;\n\n return (\n <Paper className={classes.paper}>\n <Typography variant=\"h6\" gutterBottom>\n Ask AI about the {model === 'vit' ? 'Classification' : 'Detection'} Results\n </Typography>\n \n <Typography variant=\"body2\" className={classes.marginBottom}>\n Ask a question about the detected objects or classifications to get an AI-powered analysis.\n </Typography>\n \n <TextField\n fullWidth\n label=\"Your question about the image\"\n variant=\"outlined\"\n value={userQuery}\n onChange={(e) => setUserQuery(e.target.value)}\n disabled={isAnalyzing}\n className={classes.marginBottom}\n placeholder={model === 'vit' \n ? \"E.g., What category does this image belong to?\" \n : \"E.g., How many people are in this image?\"}\n />\n \n <Button \n variant=\"contained\" \n color=\"primary\"\n onClick={handleAnalyze}\n disabled={isAnalyzing || !userQuery.trim()}\n >\n Analyze with AI\n {isAnalyzing && <CircularProgress size={24} className={classes.buttonProgress} />}\n </Button>\n \n {error && (\n <Box mt={2}>\n <Typography color=\"error\">{error}</Typography>\n </Box>\n )}\n \n {analysisResult && (\n <>\n <Divider className={classes.dividerMargin} />\n \n <Typography variant=\"subtitle1\" gutterBottom>\n AI Analysis:\n </Typography>\n \n <Box className={classes.responseBox}>\n <Typography variant=\"body1\">\n {analysisResult.response}\n </Typography>\n </Box>\n \n {analysisResult.performance && (\n <Box mt={1}>\n <Typography variant=\"body2\" color=\"textSecondary\">\n Analysis time: {formatTime(analysisResult.performance.inference_time)} on {analysisResult.performance.device}\n </Typography>\n </Box>\n )}\n </>\n )}\n </Paper>\n );\n};\n\nexport default LlmAnalysis;\n","import React, { useState, useEffect } from 'react';\nimport { \n Container, \n Typography, \n Box, \n Paper, \n Grid, \n CircularProgress,\n AppBar,\n Toolbar,\n ThemeProvider,\n createMuiTheme\n} from '@material-ui/core';\nimport ImageUploader from './components/ImageUploader';\nimport ModelSelector from './components/ModelSelector';\nimport ResultDisplay from './components/ResultDisplay';\nimport LlmAnalysis from './components/LlmAnalysis';\nimport './App.css';\n\n// Create a theme\nconst theme = createMuiTheme({\n palette: {\n primary: {\n main: '#3f51b5',\n },\n secondary: {\n main: '#f50057',\n },\n },\n typography: {\n fontFamily: 'Roboto, Arial, sans-serif',\n },\n});\n\nfunction App() {\n const [selectedImage, setSelectedImage] = useState(null);\n const [selectedModel, setSelectedModel] = useState('');\n const [isProcessing, setIsProcessing] = useState(false);\n const [results, setResults] = useState(null);\n const [error, setError] = useState(null);\n const [modelsStatus, setModelsStatus] = useState({\n yolo: false,\n detr: false,\n vit: false\n });\n\n // Check API status on component mount\n useEffect(() => {\n fetch('/api/status')\n .then(response => response.json())\n .then(data => {\n setModelsStatus(data.models);\n })\n .catch(err => {\n console.error('Error checking API status:', err);\n setError('Error connecting to the backend API. Please make sure the server is running.');\n });\n }, []);\n\n const handleImageUpload = (image) => {\n setSelectedImage(image);\n setResults(null);\n setError(null);\n };\n\n const handleModelSelect = (model) => {\n setSelectedModel(model);\n setResults(null);\n setError(null);\n };\n\n const processImage = async () => {\n if (!selectedImage || !selectedModel) {\n setError('Please select both an image and a model');\n return;\n }\n\n setIsProcessing(true);\n setError(null);\n\n // Create form data for the image\n const formData = new FormData();\n formData.append('image', selectedImage);\n\n let endpoint = '';\n switch (selectedModel) {\n case 'yolo':\n endpoint = '/api/detect/yolo';\n break;\n case 'detr':\n endpoint = '/api/detect/detr';\n break;\n case 'vit':\n endpoint = '/api/classify/vit';\n break;\n default:\n setError('Invalid model selection');\n setIsProcessing(false);\n return;\n }\n\n try {\n const response = await fetch(endpoint, {\n method: 'POST',\n body: formData,\n });\n\n if (!response.ok) {\n throw new Error(`HTTP error! Status: ${response.status}`);\n }\n\n const data = await response.json();\n setResults({ model: selectedModel, data });\n } catch (err) {\n console.error('Error processing image:', err);\n setError(`Error processing image: ${err.message}`);\n } finally {\n setIsProcessing(false);\n }\n };\n\n return (\n <ThemeProvider theme={theme}>\n <Box style={{ flexGrow: 1 }}>\n <AppBar position=\"static\">\n <Toolbar>\n <Typography variant=\"h6\" style={{ flexGrow: 1 }}>\n Multi-Model Object Detection Demo\n </Typography>\n </Toolbar>\n </AppBar>\n <Container maxWidth=\"lg\" style={{ marginTop: theme.spacing(4), marginBottom: theme.spacing(4) }}>\n <Grid container spacing={3}>\n <Grid item xs={12}>\n <Paper style={{ padding: theme.spacing(2) }}>\n <Typography variant=\"h5\" gutterBottom>\n Upload an image to see how each model performs!\n </Typography>\n <Typography variant=\"body1\" paragraph>\n This demo showcases three different object detection and image classification models:\n </Typography>\n <Typography variant=\"body1\" component=\"div\">\n <ul>\n <li><strong>YOLOv8</strong>: Fast and accurate object detection</li>\n <li><strong>DETR</strong>: DEtection TRansformer for object detection</li>\n <li><strong>ViT</strong>: Vision Transformer for image classification</li>\n </ul>\n </Typography>\n </Paper>\n </Grid>\n \n <Grid item xs={12} md={6}>\n <ImageUploader onImageUpload={handleImageUpload} />\n </Grid>\n \n <Grid item xs={12} md={6}>\n <ModelSelector \n onModelSelect={handleModelSelect} \n onProcess={processImage}\n isProcessing={isProcessing}\n modelsStatus={modelsStatus}\n selectedModel={selectedModel}\n imageSelected={!!selectedImage}\n />\n </Grid>\n \n {error && (\n <Grid item xs={12}>\n <Paper style={{ padding: theme.spacing(2), backgroundColor: '#ffebee' }}>\n <Typography color=\"error\">{error}</Typography>\n </Paper>\n </Grid>\n )}\n \n {isProcessing && (\n <Grid item xs={12} style={{ textAlign: 'center', margin: `${theme.spacing(4)}px 0` }}>\n <CircularProgress />\n <Typography variant=\"h6\" style={{ marginTop: theme.spacing(2) }}>\n Processing image...\n </Typography>\n </Grid>\n )}\n \n {results && (\n <>\n <Grid item xs={12}>\n <ResultDisplay results={results} />\n </Grid>\n <Grid item xs={12}>\n <LlmAnalysis visionResults={results.data} model={results.model} />\n </Grid>\n </>\n )}\n </Grid>\n </Container>\n </Box>\n </ThemeProvider>\n );\n}\n\nexport default App;\n","const reportWebVitals = (onPerfEntry) => {\n if (onPerfEntry && onPerfEntry instanceof Function) {\n import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {\n getCLS(onPerfEntry);\n getFID(onPerfEntry);\n getFCP(onPerfEntry);\n getLCP(onPerfEntry);\n getTTFB(onPerfEntry);\n });\n }\n};\n\nexport default reportWebVitals;\n","import React from 'react';\nimport ReactDOM from 'react-dom';\nimport './index.css';\nimport App from './App';\nimport reportWebVitals from './reportWebVitals';\n\nReactDOM.render(\n <React.StrictMode>\n <App />\n </React.StrictMode>,\n document.getElementById('root')\n);\n\n// If you want to start measuring performance in your app, pass a function\n// to log results (for example: reportWebVitals(console.log))\n// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals\nreportWebVitals();\n"],"sourceRoot":""}
frontend/build/static/js/main.3d1593c5.chunk.js DELETED
@@ -1,2 +0,0 @@
1
- (this["webpackJsonpvision-web-app"]=this["webpackJsonpvision-web-app"]||[]).push([[0],{77:function(e,a,t){e.exports=t(88)},82:function(e,a,t){},87:function(e,a,t){},88:function(e,a,t){"use strict";t.r(a);var n=t(0),r=t.n(n),l=t(10),o=t.n(l),c=(t(82),t(68)),i=t(129),s=t(134),m=t(130),d=t(131),p=t(47),g=t(132),u=t(120),E=t(70),b=t(128),f=t(118),v=t(119),h=t(53),y=t.n(h),x=t(65),B=t.n(x),C=t(115);const w=Object(C.a)(e=>({paper:{padding:e.spacing(2),display:"flex",flexDirection:"column",alignItems:"center",height:"100%",minHeight:300,transition:"all 0.3s ease"},dragActive:{border:"2px dashed #3f51b5",backgroundColor:"rgba(63, 81, 181, 0.05)"},dragInactive:{border:"2px dashed #ccc",backgroundColor:"white"},uploadBox:{display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",height:"100%",width:"100%",cursor:"pointer"},uploadIcon:{fontSize:60,color:"#3f51b5",marginBottom:e.spacing(2)},supportText:{marginTop:e.spacing(2)},previewBox:{display:"flex",flexDirection:"column",alignItems:"center",width:"100%",height:"100%",position:"relative"},imageContainer:{position:"relative",width:"100%",height:"100%",display:"flex",justifyContent:"center",alignItems:"center",overflow:"hidden",marginTop:e.spacing(2)},deleteButton:{position:"absolute",top:0,right:0,backgroundColor:"rgba(255, 255, 255, 0.7)","&:hover":{backgroundColor:"rgba(255, 255, 255, 0.9)"}}}));var N=e=>{let{onImageUpload:a}=e;const t=w(),[l,o]=Object(n.useState)(null),[c,i]=Object(n.useState)(!1),m=Object(n.useRef)(null),d=e=>{e.preventDefault(),e.stopPropagation(),"dragenter"===e.type||"dragover"===e.type?i(!0):"dragleave"===e.type&&i(!1)},g=e=>{e.type.startsWith("image/")?(o(URL.createObjectURL(e)),a(e)):alert("Please upload an image file")};return r.a.createElement(E.a,{className:"".concat(t.paper," ").concat(c?t.dragActive:t.dragInactive),onDragEnter:d,onDragLeave:d,onDragOver:d,onDrop:e=>{e.preventDefault(),e.stopPropagation(),i(!1),e.dataTransfer.files&&e.dataTransfer.files[0]&&g(e.dataTransfer.files[0])}},r.a.createElement("input",{ref:m,type:"file",accept:"image/*",onChange:e=>{e.preventDefault(),e.target.files&&e.target.files[0]&&g(e.target.files[0])},style:{display:"none"}}),l?r.a.createElement(s.a,{className:t.previewBox},r.a.createElement(p.a,{variant:"h6",gutterBottom:!0},"Preview"),r.a.createElement(s.a,{className:t.imageContainer},r.a.createElement("img",{src:l,alt:"Preview",className:"preview-image"}),r.a.createElement(v.a,{"aria-label":"delete",className:t.deleteButton,onClick:()=>{o(null),a(null),m.current.value=""}},r.a.createElement(B.a,null)))):r.a.createElement(s.a,{className:t.uploadBox,onClick:()=>{m.current.click()}},r.a.createElement(y.a,{className:t.uploadIcon}),r.a.createElement(p.a,{variant:"h6",gutterBottom:!0},"Drag & Drop an image here"),r.a.createElement(p.a,{variant:"body2",color:"textSecondary",gutterBottom:!0},"or"),r.a.createElement(f.a,{variant:"contained",color:"primary",component:"span",startIcon:r.a.createElement(y.a,null)},"Browse Files"),r.a.createElement(p.a,{variant:"body2",color:"textSecondary",className:t.supportText},"Supported formats: JPG, PNG, GIF")))},j=t(121),S=t(122),T=t(136),O=t(123),k=t(54),I=t.n(k),D=t(66),P=t.n(D),F=t(67),R=t.n(F);const A=Object(C.a)(e=>({card:{height:"100%",display:"flex",flexDirection:"column"},selectedCard:{border:"2px solid #3f51b5"},unavailableCard:{opacity:.6},cardContent:{flexGrow:1},chipContainer:{marginBottom:e.spacing(1.5)},successChip:{backgroundColor:"#34C759",color:"#fff"},errorChip:{backgroundColor:"#FF3B3F",color:"#fff"},modelType:{marginTop:e.spacing(1)},processButton:{marginTop:e.spacing(3),textAlign:"center"}}));var L=e=>{let{onModelSelect:a,onProcess:t,isProcessing:n,modelsStatus:l,selectedModel:o,imageSelected:c}=e;const i=A(),m=[{id:"yolo",name:"YOLOv8",description:"Fast and accurate object detection",icon:r.a.createElement(I.a,null),available:l.yolo},{id:"detr",name:"DETR",description:"DEtection TRansformer for object detection",icon:r.a.createElement(I.a,null),available:l.detr},{id:"vit",name:"ViT",description:"Vision Transformer for image classification",icon:r.a.createElement(P.a,null),available:l.vit}],d=e=>{m.find(a=>a.id===e).available&&a(e)};return r.a.createElement(s.a,{sx:{p:2,height:"100%"}},r.a.createElement(p.a,{variant:"h6",gutterBottom:!0},"Select Model"),r.a.createElement(u.a,{container:!0,spacing:2},m.map(e=>r.a.createElement(u.a,{item:!0,xs:12,sm:4,key:e.id},r.a.createElement(j.a,{className:"\n ".concat(i.card," \n ").concat(o===e.id?i.selectedCard:""," \n ").concat(e.available?"":i.unavailableCard,"\n "),onClick:()=>d(e.id)},r.a.createElement(S.a,{className:i.cardContent},r.a.createElement(s.a,{sx:{mb:2,color:"primary"}},e.icon),r.a.createElement(p.a,{variant:"h5",component:"div",gutterBottom:!0},e.name),r.a.createElement("div",{className:i.chipContainer},e.available?r.a.createElement(T.a,{label:"Available",className:i.successChip,size:"small"}):r.a.createElement(T.a,{label:"Not Available",className:i.errorChip,size:"small"})),r.a.createElement(p.a,{variant:"body2",color:"textSecondary"},e.description)),r.a.createElement(O.a,null,r.a.createElement(f.a,{size:"small",onClick:()=>d(e.id),disabled:!e.available,color:o===e.id?"primary":"default",variant:o===e.id?"contained":"outlined",fullWidth:!0},o===e.id?"Selected":"Select")))))),r.a.createElement("div",{className:i.processButton},r.a.createElement(f.a,{variant:"contained",color:"primary",size:"large",startIcon:r.a.createElement(R.a,null),onClick:t,disabled:!o||!c||n},n?"Processing...":"Process Image")))},M=t(124),z=t(125),W=t(126),G=t(127);const _=Object(C.a)(e=>({paper:{padding:e.spacing(2)},marginBottom:{marginBottom:e.spacing(2)},resultImage:{maxWidth:"100%",maxHeight:"400px",objectFit:"contain"},dividerMargin:{margin:"".concat(e.spacing(2),"px 0")},chipContainer:{display:"flex",gap:e.spacing(1),flexWrap:"wrap"}}));var H=e=>{let{results:a}=e;const t=_();if(!a)return null;const{model:n,data:l}=a;if(l.error)return r.a.createElement(E.a,{sx:{p:2,bgcolor:"#ffebee"}},r.a.createElement(p.a,{color:"error"},l.error));const o=()=>l.performance?r.a.createElement(s.a,{className:"performance-info"},r.a.createElement(M.a,{className:t.dividerMargin}),r.a.createElement(p.a,{variant:"body2"},"Inference time: ",(e=>{if(void 0===e||null===e||isNaN(e))return"-";const a=Number(e);return a<1e3?"".concat(a.toFixed(2)," ms"):"".concat((a/1e3).toFixed(2)," s")})(l.performance.inference_time)," on ",l.performance.device)):null;return"yolo"===n||"detr"===n?r.a.createElement(E.a,{className:t.paper},r.a.createElement(p.a,{variant:"h6",gutterBottom:!0},"yolo"===n?"YOLOv8":"DETR"," Detection Results"),r.a.createElement(u.a,{container:!0,spacing:3},r.a.createElement(u.a,{item:!0,xs:12,md:6},l.image&&r.a.createElement(s.a,{className:t.marginBottom},r.a.createElement(p.a,{variant:"subtitle1",gutterBottom:!0},"Detection Result"),r.a.createElement("img",{src:"data:image/png;base64,".concat(l.image),alt:"Detection Result",className:t.resultImage}))),r.a.createElement(u.a,{item:!0,xs:12,md:6},r.a.createElement(s.a,{className:t.marginBottom},r.a.createElement(p.a,{variant:"subtitle1",gutterBottom:!0},"Detected Objects:"),l.detections&&l.detections.length>0?r.a.createElement(z.a,null,l.detections.map((e,a)=>r.a.createElement(r.a.Fragment,{key:a},r.a.createElement(W.a,null,r.a.createElement(G.a,{primary:r.a.createElement(s.a,{style:{display:"flex",alignItems:"center"}},r.a.createElement(p.a,{variant:"body1",component:"span"},e.class),r.a.createElement(T.a,{label:"".concat((100*e.confidence).toFixed(0),"%"),size:"small",color:"primary",style:{marginLeft:8}})),secondary:"Bounding Box: [".concat(e.bbox.join(", "),"]")})),a<l.detections.length-1&&r.a.createElement(M.a,null)))):r.a.createElement(p.a,{variant:"body1"},"No objects detected")))),o()):"vit"===n?r.a.createElement(E.a,{className:t.paper},r.a.createElement(p.a,{variant:"h6",gutterBottom:!0},"ViT Classification Results"),r.a.createElement(p.a,{variant:"subtitle1",gutterBottom:!0},"Top Predictions:"),l.top_predictions&&l.top_predictions.length>0?r.a.createElement(z.a,null,l.top_predictions.map((e,a)=>r.a.createElement(r.a.Fragment,{key:a},r.a.createElement(W.a,null,r.a.createElement(G.a,{primary:r.a.createElement(s.a,{style:{display:"flex",alignItems:"center"}},r.a.createElement(p.a,{variant:"body1",component:"span"},e.rank,". ",e.class),r.a.createElement(T.a,{label:"".concat((100*e.probability).toFixed(1),"%"),size:"small",color:0===a?"primary":"default",style:{marginLeft:8}}))})),a<l.top_predictions.length-1&&r.a.createElement(M.a,null)))):r.a.createElement(p.a,{variant:"body1"},"No classifications available"),o()):null},U=t(133);const V=Object(C.a)(e=>({paper:{padding:e.spacing(2),marginTop:e.spacing(2)},marginBottom:{marginBottom:e.spacing(2)},dividerMargin:{margin:"".concat(e.spacing(2),"px 0")},responseBox:{padding:e.spacing(2),backgroundColor:"#f5f5f5",borderRadius:e.shape.borderRadius,marginTop:e.spacing(2),whiteSpace:"pre-wrap"},buttonProgress:{marginLeft:e.spacing(1)}}));var J=e=>{let{visionResults:a,model:t}=e;const l=V(),[o,c]=Object(n.useState)(""),[i,m]=Object(n.useState)(!1),[d,g]=Object(n.useState)(null),[u,v]=Object(n.useState)(null);return a?r.a.createElement(E.a,{className:l.paper},r.a.createElement(p.a,{variant:"h6",gutterBottom:!0},"Ask AI about the ","vit"===t?"Classification":"Detection"," Results"),r.a.createElement(p.a,{variant:"body2",className:l.marginBottom},"Ask a question about the detected objects or classifications to get an AI-powered analysis."),r.a.createElement(U.a,{fullWidth:!0,label:"Your question about the image",variant:"outlined",value:o,onChange:e=>c(e.target.value),disabled:i,className:l.marginBottom,placeholder:"vit"===t?"E.g., What category does this image belong to?":"E.g., How many people are in this image?"}),r.a.createElement(f.a,{variant:"contained",color:"primary",onClick:async()=>{if(o.trim()){m(!0),v(null);try{const e=await fetch("/api/analyze",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({visionResults:a,userQuery:o})});if(!e.ok)throw new Error("HTTP error! Status: ".concat(e.status));const t=await e.json();t.error?v(t.error):g(t)}catch(e){console.error("Error analyzing with LLM:",e),v("Error analyzing with LLM: ".concat(e.message))}finally{m(!1)}}},disabled:i||!o.trim()},"Analyze with AI",i&&r.a.createElement(b.a,{size:24,className:l.buttonProgress})),u&&r.a.createElement(s.a,{mt:2},r.a.createElement(p.a,{color:"error"},u)),d&&r.a.createElement(r.a.Fragment,null,r.a.createElement(M.a,{className:l.dividerMargin}),r.a.createElement(p.a,{variant:"subtitle1",gutterBottom:!0},"AI Analysis:"),r.a.createElement(s.a,{className:l.responseBox},r.a.createElement(p.a,{variant:"body1"},d.response)),d.performance&&r.a.createElement(s.a,{mt:1},r.a.createElement(p.a,{variant:"body2",color:"textSecondary"},"Analysis time: ",(e=>{if(void 0===e||null===e||isNaN(e))return"-";const a=Number(e);return a<1e3?"".concat(a.toFixed(2)," ms"):"".concat((a/1e3).toFixed(2)," s")})(d.performance.inference_time)," on ",d.performance.device)))):null};t(87);const Y=Object(c.a)({palette:{primary:{main:"#3f51b5"},secondary:{main:"#f50057"}},typography:{fontFamily:"Roboto, Arial, sans-serif"}});var q=function(){const[e,a]=Object(n.useState)(null),[t,l]=Object(n.useState)(""),[o,c]=Object(n.useState)(!1),[f,v]=Object(n.useState)(null),[h,y]=Object(n.useState)(null),[x,B]=Object(n.useState)({yolo:!1,detr:!1,vit:!1});return Object(n.useEffect)(()=>{fetch("/api/status").then(e=>e.json()).then(e=>{B(e.models)}).catch(e=>{console.error("Error checking API status:",e),y("Error connecting to the backend API. Please make sure the server is running.")})},[]),r.a.createElement(i.a,{theme:Y},r.a.createElement(s.a,{style:{flexGrow:1}},r.a.createElement(m.a,{position:"static"},r.a.createElement(d.a,null,r.a.createElement(p.a,{variant:"h6",style:{flexGrow:1}},"Multi-Model Object Detection Demo"))),r.a.createElement(g.a,{maxWidth:"lg",style:{marginTop:Y.spacing(4),marginBottom:Y.spacing(4)}},r.a.createElement(u.a,{container:!0,spacing:3},r.a.createElement(u.a,{item:!0,xs:12},r.a.createElement(E.a,{style:{padding:Y.spacing(2)}},r.a.createElement(p.a,{variant:"h5",gutterBottom:!0},"Upload an image to see how each model performs!"),r.a.createElement(p.a,{variant:"body1",paragraph:!0},"This demo showcases three different object detection and image classification models:"),r.a.createElement(p.a,{variant:"body1",component:"div"},r.a.createElement("ul",null,r.a.createElement("li",null,r.a.createElement("strong",null,"YOLOv8"),": Fast and accurate object detection"),r.a.createElement("li",null,r.a.createElement("strong",null,"DETR"),": DEtection TRansformer for object detection"),r.a.createElement("li",null,r.a.createElement("strong",null,"ViT"),": Vision Transformer for image classification"))))),r.a.createElement(u.a,{item:!0,xs:12,md:6},r.a.createElement(N,{onImageUpload:e=>{a(e),v(null),y(null)}})),r.a.createElement(u.a,{item:!0,xs:12,md:6},r.a.createElement(L,{onModelSelect:e=>{l(e),v(null),y(null)},onProcess:async()=>{if(!e||!t)return void y("Please select both an image and a model");c(!0),y(null);const a=new FormData;a.append("image",e);let n="";switch(t){case"yolo":n="/api/detect/yolo";break;case"detr":n="/api/detect/detr";break;case"vit":n="/api/classify/vit";break;default:return y("Invalid model selection"),void c(!1)}try{const e=await fetch(n,{method:"POST",body:a});if(!e.ok)throw new Error("HTTP error! Status: ".concat(e.status));const r=await e.json();v({model:t,data:r})}catch(r){console.error("Error processing image:",r),y("Error processing image: ".concat(r.message))}finally{c(!1)}},isProcessing:o,modelsStatus:x,selectedModel:t,imageSelected:!!e})),h&&r.a.createElement(u.a,{item:!0,xs:12},r.a.createElement(E.a,{style:{padding:Y.spacing(2),backgroundColor:"#ffebee"}},r.a.createElement(p.a,{color:"error"},h))),o&&r.a.createElement(u.a,{item:!0,xs:12,style:{textAlign:"center",margin:"".concat(Y.spacing(4),"px 0")}},r.a.createElement(b.a,null),r.a.createElement(p.a,{variant:"h6",style:{marginTop:Y.spacing(2)}},"Processing image...")),f&&r.a.createElement(r.a.Fragment,null,r.a.createElement(u.a,{item:!0,xs:12},r.a.createElement(H,{results:f})),r.a.createElement(u.a,{item:!0,xs:12},r.a.createElement(J,{visionResults:f.data,model:f.model})))))))};var Q=e=>{e&&e instanceof Function&&t.e(3).then(t.bind(null,137)).then(a=>{let{getCLS:t,getFID:n,getFCP:r,getLCP:l,getTTFB:o}=a;t(e),n(e),r(e),l(e),o(e)})};o.a.render(r.a.createElement(r.a.StrictMode,null,r.a.createElement(q,null)),document.getElementById("root")),Q()}},[[77,1,2]]]);
2
- //# sourceMappingURL=main.3d1593c5.chunk.js.map
 
 
 
frontend/build/static/js/main.3d1593c5.chunk.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["components/ImageUploader.js","components/ModelSelector.js","components/ResultDisplay.js","components/LlmAnalysis.js","App.js","reportWebVitals.js","index.js"],"names":["useStyles","makeStyles","theme","paper","padding","spacing","display","flexDirection","alignItems","height","minHeight","transition","dragActive","border","backgroundColor","dragInactive","uploadBox","justifyContent","width","cursor","uploadIcon","fontSize","color","marginBottom","supportText","marginTop","previewBox","position","imageContainer","overflow","deleteButton","top","right","ImageUploader","_ref","onImageUpload","classes","previewUrl","setPreviewUrl","useState","setDragActive","fileInputRef","useRef","handleDrag","e","preventDefault","stopPropagation","type","handleFiles","file","startsWith","URL","createObjectURL","alert","React","createElement","Paper","className","concat","onDragEnter","onDragLeave","onDragOver","onDrop","dataTransfer","files","ref","accept","onChange","target","style","Box","Typography","variant","gutterBottom","src","alt","IconButton","aria-label","onClick","handleRemoveImage","current","value","DeleteIcon","onButtonClick","click","CloudUploadIcon","Button","component","startIcon","card","selectedCard","unavailableCard","opacity","cardContent","flexGrow","chipContainer","successChip","errorChip","modelType","processButton","textAlign","ModelSelector","onModelSelect","onProcess","isProcessing","modelsStatus","selectedModel","imageSelected","models","id","name","description","icon","VisibilityIcon","available","yolo","detr","CategoryIcon","vit","handleModelClick","modelId","find","m","sx","p","Grid","container","map","model","item","xs","sm","key","Card","CardContent","mb","Chip","label","size","CardActions","disabled","fullWidth","PlayArrowIcon","resultImage","maxWidth","maxHeight","objectFit","dividerMargin","margin","gap","flexWrap","ResultDisplay","results","data","error","bgcolor","renderPerformanceInfo","performance","Divider","ms","undefined","isNaN","num","Number","toFixed","formatTime","inference_time","device","md","image","detections","length","List","detection","index","Fragment","ListItem","ListItemText","primary","class","confidence","marginLeft","secondary","bbox","join","top_predictions","prediction","rank","probability","responseBox","borderRadius","shape","whiteSpace","buttonProgress","LlmAnalysis","visionResults","userQuery","setUserQuery","isAnalyzing","setIsAnalyzing","analysisResult","setAnalysisResult","setError","TextField","placeholder","async","trim","response","fetch","method","headers","body","JSON","stringify","ok","Error","status","json","err","console","message","CircularProgress","mt","createMuiTheme","palette","main","typography","fontFamily","App","selectedImage","setSelectedImage","setSelectedModel","setIsProcessing","setResults","setModelsStatus","useEffect","then","catch","ThemeProvider","AppBar","Toolbar","Container","paragraph","formData","FormData","append","endpoint","reportWebVitals","onPerfEntry","Function","getCLS","getFID","getFCP","getLCP","getTTFB","ReactDOM","render","StrictMode","document","getElementById"],"mappings":"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","file":"static/js/main.3d1593c5.chunk.js","sourcesContent":["import React, { useState, useRef } from 'react';\nimport { \n Paper, \n Typography, \n Box, \n Button, \n IconButton \n} from '@material-ui/core';\nimport CloudUploadIcon from '@material-ui/icons/CloudUpload';\nimport DeleteIcon from '@material-ui/icons/Delete';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n paper: {\n padding: theme.spacing(2),\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n height: '100%',\n minHeight: 300,\n transition: 'all 0.3s ease'\n },\n dragActive: {\n border: '2px dashed #3f51b5',\n backgroundColor: 'rgba(63, 81, 181, 0.05)'\n },\n dragInactive: {\n border: '2px dashed #ccc',\n backgroundColor: 'white'\n },\n uploadBox: {\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n height: '100%',\n width: '100%',\n cursor: 'pointer'\n },\n uploadIcon: {\n fontSize: 60,\n color: '#3f51b5',\n marginBottom: theme.spacing(2)\n },\n supportText: {\n marginTop: theme.spacing(2)\n },\n previewBox: {\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n width: '100%',\n height: '100%',\n position: 'relative'\n },\n imageContainer: {\n position: 'relative',\n width: '100%',\n height: '100%',\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n overflow: 'hidden',\n marginTop: theme.spacing(2)\n },\n deleteButton: {\n position: 'absolute',\n top: 0,\n right: 0,\n backgroundColor: 'rgba(255, 255, 255, 0.7)',\n '&:hover': {\n backgroundColor: 'rgba(255, 255, 255, 0.9)',\n }\n }\n}));\n\nconst ImageUploader = ({ onImageUpload }) => {\n const classes = useStyles();\n const [previewUrl, setPreviewUrl] = useState(null);\n const [dragActive, setDragActive] = useState(false);\n const fileInputRef = useRef(null);\n\n const handleDrag = (e) => {\n e.preventDefault();\n e.stopPropagation();\n if (e.type === 'dragenter' || e.type === 'dragover') {\n setDragActive(true);\n } else if (e.type === 'dragleave') {\n setDragActive(false);\n }\n };\n\n const handleDrop = (e) => {\n e.preventDefault();\n e.stopPropagation();\n setDragActive(false);\n if (e.dataTransfer.files && e.dataTransfer.files[0]) {\n handleFiles(e.dataTransfer.files[0]);\n }\n };\n\n const handleChange = (e) => {\n e.preventDefault();\n if (e.target.files && e.target.files[0]) {\n handleFiles(e.target.files[0]);\n }\n };\n\n const handleFiles = (file) => {\n if (file.type.startsWith('image/')) {\n setPreviewUrl(URL.createObjectURL(file));\n onImageUpload(file);\n } else {\n alert('Please upload an image file');\n }\n };\n\n const onButtonClick = () => {\n fileInputRef.current.click();\n };\n\n const handleRemoveImage = () => {\n setPreviewUrl(null);\n onImageUpload(null);\n fileInputRef.current.value = \"\";\n };\n\n return (\n <Paper \n className={`${classes.paper} ${dragActive ? classes.dragActive : classes.dragInactive}`}\n onDragEnter={handleDrag}\n onDragLeave={handleDrag}\n onDragOver={handleDrag}\n onDrop={handleDrop}\n >\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*\"\n onChange={handleChange}\n style={{ display: 'none' }}\n />\n\n {!previewUrl ? (\n <Box \n className={classes.uploadBox}\n onClick={onButtonClick}\n >\n <CloudUploadIcon className={classes.uploadIcon} />\n <Typography variant=\"h6\" gutterBottom>\n Drag & Drop an image here\n </Typography>\n <Typography variant=\"body2\" color=\"textSecondary\" gutterBottom>\n or\n </Typography>\n <Button\n variant=\"contained\"\n color=\"primary\"\n component=\"span\"\n startIcon={<CloudUploadIcon />}\n >\n Browse Files\n </Button>\n <Typography variant=\"body2\" color=\"textSecondary\" className={classes.supportText}>\n Supported formats: JPG, PNG, GIF\n </Typography>\n </Box>\n ) : (\n <Box className={classes.previewBox}>\n <Typography variant=\"h6\" gutterBottom>\n Preview\n </Typography>\n <Box className={classes.imageContainer}>\n <img\n src={previewUrl}\n alt=\"Preview\"\n className=\"preview-image\"\n />\n <IconButton\n aria-label=\"delete\"\n className={classes.deleteButton}\n onClick={handleRemoveImage}\n >\n <DeleteIcon />\n </IconButton>\n </Box>\n </Box>\n )}\n </Paper>\n );\n};\n\nexport default ImageUploader;\n","import React from 'react';\nimport { \n Grid, \n Card, \n CardContent, \n CardActions, \n Typography, \n Button, \n Chip,\n Box\n} from '@material-ui/core';\nimport VisibilityIcon from '@material-ui/icons/Visibility';\nimport CategoryIcon from '@material-ui/icons/Category';\nimport PlayArrowIcon from '@material-ui/icons/PlayArrow';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n card: {\n height: '100%',\n display: 'flex',\n flexDirection: 'column',\n },\n selectedCard: {\n border: '2px solid #3f51b5',\n },\n unavailableCard: {\n opacity: 0.6,\n },\n cardContent: {\n flexGrow: 1,\n },\n chipContainer: {\n marginBottom: theme.spacing(1.5),\n },\n successChip: {\n backgroundColor: '#34C759',\n color: '#fff',\n },\n errorChip: {\n backgroundColor: '#FF3B3F',\n color: '#fff',\n },\n modelType: {\n marginTop: theme.spacing(1),\n },\n processButton: {\n marginTop: theme.spacing(3),\n textAlign: 'center',\n }\n}));\n\nconst ModelSelector = ({ \n onModelSelect, \n onProcess, \n isProcessing, \n modelsStatus, \n selectedModel,\n imageSelected \n}) => {\n const classes = useStyles();\n \n const models = [\n {\n id: 'yolo',\n name: 'YOLOv8',\n description: 'Fast and accurate object detection',\n icon: <VisibilityIcon />,\n available: modelsStatus.yolo\n },\n {\n id: 'detr',\n name: 'DETR',\n description: 'DEtection TRansformer for object detection',\n icon: <VisibilityIcon />,\n available: modelsStatus.detr\n },\n {\n id: 'vit',\n name: 'ViT',\n description: 'Vision Transformer for image classification',\n icon: <CategoryIcon />,\n available: modelsStatus.vit\n }\n ];\n\n const handleModelClick = (modelId) => {\n if (models.find(m => m.id === modelId).available) {\n onModelSelect(modelId);\n }\n };\n\n return (\n <Box sx={{ p: 2, height: '100%' }}>\n <Typography variant=\"h6\" gutterBottom>\n Select Model\n </Typography>\n \n <Grid container spacing={2}>\n {models.map((model) => (\n <Grid item xs={12} sm={4} key={model.id}>\n <Card \n className={`\n ${classes.card} \n ${selectedModel === model.id ? classes.selectedCard : ''} \n ${!model.available ? classes.unavailableCard : ''}\n `}\n onClick={() => handleModelClick(model.id)}\n >\n <CardContent className={classes.cardContent}>\n <Box sx={{ mb: 2, color: 'primary' }}>\n {model.icon}\n </Box>\n <Typography variant=\"h5\" component=\"div\" gutterBottom>\n {model.name}\n </Typography>\n <div className={classes.chipContainer}>\n {model.available ? (\n <Chip \n label=\"Available\" \n className={classes.successChip}\n size=\"small\" \n />\n ) : (\n <Chip \n label=\"Not Available\" \n className={classes.errorChip}\n size=\"small\" \n />\n )}\n </div>\n <Typography variant=\"body2\" color=\"textSecondary\">\n {model.description}\n </Typography>\n </CardContent>\n <CardActions>\n <Button \n size=\"small\" \n onClick={() => handleModelClick(model.id)}\n disabled={!model.available}\n color={selectedModel === model.id ? \"primary\" : \"default\"}\n variant={selectedModel === model.id ? \"contained\" : \"outlined\"}\n fullWidth\n >\n {selectedModel === model.id ? 'Selected' : 'Select'}\n </Button>\n </CardActions>\n </Card>\n </Grid>\n ))}\n </Grid>\n\n <div className={classes.processButton}>\n <Button\n variant=\"contained\"\n color=\"primary\"\n size=\"large\"\n startIcon={<PlayArrowIcon />}\n onClick={onProcess}\n disabled={!selectedModel || !imageSelected || isProcessing}\n >\n {isProcessing ? 'Processing...' : 'Process Image'}\n </Button>\n </div>\n </Box>\n );\n};\n\nexport default ModelSelector;\n","import React from 'react';\nimport { \n Paper, \n Typography, \n Box, \n List, \n ListItem, \n ListItemText, \n Divider,\n Grid,\n Chip\n} from '@material-ui/core';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n paper: {\n padding: theme.spacing(2)\n },\n marginBottom: {\n marginBottom: theme.spacing(2)\n },\n resultImage: {\n maxWidth: '100%',\n maxHeight: '400px',\n objectFit: 'contain'\n },\n dividerMargin: {\n margin: `${theme.spacing(2)}px 0`\n },\n chipContainer: {\n display: 'flex',\n gap: theme.spacing(1),\n flexWrap: 'wrap'\n }\n}));\n\nconst ResultDisplay = ({ results }) => {\n const classes = useStyles();\n if (!results) return null;\n \n const { model, data } = results;\n \n // Helper to format times nicely\n const formatTime = (ms) => {\n if (ms === undefined || ms === null || isNaN(ms)) return '-';\n const num = Number(ms);\n if (num < 1000) return `${num.toFixed(2)} ms`;\n return `${(num / 1000).toFixed(2)} s`;\n };\n \n // Check if there's an error\n if (data.error) {\n return (\n <Paper sx={{ p: 2, bgcolor: '#ffebee' }}>\n <Typography color=\"error\">{data.error}</Typography>\n </Paper>\n );\n }\n\n // Display performance info\n const renderPerformanceInfo = () => {\n if (!data.performance) return null;\n \n return (\n <Box className=\"performance-info\">\n <Divider className={classes.dividerMargin} />\n <Typography variant=\"body2\">\n Inference time: {formatTime(data.performance.inference_time)} on {data.performance.device}\n </Typography>\n </Box>\n );\n };\n\n // Render for YOLO and DETR (object detection)\n if (model === 'yolo' || model === 'detr') {\n return (\n <Paper className={classes.paper}>\n <Typography variant=\"h6\" gutterBottom>\n {model === 'yolo' ? 'YOLOv8' : 'DETR'} Detection Results\n </Typography>\n \n <Grid container spacing={3}>\n <Grid item xs={12} md={6}>\n {data.image && (\n <Box className={classes.marginBottom}>\n <Typography variant=\"subtitle1\" gutterBottom>\n Detection Result\n </Typography>\n <img \n src={`data:image/png;base64,${data.image}`} \n alt=\"Detection Result\" \n className={classes.resultImage}\n />\n </Box>\n )}\n </Grid>\n \n <Grid item xs={12} md={6}>\n <Box className={classes.marginBottom}>\n <Typography variant=\"subtitle1\" gutterBottom>\n Detected Objects:\n </Typography>\n \n {data.detections && data.detections.length > 0 ? (\n <List>\n {data.detections.map((detection, index) => (\n <React.Fragment key={index}>\n <ListItem>\n <ListItemText \n primary={\n <Box style={{ display: 'flex', alignItems: 'center' }}>\n <Typography variant=\"body1\" component=\"span\">\n {detection.class}\n </Typography>\n <Chip \n label={`${(detection.confidence * 100).toFixed(0)}%`}\n size=\"small\"\n color=\"primary\"\n style={{ marginLeft: 8 }}\n />\n </Box>\n } \n secondary={`Bounding Box: [${detection.bbox.join(', ')}]`} \n />\n </ListItem>\n {index < data.detections.length - 1 && <Divider />}\n </React.Fragment>\n ))}\n </List>\n ) : (\n <Typography variant=\"body1\">No objects detected</Typography>\n )}\n </Box>\n </Grid>\n </Grid>\n \n {renderPerformanceInfo()}\n </Paper>\n );\n }\n \n // Render for ViT (classification)\n if (model === 'vit') {\n return (\n <Paper className={classes.paper}>\n <Typography variant=\"h6\" gutterBottom>\n ViT Classification Results\n </Typography>\n \n <Typography variant=\"subtitle1\" gutterBottom>\n Top Predictions:\n </Typography>\n \n {data.top_predictions && data.top_predictions.length > 0 ? (\n <List>\n {data.top_predictions.map((prediction, index) => (\n <React.Fragment key={index}>\n <ListItem>\n <ListItemText \n primary={\n <Box style={{ display: 'flex', alignItems: 'center' }}>\n <Typography variant=\"body1\" component=\"span\">\n {prediction.rank}. {prediction.class}\n </Typography>\n <Chip \n label={`${(prediction.probability * 100).toFixed(1)}%`}\n size=\"small\"\n color={index === 0 ? \"primary\" : \"default\"}\n style={{ marginLeft: 8 }}\n />\n </Box>\n } \n />\n </ListItem>\n {index < data.top_predictions.length - 1 && <Divider />}\n </React.Fragment>\n ))}\n </List>\n ) : (\n <Typography variant=\"body1\">No classifications available</Typography>\n )}\n \n {renderPerformanceInfo()}\n </Paper>\n );\n }\n \n return null;\n};\n\nexport default ResultDisplay;\n","import React, { useState } from 'react';\nimport { \n Paper, \n Typography, \n Box, \n TextField, \n Button, \n CircularProgress,\n Divider\n} from '@material-ui/core';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n paper: {\n padding: theme.spacing(2),\n marginTop: theme.spacing(2)\n },\n marginBottom: {\n marginBottom: theme.spacing(2)\n },\n dividerMargin: {\n margin: `${theme.spacing(2)}px 0`\n },\n responseBox: {\n padding: theme.spacing(2),\n backgroundColor: '#f5f5f5',\n borderRadius: theme.shape.borderRadius,\n marginTop: theme.spacing(2),\n whiteSpace: 'pre-wrap'\n },\n buttonProgress: {\n marginLeft: theme.spacing(1)\n }\n}));\n\nconst LlmAnalysis = ({ visionResults, model }) => {\n const classes = useStyles();\n const [userQuery, setUserQuery] = useState('');\n const [isAnalyzing, setIsAnalyzing] = useState(false);\n const [analysisResult, setAnalysisResult] = useState(null);\n const [error, setError] = useState(null);\n\n // Format time for display\n const formatTime = (ms) => {\n if (ms === undefined || ms === null || isNaN(ms)) return '-';\n const num = Number(ms);\n if (num < 1000) return `${num.toFixed(2)} ms`;\n return `${(num / 1000).toFixed(2)} s`;\n };\n\n const handleAnalyze = async () => {\n if (!userQuery.trim()) return;\n \n setIsAnalyzing(true);\n setError(null);\n \n try {\n const response = await fetch('/api/analyze', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n visionResults: visionResults,\n userQuery: userQuery\n }),\n });\n\n if (!response.ok) {\n throw new Error(`HTTP error! Status: ${response.status}`);\n }\n\n const data = await response.json();\n \n if (data.error) {\n setError(data.error);\n } else {\n setAnalysisResult(data);\n }\n } catch (err) {\n console.error('Error analyzing with LLM:', err);\n setError(`Error analyzing with LLM: ${err.message}`);\n } finally {\n setIsAnalyzing(false);\n }\n };\n\n if (!visionResults) return null;\n\n return (\n <Paper className={classes.paper}>\n <Typography variant=\"h6\" gutterBottom>\n Ask AI about the {model === 'vit' ? 'Classification' : 'Detection'} Results\n </Typography>\n \n <Typography variant=\"body2\" className={classes.marginBottom}>\n Ask a question about the detected objects or classifications to get an AI-powered analysis.\n </Typography>\n \n <TextField\n fullWidth\n label=\"Your question about the image\"\n variant=\"outlined\"\n value={userQuery}\n onChange={(e) => setUserQuery(e.target.value)}\n disabled={isAnalyzing}\n className={classes.marginBottom}\n placeholder={model === 'vit' \n ? \"E.g., What category does this image belong to?\" \n : \"E.g., How many people are in this image?\"}\n />\n \n <Button \n variant=\"contained\" \n color=\"primary\"\n onClick={handleAnalyze}\n disabled={isAnalyzing || !userQuery.trim()}\n >\n Analyze with AI\n {isAnalyzing && <CircularProgress size={24} className={classes.buttonProgress} />}\n </Button>\n \n {error && (\n <Box mt={2}>\n <Typography color=\"error\">{error}</Typography>\n </Box>\n )}\n \n {analysisResult && (\n <>\n <Divider className={classes.dividerMargin} />\n \n <Typography variant=\"subtitle1\" gutterBottom>\n AI Analysis:\n </Typography>\n \n <Box className={classes.responseBox}>\n <Typography variant=\"body1\">\n {analysisResult.response}\n </Typography>\n </Box>\n \n {analysisResult.performance && (\n <Box mt={1}>\n <Typography variant=\"body2\" color=\"textSecondary\">\n Analysis time: {formatTime(analysisResult.performance.inference_time)} on {analysisResult.performance.device}\n </Typography>\n </Box>\n )}\n </>\n )}\n </Paper>\n );\n};\n\nexport default LlmAnalysis;\n","import React, { useState, useEffect } from 'react';\nimport { \n Container, \n Typography, \n Box, \n Paper, \n Grid, \n CircularProgress,\n AppBar,\n Toolbar,\n ThemeProvider,\n createMuiTheme\n} from '@material-ui/core';\nimport ImageUploader from './components/ImageUploader';\nimport ModelSelector from './components/ModelSelector';\nimport ResultDisplay from './components/ResultDisplay';\nimport LlmAnalysis from './components/LlmAnalysis';\nimport './App.css';\n\n// Create a theme\nconst theme = createMuiTheme({\n palette: {\n primary: {\n main: '#3f51b5',\n },\n secondary: {\n main: '#f50057',\n },\n },\n typography: {\n fontFamily: 'Roboto, Arial, sans-serif',\n },\n});\n\nfunction App() {\n const [selectedImage, setSelectedImage] = useState(null);\n const [selectedModel, setSelectedModel] = useState('');\n const [isProcessing, setIsProcessing] = useState(false);\n const [results, setResults] = useState(null);\n const [error, setError] = useState(null);\n const [modelsStatus, setModelsStatus] = useState({\n yolo: false,\n detr: false,\n vit: false\n });\n\n // Check API status on component mount\n useEffect(() => {\n fetch('/api/status')\n .then(response => response.json())\n .then(data => {\n setModelsStatus(data.models);\n })\n .catch(err => {\n console.error('Error checking API status:', err);\n setError('Error connecting to the backend API. Please make sure the server is running.');\n });\n }, []);\n\n const handleImageUpload = (image) => {\n setSelectedImage(image);\n setResults(null);\n setError(null);\n };\n\n const handleModelSelect = (model) => {\n setSelectedModel(model);\n setResults(null);\n setError(null);\n };\n\n const processImage = async () => {\n if (!selectedImage || !selectedModel) {\n setError('Please select both an image and a model');\n return;\n }\n\n setIsProcessing(true);\n setError(null);\n\n // Create form data for the image\n const formData = new FormData();\n formData.append('image', selectedImage);\n\n let endpoint = '';\n switch (selectedModel) {\n case 'yolo':\n endpoint = '/api/detect/yolo';\n break;\n case 'detr':\n endpoint = '/api/detect/detr';\n break;\n case 'vit':\n endpoint = '/api/classify/vit';\n break;\n default:\n setError('Invalid model selection');\n setIsProcessing(false);\n return;\n }\n\n try {\n const response = await fetch(endpoint, {\n method: 'POST',\n body: formData,\n });\n\n if (!response.ok) {\n throw new Error(`HTTP error! Status: ${response.status}`);\n }\n\n const data = await response.json();\n setResults({ model: selectedModel, data });\n } catch (err) {\n console.error('Error processing image:', err);\n setError(`Error processing image: ${err.message}`);\n } finally {\n setIsProcessing(false);\n }\n };\n\n return (\n <ThemeProvider theme={theme}>\n <Box style={{ flexGrow: 1 }}>\n <AppBar position=\"static\">\n <Toolbar>\n <Typography variant=\"h6\" style={{ flexGrow: 1 }}>\n Multi-Model Object Detection Demo\n </Typography>\n </Toolbar>\n </AppBar>\n <Container maxWidth=\"lg\" style={{ marginTop: theme.spacing(4), marginBottom: theme.spacing(4) }}>\n <Grid container spacing={3}>\n <Grid item xs={12}>\n <Paper style={{ padding: theme.spacing(2) }}>\n <Typography variant=\"h5\" gutterBottom>\n Upload an image to see how each model performs!\n </Typography>\n <Typography variant=\"body1\" paragraph>\n This demo showcases three different object detection and image classification models:\n </Typography>\n <Typography variant=\"body1\" component=\"div\">\n <ul>\n <li><strong>YOLOv8</strong>: Fast and accurate object detection</li>\n <li><strong>DETR</strong>: DEtection TRansformer for object detection</li>\n <li><strong>ViT</strong>: Vision Transformer for image classification</li>\n </ul>\n </Typography>\n </Paper>\n </Grid>\n \n <Grid item xs={12} md={6}>\n <ImageUploader onImageUpload={handleImageUpload} />\n </Grid>\n \n <Grid item xs={12} md={6}>\n <ModelSelector \n onModelSelect={handleModelSelect} \n onProcess={processImage}\n isProcessing={isProcessing}\n modelsStatus={modelsStatus}\n selectedModel={selectedModel}\n imageSelected={!!selectedImage}\n />\n </Grid>\n \n {error && (\n <Grid item xs={12}>\n <Paper style={{ padding: theme.spacing(2), backgroundColor: '#ffebee' }}>\n <Typography color=\"error\">{error}</Typography>\n </Paper>\n </Grid>\n )}\n \n {isProcessing && (\n <Grid item xs={12} style={{ textAlign: 'center', margin: `${theme.spacing(4)}px 0` }}>\n <CircularProgress />\n <Typography variant=\"h6\" style={{ marginTop: theme.spacing(2) }}>\n Processing image...\n </Typography>\n </Grid>\n )}\n \n {results && (\n <>\n <Grid item xs={12}>\n <ResultDisplay results={results} />\n </Grid>\n <Grid item xs={12}>\n <LlmAnalysis visionResults={results.data} model={results.model} />\n </Grid>\n </>\n )}\n </Grid>\n </Container>\n </Box>\n </ThemeProvider>\n );\n}\n\nexport default App;\n","const reportWebVitals = (onPerfEntry) => {\n if (onPerfEntry && onPerfEntry instanceof Function) {\n import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {\n getCLS(onPerfEntry);\n getFID(onPerfEntry);\n getFCP(onPerfEntry);\n getLCP(onPerfEntry);\n getTTFB(onPerfEntry);\n });\n }\n};\n\nexport default reportWebVitals;\n","import React from 'react';\nimport ReactDOM from 'react-dom';\nimport './index.css';\nimport App from './App';\nimport reportWebVitals from './reportWebVitals';\n\nReactDOM.render(\n <React.StrictMode>\n <App />\n </React.StrictMode>,\n document.getElementById('root')\n);\n\n// If you want to start measuring performance in your app, pass a function\n// to log results (for example: reportWebVitals(console.log))\n// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals\nreportWebVitals();\n"],"sourceRoot":""}
 
 
frontend/build/static/js/runtime-main.ab7e4402.js DELETED
@@ -1,2 +0,0 @@
1
- !function(e){function r(r){for(var n,i,a=r[0],c=r[1],l=r[2],p=0,s=[];p<a.length;p++)i=a[p],Object.prototype.hasOwnProperty.call(o,i)&&o[i]&&s.push(o[i][0]),o[i]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(f&&f(r);s.length;)s.shift()();return u.push.apply(u,l||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,a=1;a<t.length;a++){var c=t[a];0!==o[c]&&(n=!1)}n&&(u.splice(r--,1),e=i(i.s=t[0]))}return e}var n={},o={1:0},u=[];function i(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,i),t.l=!0,t.exports}i.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var u,a=document.createElement("script");a.charset="utf-8",a.timeout=120,i.nc&&a.setAttribute("nonce",i.nc),a.src=function(e){return i.p+"static/js/"+({}[e]||e)+"."+{3:"0e3ce0f8"}[e]+".chunk.js"}(e);var c=new Error;u=function(r){a.onerror=a.onload=null,clearTimeout(l);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),u=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,t[1](c)}o[e]=void 0}};var l=setTimeout((function(){u({type:"timeout",target:a})}),12e4);a.onerror=a.onload=u,document.head.appendChild(a)}return Promise.all(r)},i.m=e,i.c=n,i.d=function(e,r,t){i.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},i.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,r){if(1&r&&(e=i(e)),8&r)return e;if(4&r&&"object"===typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(i.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)i.d(t,n,function(r){return e[r]}.bind(null,n));return t},i.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(r,"a",r),r},i.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},i.p="/",i.oe=function(e){throw console.error(e),e};var a=this["webpackJsonpvision-web-app"]=this["webpackJsonpvision-web-app"]||[],c=a.push.bind(a);a.push=r,a=a.slice();for(var l=0;l<a.length;l++)r(a[l]);var f=c;t()}([]);
2
- //# sourceMappingURL=runtime-main.ab7e4402.js.map
 
 
 
frontend/build/static/js/runtime-main.ab7e4402.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../webpack/bootstrap"],"names":["webpackJsonpCallback","data","moduleId","chunkId","chunkIds","moreModules","executeModules","i","resolves","length","Object","prototype","hasOwnProperty","call","installedChunks","push","modules","parentJsonpFunction","shift","deferredModules","apply","checkDeferredModules","result","deferredModule","fulfilled","j","depId","splice","__webpack_require__","s","installedModules","1","exports","module","l","e","promises","installedChunkData","promise","Promise","resolve","reject","onScriptComplete","script","document","createElement","charset","timeout","nc","setAttribute","src","p","jsonpScriptSrc","error","Error","event","onerror","onload","clearTimeout","chunk","errorType","type","realSrc","target","message","name","request","undefined","setTimeout","head","appendChild","all","m","c","d","getter","o","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","oe","err","console","jsonpArray","this","oldJsonpFunction","slice"],"mappings":"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","file":"static/js/runtime-main.ab7e4402.js","sourcesContent":[" \t// install a JSONP callback for chunk loading\n \tfunction webpackJsonpCallback(data) {\n \t\tvar chunkIds = data[0];\n \t\tvar moreModules = data[1];\n \t\tvar executeModules = data[2];\n\n \t\t// add \"moreModules\" to the modules object,\n \t\t// then flag all \"chunkIds\" as loaded and fire callback\n \t\tvar moduleId, chunkId, i = 0, resolves = [];\n \t\tfor(;i < chunkIds.length; i++) {\n \t\t\tchunkId = chunkIds[i];\n \t\t\tif(Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) {\n \t\t\t\tresolves.push(installedChunks[chunkId][0]);\n \t\t\t}\n \t\t\tinstalledChunks[chunkId] = 0;\n \t\t}\n \t\tfor(moduleId in moreModules) {\n \t\t\tif(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {\n \t\t\t\tmodules[moduleId] = moreModules[moduleId];\n \t\t\t}\n \t\t}\n \t\tif(parentJsonpFunction) parentJsonpFunction(data);\n\n \t\twhile(resolves.length) {\n \t\t\tresolves.shift()();\n \t\t}\n\n \t\t// add entry modules from loaded chunk to deferred list\n \t\tdeferredModules.push.apply(deferredModules, executeModules || []);\n\n \t\t// run deferred modules when all chunks ready\n \t\treturn checkDeferredModules();\n \t};\n \tfunction checkDeferredModules() {\n \t\tvar result;\n \t\tfor(var i = 0; i < deferredModules.length; i++) {\n \t\t\tvar deferredModule = deferredModules[i];\n \t\t\tvar fulfilled = true;\n \t\t\tfor(var j = 1; j < deferredModule.length; j++) {\n \t\t\t\tvar depId = deferredModule[j];\n \t\t\t\tif(installedChunks[depId] !== 0) fulfilled = false;\n \t\t\t}\n \t\t\tif(fulfilled) {\n \t\t\t\tdeferredModules.splice(i--, 1);\n \t\t\t\tresult = __webpack_require__(__webpack_require__.s = deferredModule[0]);\n \t\t\t}\n \t\t}\n\n \t\treturn result;\n \t}\n\n \t// The module cache\n \tvar installedModules = {};\n\n \t// object to store loaded and loading chunks\n \t// undefined = chunk not loaded, null = chunk preloaded/prefetched\n \t// Promise = chunk loading, 0 = chunk loaded\n \tvar installedChunks = {\n \t\t1: 0\n \t};\n\n \tvar deferredModules = [];\n\n \t// script path function\n \tfunction jsonpScriptSrc(chunkId) {\n \t\treturn __webpack_require__.p + \"static/js/\" + ({}[chunkId]||chunkId) + \".\" + {\"3\":\"0e3ce0f8\"}[chunkId] + \".chunk.js\"\n \t}\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n \t// This file contains only the entry chunk.\n \t// The chunk loading function for additional chunks\n \t__webpack_require__.e = function requireEnsure(chunkId) {\n \t\tvar promises = [];\n\n\n \t\t// JSONP chunk loading for javascript\n\n \t\tvar installedChunkData = installedChunks[chunkId];\n \t\tif(installedChunkData !== 0) { // 0 means \"already installed\".\n\n \t\t\t// a Promise means \"currently loading\".\n \t\t\tif(installedChunkData) {\n \t\t\t\tpromises.push(installedChunkData[2]);\n \t\t\t} else {\n \t\t\t\t// setup Promise in chunk cache\n \t\t\t\tvar promise = new Promise(function(resolve, reject) {\n \t\t\t\t\tinstalledChunkData = installedChunks[chunkId] = [resolve, reject];\n \t\t\t\t});\n \t\t\t\tpromises.push(installedChunkData[2] = promise);\n\n \t\t\t\t// start chunk loading\n \t\t\t\tvar script = document.createElement('script');\n \t\t\t\tvar onScriptComplete;\n\n \t\t\t\tscript.charset = 'utf-8';\n \t\t\t\tscript.timeout = 120;\n \t\t\t\tif (__webpack_require__.nc) {\n \t\t\t\t\tscript.setAttribute(\"nonce\", __webpack_require__.nc);\n \t\t\t\t}\n \t\t\t\tscript.src = jsonpScriptSrc(chunkId);\n\n \t\t\t\t// create error before stack unwound to get useful stacktrace later\n \t\t\t\tvar error = new Error();\n \t\t\t\tonScriptComplete = function (event) {\n \t\t\t\t\t// avoid mem leaks in IE.\n \t\t\t\t\tscript.onerror = script.onload = null;\n \t\t\t\t\tclearTimeout(timeout);\n \t\t\t\t\tvar chunk = installedChunks[chunkId];\n \t\t\t\t\tif(chunk !== 0) {\n \t\t\t\t\t\tif(chunk) {\n \t\t\t\t\t\t\tvar errorType = event && (event.type === 'load' ? 'missing' : event.type);\n \t\t\t\t\t\t\tvar realSrc = event && event.target && event.target.src;\n \t\t\t\t\t\t\terror.message = 'Loading chunk ' + chunkId + ' failed.\\n(' + errorType + ': ' + realSrc + ')';\n \t\t\t\t\t\t\terror.name = 'ChunkLoadError';\n \t\t\t\t\t\t\terror.type = errorType;\n \t\t\t\t\t\t\terror.request = realSrc;\n \t\t\t\t\t\t\tchunk[1](error);\n \t\t\t\t\t\t}\n \t\t\t\t\t\tinstalledChunks[chunkId] = undefined;\n \t\t\t\t\t}\n \t\t\t\t};\n \t\t\t\tvar timeout = setTimeout(function(){\n \t\t\t\t\tonScriptComplete({ type: 'timeout', target: script });\n \t\t\t\t}, 120000);\n \t\t\t\tscript.onerror = script.onload = onScriptComplete;\n \t\t\t\tdocument.head.appendChild(script);\n \t\t\t}\n \t\t}\n \t\treturn Promise.all(promises);\n \t};\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"/\";\n\n \t// on error function for async loading\n \t__webpack_require__.oe = function(err) { console.error(err); throw err; };\n\n \tvar jsonpArray = this[\"webpackJsonpvision-web-app\"] = this[\"webpackJsonpvision-web-app\"] || [];\n \tvar oldJsonpFunction = jsonpArray.push.bind(jsonpArray);\n \tjsonpArray.push = webpackJsonpCallback;\n \tjsonpArray = jsonpArray.slice();\n \tfor(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);\n \tvar parentJsonpFunction = oldJsonpFunction;\n\n\n \t// run deferred modules from other chunks\n \tcheckDeferredModules();\n"],"sourceRoot":""}