Spaces:
Running
Running
David Ko
commited on
Commit
·
96aa590
1
Parent(s):
ea52b48
fix(frontend): force preview <img> to contain within container; rebuild & sync CRA assets
Browse files- frontend/src/components/ImageUploader.js +7 -0
- static/asset-manifest.json +4 -4
- static/index.html +1 -1
- static/{precache-manifest.7ffa256e4ee99cb22c2ef5bb2f02bd1a.js → precache-manifest.4683f6cd857bd9aa85304ac13a82d061.js} +4 -4
- static/service-worker.js +1 -1
- static/static/js/main.13ed4cd9.chunk.js +0 -2
- static/static/js/main.13ed4cd9.chunk.js.map +0 -1
- static/static/js/main.fc0e2dc7.chunk.js +2 -0
- static/static/js/main.fc0e2dc7.chunk.js.map +1 -0
frontend/src/components/ImageUploader.js
CHANGED
@@ -179,6 +179,13 @@ const ImageUploader = ({ onImageUpload }) => {
|
|
179 |
src={previewUrl}
|
180 |
alt="Preview"
|
181 |
className="preview-image"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
182 |
/>
|
183 |
<IconButton
|
184 |
aria-label="delete"
|
|
|
179 |
src={previewUrl}
|
180 |
alt="Preview"
|
181 |
className="preview-image"
|
182 |
+
style={{
|
183 |
+
width: '100%',
|
184 |
+
height: 'auto',
|
185 |
+
maxHeight: '100%',
|
186 |
+
objectFit: 'contain',
|
187 |
+
display: 'block'
|
188 |
+
}}
|
189 |
/>
|
190 |
<IconButton
|
191 |
aria-label="delete"
|
static/asset-manifest.json
CHANGED
@@ -1,8 +1,8 @@
|
|
1 |
{
|
2 |
"files": {
|
3 |
"main.css": "/static/css/main.3b4eede1.chunk.css",
|
4 |
-
"main.js": "/static/js/main.
|
5 |
-
"main.js.map": "/static/js/main.
|
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.9b5b80e8.chunk.js": "/static/js/2.9b5b80e8.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.
|
14 |
"service-worker.js": "/service-worker.js",
|
15 |
"static/css/main.3b4eede1.chunk.css.map": "/static/css/main.3b4eede1.chunk.css.map",
|
16 |
"static/js/2.9b5b80e8.chunk.js.LICENSE.txt": "/static/js/2.9b5b80e8.chunk.js.LICENSE.txt"
|
@@ -19,6 +19,6 @@
|
|
19 |
"static/js/runtime-main.25710301.js",
|
20 |
"static/js/2.9b5b80e8.chunk.js",
|
21 |
"static/css/main.3b4eede1.chunk.css",
|
22 |
-
"static/js/main.
|
23 |
]
|
24 |
}
|
|
|
1 |
{
|
2 |
"files": {
|
3 |
"main.css": "/static/css/main.3b4eede1.chunk.css",
|
4 |
+
"main.js": "/static/js/main.fc0e2dc7.chunk.js",
|
5 |
+
"main.js.map": "/static/js/main.fc0e2dc7.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.9b5b80e8.chunk.js": "/static/js/2.9b5b80e8.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.4683f6cd857bd9aa85304ac13a82d061.js": "/precache-manifest.4683f6cd857bd9aa85304ac13a82d061.js",
|
14 |
"service-worker.js": "/service-worker.js",
|
15 |
"static/css/main.3b4eede1.chunk.css.map": "/static/css/main.3b4eede1.chunk.css.map",
|
16 |
"static/js/2.9b5b80e8.chunk.js.LICENSE.txt": "/static/js/2.9b5b80e8.chunk.js.LICENSE.txt"
|
|
|
19 |
"static/js/runtime-main.25710301.js",
|
20 |
"static/js/2.9b5b80e8.chunk.js",
|
21 |
"static/css/main.3b4eede1.chunk.css",
|
22 |
+
"static/js/main.fc0e2dc7.chunk.js"
|
23 |
]
|
24 |
}
|
static/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.3b4eede1.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.9b5b80e8.chunk.js"></script><script src="/static/js/main.
|
|
|
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.3b4eede1.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.9b5b80e8.chunk.js"></script><script src="/static/js/main.fc0e2dc7.chunk.js"></script></body></html>
|
static/{precache-manifest.7ffa256e4ee99cb22c2ef5bb2f02bd1a.js → precache-manifest.4683f6cd857bd9aa85304ac13a82d061.js}
RENAMED
@@ -1,10 +1,10 @@
|
|
1 |
self.__precacheManifest = (self.__precacheManifest || []).concat([
|
2 |
{
|
3 |
-
"revision": "
|
4 |
"url": "/index.html"
|
5 |
},
|
6 |
{
|
7 |
-
"revision": "
|
8 |
"url": "/static/css/main.3b4eede1.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": "
|
24 |
-
"url": "/static/js/main.
|
25 |
},
|
26 |
{
|
27 |
"revision": "d8c310b0ac7ffa6d8151",
|
|
|
1 |
self.__precacheManifest = (self.__precacheManifest || []).concat([
|
2 |
{
|
3 |
+
"revision": "d54f7d4646b42d5859a3c60dc17c2cbc",
|
4 |
"url": "/index.html"
|
5 |
},
|
6 |
{
|
7 |
+
"revision": "bb5a62e98165b2cca977",
|
8 |
"url": "/static/css/main.3b4eede1.chunk.css"
|
9 |
},
|
10 |
{
|
|
|
20 |
"url": "/static/js/3.9013e23f.chunk.js"
|
21 |
},
|
22 |
{
|
23 |
+
"revision": "bb5a62e98165b2cca977",
|
24 |
+
"url": "/static/js/main.fc0e2dc7.chunk.js"
|
25 |
},
|
26 |
{
|
27 |
"revision": "d8c310b0ac7ffa6d8151",
|
static/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.
|
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.4683f6cd857bd9aa85304ac13a82d061.js"
|
18 |
);
|
19 |
|
20 |
self.addEventListener('message', (event) => {
|
static/static/js/main.13ed4cd9.chunk.js
DELETED
@@ -1,2 +0,0 @@
|
|
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),u=t(48),g=t(158),p=t(138),E=t(81),h=t(143),y=t(136),b=t(137),f=t(63),v=t.n(f),x=t(75),S=t.n(x),C=t(133);const j=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:"60vh",[e.breakpoints.down("sm")]:{height:"45vh"},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)},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(u.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(b.a,{"aria-label":"delete",className:t.deleteButton,onClick:()=>{o(null),a(null),m.current.value=""}},r.a.createElement(S.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(u.a,{variant:"h6",gutterBottom:!0},"Drag & Drop an image here"),r.a.createElement(u.a,{variant:"body2",color:"textSecondary",gutterBottom:!0},"or"),r.a.createElement(y.a,{variant:"contained",color:"primary",component:"span",startIcon:r.a.createElement(v.a,null)},"Browse Files"),r.a.createElement(u.a,{variant:"body2",color:"textSecondary",className:t.supportText},"Supported formats: JPG, PNG, GIF")))},O=t(139),N=t(140),B=t(166),T=t(141),I=t(64),k=t.n(I),P=t(76),D=t.n(P),A=t(77),F=t.n(A);const R=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 z=e=>{let{onModelSelect:a,onProcess:t,isProcessing:n,modelsStatus:l,selectedModel:o,imageSelected:c}=e;const i=R(),m=[{id:"yolo",name:"YOLOv8",description:"Fast and accurate object detection",icon:r.a.createElement(k.a,null),available:l.yolo},{id:"detr",name:"DETR",description:"DEtection TRansformer for object detection",icon:r.a.createElement(k.a,null),available:l.detr},{id:"vit",name:"ViT",description:"Vision Transformer for image classification",icon:r.a.createElement(D.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(u.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(O.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(N.a,{className:i.cardContent},r.a.createElement(s.a,{sx:{mb:2,color:"primary"}},e.icon),r.a.createElement(u.a,{variant:"h5",component:"div",gutterBottom:!0},e.name),r.a.createElement("div",{className:i.chipContainer},e.available?r.a.createElement(B.a,{label:"Available",className:i.successChip,size:"small"}):r.a.createElement(B.a,{label:"Not Available",className:i.errorChip,size:"small"})),r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},e.description)),r.a.createElement(T.a,null,r.a.createElement(y.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(y.a,{variant:"contained",color:"primary",size:"large",startIcon:r.a.createElement(F.a,null),onClick:t,disabled:!o||!c||n},n?"Processing...":"Process Image")))},L=t(153),M=t(150),W=t(105),_=t(154),J=t(142),U=t(164),H=t(163),V=t(145),G=t(146),Y=t(147),q=t(167),K=t(160),Q=t(151),X=t(169),Z=t(152),$=t(161);const ee=Object(C.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),[g,E]=Object(n.useState)(!1),[b,f]=Object(n.useState)("image"),[v,x]=Object(n.useState)(""),[S,C]=Object(n.useState)([]),[j,w]=Object(n.useState)(!1),[T,I]=Object(n.useState)(null),{model:k,data:P}=a,D=()=>{E(!1)},A=()=>"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(u.a,{variant:"h6",gutterBottom:!0},"Vector Database Actions"),r.a.createElement(s.a,{display:"flex",alignItems:"center",mb:2},r.a.createElement(y.a,{variant:"contained",color:"primary",onClick:async()=>{o(!0),d(null);try{let e;if(e="vit"===k?await fetch("/api/add-to-collection",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({image:P.image,metadata:{model:"vit",classifications:P.classifications}})}):await fetch("/api/add-detected-objects",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({image:P.image,objects:P.detections,imageId:A()})}),!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(h.a,{size:20,color:"inherit",style:{marginRight:8}}),"Saving..."):"Save to Vector DB"),r.a.createElement(y.a,{variant:"outlined",color:"primary",onClick:()=>{E(!0),C([]),I(null)},className:t.button},"Search Similar")),m&&r.a.createElement($.a,{severity:"error",style:{marginTop:8}},m),r.a.createElement(U.a,{open:c,autoHideDuration:5e3,onClose:()=>i(!1)},r.a.createElement($.a,{severity:"success"},"vit"===k?"Image and classifications successfully saved to vector DB!":"Detected objects successfully saved to vector DB!")),r.a.createElement(H.a,{open:g,onClose:D,maxWidth:"md",fullWidth:!0},r.a.createElement(V.a,null,"Search Vector Database"),r.a.createElement(G.a,null,r.a.createElement(Y.a,{className:t.formControl},r.a.createElement(q.a,{id:"search-type-label"},"Search Type"),r.a.createElement(K.a,{labelId:"search-type-label",id:"search-type",value:b,onChange:e=>{f(e.target.value),C([]),I(null)}},r.a.createElement(Q.a,{value:"image"},"Search by Current Image"),r.a.createElement(Q.a,{value:"class"},"Search by Class Name"))),"class"===b&&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})),T&&r.a.createElement($.a,{severity:"error",style:{marginBottom:16}},T),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(h.a,null),r.a.createElement(u.a,{variant:"body1",style:{marginLeft:16}},"Searching...")):r.a.createElement(r.a.Fragment,null,console.log("Search dialog render - searchResults:",S),S.length>0?(console.log("Rendering search results:",S),console.log("Search results length:",S.length),0===S.length?(console.log("No results to render"),r.a.createElement(u.a,{variant:"body1"},"No results found.")):r.a.createElement(p.a,{container:!0,spacing:2},S.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(O.a,{className:t.resultCard},e.metadata&&e.metadata.image_data?r.a.createElement(J.a,{className:t.resultImage,component:"img",height:"200",image:"data:image/jpeg;base64,".concat(e.metadata.image_data),alt:e.metadata&&e.metadata.class?e.metadata.class:"Object"}):r.a.createElement(s.a,{className:t.resultImage,style:{backgroundColor:"#f0f0f0",display:"flex",alignItems:"center",justifyContent:"center",height:200}},r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},e.metadata&&e.metadata.class?e.metadata.class:"Object"," Image")),r.a.createElement(N.a,null,r.a.createElement(s.a,{display:"flex",justifyContent:"space-between",alignItems:"center",mb:1},r.a.createElement(u.a,{variant:"subtitle1"},"Result #",a+1),r.a.createElement(B.a,{label:"Similarity: ".concat(n.toFixed(2),"%"),className:t.similarityChip,size:"small"})),r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},r.a.createElement("strong",null,"Class:")," ",e.metadata.class||"N/A"),e.metadata.confidence&&r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},r.a.createElement("strong",null,"Confidence:")," ",(100*e.metadata.confidence).toFixed(2),"%"),r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},r.a.createElement("strong",null,"Object ID:")," ",e.id))))}))):r.a.createElement(u.a,{variant:"body1"},"No results found. Please try another search.")))),r.a.createElement(Z.a,null,r.a.createElement(y.a,{onClick:D,color:"default"},"Close"),r.a.createElement(y.a,{onClick:async()=>{w(!0),I(null);try{let e={};if("image"===b)e={searchType:"image",image:P.image,n_results:5};else{if(!v.trim())throw new Error("Please enter a class name");e={searchType:"class",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);if(console.log("Search API response:",t),!t.success||!Array.isArray(t.results))throw console.error("Unexpected API response format:",t),new Error("Unexpected API response format");console.log("Setting search results array:",t.results),console.log("Results array length:",t.results.length),console.log("First result item:",t.results[0]),C(t.results)}catch(e){console.error("Error searching vector DB:",e),I("Error searching vector DB: ".concat(e.message))}finally{w(!1)}},color:"primary",variant:"contained",disabled:j||"class"===b&&!v.trim()},"Search"))))};const te=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 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(u.a,{color:"error"},l.error));const o=()=>l.performance?r.a.createElement(s.a,{className:"performance-info"},r.a.createElement(L.a,{className:t.dividerMargin}),r.a.createElement(u.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(u.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(u.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(u.a,{variant:"subtitle1",gutterBottom:!0},"Detected Objects:"),l.detections&&l.detections.length>0?r.a.createElement(M.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(u.a,{variant:"body1",component:"span"},e.class),r.a.createElement(B.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(L.a,null)))):r.a.createElement(u.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(u.a,{variant:"h6",gutterBottom:!0},"ViT Classification Results"),r.a.createElement(u.a,{variant:"subtitle1",gutterBottom:!0},"Top Predictions:"),l.top_predictions&&l.top_predictions.length>0?r.a.createElement(M.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(u.a,{variant:"body1",component:"span"},e.rank,". ",e.class),r.a.createElement(B.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(L.a,null)))):r.a.createElement(u.a,{variant:"body1"},"No classifications available"),o(),r.a.createElement(ae,{results:a})):null};const re=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 le=e=>{let{visionResults:a,model:t}=e;const l=re(),[o,c]=Object(n.useState)(""),[i,m]=Object(n.useState)(!1),[d,g]=Object(n.useState)(null),[p,b]=Object(n.useState)(null);return a?r.a.createElement(E.a,{className:l.paper},r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"Ask AI about the ","vit"===t?"Classification":"Detection"," Results"),r.a.createElement(u.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(y.a,{variant:"contained",color:"primary",onClick:async()=>{if(o.trim()){m(!0),b(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?b(t.error):g(t)}catch(e){console.error("Error analyzing with LLM:",e),b("Error analyzing with LLM: ".concat(e.message))}finally{m(!1)}}},disabled:i||!o.trim()},"Analyze with AI",i&&r.a.createElement(h.a,{size:24,className:l.buttonProgress})),p&&r.a.createElement(s.a,{mt:2},r.a.createElement(u.a,{color:"error"},p)),d&&r.a.createElement(r.a.Fragment,null,r.a.createElement(L.a,{className:l.dividerMargin}),r.a.createElement(u.a,{variant:"subtitle1",gutterBottom:!0},"AI Analysis:"),r.a.createElement(s.a,{className:l.responseBox},r.a.createElement(u.a,{variant:"body1"},d.response)),d.performance&&r.a.createElement(s.a,{mt:1},r.a.createElement(u.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};var oe=function(){const[e,a]=Object(n.useState)("gpt-4"),[t,l]=Object(n.useState)(""),[o,c]=Object(n.useState)("You are a helpful assistant."),[i,s]=Object(n.useState)(""),[m,d]=Object(n.useState)(""),[g,h]=Object(n.useState)(!1),[b,f]=Object(n.useState)("");return r.a.createElement(E.a,{style:{padding:16}},r.a.createElement(u.a,{variant:"h5",gutterBottom:!0},"OpenAI Chat (OpenAI API)"),r.a.createElement(u.a,{variant:"body2",color:"textSecondary",gutterBottom:!0},"If the server env var OPENAI_API_KEY is set, the API Key field is optional."),r.a.createElement(p.a,{container:!0,spacing:2},r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(X.a,{label:"Model",value:e,onChange:e=>a(e.target.value),fullWidth:!0,variant:"outlined",size:"small"})),r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(X.a,{label:"OpenAI API Key (optional)",value:t,onChange:e=>l(e.target.value),fullWidth:!0,variant:"outlined",size:"small",type:"password",placeholder:"sk-..."})),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(X.a,{label:"System Prompt (optional)",value:o,onChange:e=>c(e.target.value),fullWidth:!0,variant:"outlined",size:"small"})),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(X.a,{label:"User Question",value:i,onChange:e=>s(e.target.value),fullWidth:!0,multiline:!0,rows:4,variant:"outlined"})),b&&r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(u.a,{color:"error"},b)),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement("div",{style:{display:"flex",gap:8}},r.a.createElement(y.a,{color:"primary",variant:"contained",onClick:async()=>{f(""),d("");const a=(i||"").trim();if(a){h(!0);try{const r=await fetch("/api/openai/chat",{method:"POST",headers:{"Content-Type":"application/json"},credentials:"include",body:JSON.stringify({prompt:a,model:(e||"").trim()||"gpt-4",api_key:t||void 0,system:o||void 0})});if(!r.ok){let e=await r.text();try{e=JSON.stringify(JSON.parse(e),null,2)}catch(n){}throw new Error(e)}const l=await r.json(),c="Model: ".concat(l.model," | Latency: ").concat(l.latency_sec,"s")+(l.usage?" | Usage: ".concat(JSON.stringify(l.usage)):"");d((l.response||"(Empty response)")+"\n\n---\n"+c)}catch(r){f("Error: "+r.message)}finally{h(!1)}}else f("Please enter a question.")},disabled:g},g?"Sending...":"Send Question"),r.a.createElement(y.a,{variant:"outlined",onClick:()=>{s(""),d(""),f("")}},"Clear"))),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(L.a,{style:{margin:"12px 0"}}),r.a.createElement(u.a,{variant:"subtitle2",color:"textSecondary"},"Response"),r.a.createElement("pre",{style:{whiteSpace:"pre-wrap",fontFamily:"ui-monospace, monospace"}},m))))};t(102);const ce=Object(c.a)({palette:{primary:{main:"#3f51b5"},secondary:{main:"#f50057"}},typography:{fontFamily:"Roboto, Arial, sans-serif"}});var ie=function(){const[e,a]=Object(n.useState)(null),[t,l]=Object(n.useState)(""),[o,c]=Object(n.useState)(!1),[y,b]=Object(n.useState)(null),[f,v]=Object(n.useState)(null),[x,S]=Object(n.useState)({yolo:!1,detr:!1,vit:!1});return Object(n.useEffect)(()=>{fetch("/api/status").then(e=>e.json()).then(e=>{S(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:ce},r.a.createElement(s.a,{style:{flexGrow:1}},r.a.createElement(m.a,{position:"static"},r.a.createElement(d.a,null,r.a.createElement(u.a,{variant:"h6",style:{flexGrow:1}},"Multi-Model Object Detection Demo"))),r.a.createElement(g.a,{maxWidth:"lg",style:{marginTop:ce.spacing(4),marginBottom:ce.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:ce.spacing(2)}},r.a.createElement(u.a,{variant:"h5",gutterBottom:!0},"Upload an image to see how each model performs!"),r.a.createElement(u.a,{variant:"body1",paragraph:!0},"This demo showcases three different object detection and image classification models:"),r.a.createElement(u.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),b(null),v(null)}})),r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(z,{onModelSelect:e=>{l(e),b(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();b({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})),f&&r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(E.a,{style:{padding:ce.spacing(2),backgroundColor:"#ffebee"}},r.a.createElement(u.a,{color:"error"},f))),o&&r.a.createElement(p.a,{item:!0,xs:12,style:{textAlign:"center",margin:"".concat(ce.spacing(4),"px 0")}},r.a.createElement(h.a,null),r.a.createElement(u.a,{variant:"h6",style:{marginTop:ce.spacing(2)}},"Processing image...")),y&&r.a.createElement(r.a.Fragment,null,r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(ne,{results:y})),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(le,{visionResults:y.data,model:y.model}))),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(oe,null))))))};var se=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(ie,null)),document.getElementById("root")),se()},92:function(e,a,t){e.exports=t(103)},97:function(e,a,t){}},[[92,1,2]]]);
|
2 |
-
//# sourceMappingURL=main.13ed4cd9.chunk.js.map
|
|
|
|
|
|
static/static/js/main.13ed4cd9.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","components/OpenAIChat.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","breakpoints","down","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","imageId","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","log","length","index","similarity","distance","image_data","CardMedia","class","toFixed","confidence","DialogActions","requestBody","n_results","trim","class_name","success","Array","isArray","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","OpenAIChat","setModel","apiKey","setApiKey","system","setSystem","prompt","setPrompt","setResponse","loading","setLoading","multiline","rows","res","credentials","api_key","txt","text","parse","_unused","meta","latency_sec","usage","onClear","fontFamily","createMuiTheme","typography","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.13ed4cd9.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 // Use viewport-based height so any aspect ratio fits inside\n height: '60vh',\n [theme.breakpoints.down('sm')]: {\n height: '45vh',\n },\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-to-collection', {\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 imageId: 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 searchType: 'image',\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 searchType: 'class',\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 console.log('Search API response:', result);\n \n // The backend responds with {success, searchType, results} structure, so extract only the results array\n if (result.success && Array.isArray(result.results)) {\n console.log('Setting search results array:', result.results);\n console.log('Results array length:', result.results.length);\n console.log('First result item:', result.results[0]);\n setSearchResults(result.results);\n } else {\n console.error('Unexpected API response format:', result);\n throw new Error('Unexpected API response format');\n }\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 console.log('Rendering search results:', searchResults);\n console.log('Search results length:', searchResults.length);\n \n if (searchResults.length === 0) {\n console.log('No results to render');\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 {result.metadata && result.metadata.image_data ? (\n <CardMedia\n className={classes.resultImage}\n component=\"img\"\n height=\"200\"\n image={`data:image/jpeg;base64,${result.metadata.image_data}`}\n alt={result.metadata && result.metadata.class ? result.metadata.class : 'Object'}\n />\n ) : (\n <Box \n className={classes.resultImage}\n style={{ \n backgroundColor: '#f0f0f0', \n display: 'flex', \n alignItems: 'center', \n justifyContent: 'center',\n height: 200\n }}\n >\n <Typography variant=\"body2\" color=\"textSecondary\">\n {result.metadata && result.metadata.class ? result.metadata.class : 'Object'} Image\n </Typography>\n </Box>\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 <>\n {console.log('Search dialog render - searchResults:', searchResults)}\n {searchResults.length > 0 ? renderSearchResults() : \n <Typography variant=\"body1\">No results found. Please try another search.</Typography>\n }\n </>\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 } from 'react';\nimport { \n Paper,\n Typography,\n Grid,\n TextField,\n Button,\n Divider\n} from '@material-ui/core';\n\nfunction OpenAIChat() {\n const [model, setModel] = useState('gpt-4');\n const [apiKey, setApiKey] = useState('');\n const [system, setSystem] = useState('You are a helpful assistant.');\n const [prompt, setPrompt] = useState('');\n const [response, setResponse] = useState('');\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState('');\n\n const onSend = async () => {\n setError('');\n setResponse('');\n const p = (prompt || '').trim();\n if (!p) { setError('Please enter a question.'); return; }\n\n setLoading(true);\n try {\n const res = await fetch('/api/openai/chat', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n credentials: 'include',\n body: JSON.stringify({\n prompt: p,\n model: (model || '').trim() || 'gpt-4',\n api_key: (apiKey || undefined),\n system: (system || undefined)\n })\n });\n\n if (!res.ok) {\n let txt = await res.text();\n try { txt = JSON.stringify(JSON.parse(txt), null, 2); } catch {}\n throw new Error(txt);\n }\n const data = await res.json();\n const meta = `Model: ${data.model} | Latency: ${data.latency_sec}s` + (data.usage ? ` | Usage: ${JSON.stringify(data.usage)}` : '');\n setResponse((data.response || '(Empty response)') + '\\n\\n---\\n' + meta);\n } catch (e) {\n setError('Error: ' + e.message);\n } finally {\n setLoading(false);\n }\n };\n\n const onClear = () => {\n setPrompt('');\n setResponse('');\n setError('');\n };\n\n return (\n <Paper style={{ padding: 16 }}>\n <Typography variant=\"h5\" gutterBottom>\n OpenAI Chat (OpenAI API)\n </Typography>\n <Typography variant=\"body2\" color=\"textSecondary\" gutterBottom>\n If the server env var OPENAI_API_KEY is set, the API Key field is optional.\n </Typography>\n <Grid container spacing={2}>\n <Grid item xs={12} md={6}>\n <TextField\n label=\"Model\"\n value={model}\n onChange={(e) => setModel(e.target.value)}\n fullWidth\n variant=\"outlined\"\n size=\"small\"\n />\n </Grid>\n <Grid item xs={12} md={6}>\n <TextField\n label=\"OpenAI API Key (optional)\"\n value={apiKey}\n onChange={(e) => setApiKey(e.target.value)}\n fullWidth\n variant=\"outlined\"\n size=\"small\"\n type=\"password\"\n placeholder=\"sk-...\"\n />\n </Grid>\n <Grid item xs={12}>\n <TextField\n label=\"System Prompt (optional)\"\n value={system}\n onChange={(e) => setSystem(e.target.value)}\n fullWidth\n variant=\"outlined\"\n size=\"small\"\n />\n </Grid>\n <Grid item xs={12}>\n <TextField\n label=\"User Question\"\n value={prompt}\n onChange={(e) => setPrompt(e.target.value)}\n fullWidth\n multiline\n rows={4}\n variant=\"outlined\"\n />\n </Grid>\n {error && (\n <Grid item xs={12}>\n <Typography color=\"error\">{error}</Typography>\n </Grid>\n )}\n <Grid item xs={12}>\n <div style={{ display: 'flex', gap: 8 }}>\n <Button color=\"primary\" variant=\"contained\" onClick={onSend} disabled={loading}>\n {loading ? 'Sending...' : 'Send Question'}\n </Button>\n <Button variant=\"outlined\" onClick={onClear}>Clear</Button>\n </div>\n </Grid>\n <Grid item xs={12}>\n <Divider style={{ margin: '12px 0' }} />\n <Typography variant=\"subtitle2\" color=\"textSecondary\">Response</Typography>\n <pre style={{ whiteSpace: 'pre-wrap', fontFamily: 'ui-monospace, monospace' }}>{response}</pre>\n </Grid>\n </Grid>\n </Paper>\n );\n}\n\nexport default OpenAIChat;\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 OpenAIChat from './components/OpenAIChat';\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\n {/* OpenAI Chat section at the end */}\n <Grid item xs={12}>\n <OpenAIChat />\n </Grid>\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":""}
|
|
|
|
static/static/js/main.fc0e2dc7.chunk.js
ADDED
@@ -0,0 +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),u=t(48),g=t(158),p=t(138),E=t(81),h=t(143),y=t(136),b=t(137),f=t(63),v=t.n(f),x=t(75),S=t.n(x),C=t(133);const j=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:"60vh",[e.breakpoints.down("sm")]:{height:"45vh"},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)},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(u.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",style:{width:"100%",height:"auto",maxHeight:"100%",objectFit:"contain",display:"block"}}),r.a.createElement(b.a,{"aria-label":"delete",className:t.deleteButton,onClick:()=>{o(null),a(null),m.current.value=""}},r.a.createElement(S.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(u.a,{variant:"h6",gutterBottom:!0},"Drag & Drop an image here"),r.a.createElement(u.a,{variant:"body2",color:"textSecondary",gutterBottom:!0},"or"),r.a.createElement(y.a,{variant:"contained",color:"primary",component:"span",startIcon:r.a.createElement(v.a,null)},"Browse Files"),r.a.createElement(u.a,{variant:"body2",color:"textSecondary",className:t.supportText},"Supported formats: JPG, PNG, GIF")))},O=t(139),N=t(140),B=t(166),T=t(141),I=t(64),k=t.n(I),P=t(76),D=t.n(P),A=t(77),F=t.n(A);const R=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 z=e=>{let{onModelSelect:a,onProcess:t,isProcessing:n,modelsStatus:l,selectedModel:o,imageSelected:c}=e;const i=R(),m=[{id:"yolo",name:"YOLOv8",description:"Fast and accurate object detection",icon:r.a.createElement(k.a,null),available:l.yolo},{id:"detr",name:"DETR",description:"DEtection TRansformer for object detection",icon:r.a.createElement(k.a,null),available:l.detr},{id:"vit",name:"ViT",description:"Vision Transformer for image classification",icon:r.a.createElement(D.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(u.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(O.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(N.a,{className:i.cardContent},r.a.createElement(s.a,{sx:{mb:2,color:"primary"}},e.icon),r.a.createElement(u.a,{variant:"h5",component:"div",gutterBottom:!0},e.name),r.a.createElement("div",{className:i.chipContainer},e.available?r.a.createElement(B.a,{label:"Available",className:i.successChip,size:"small"}):r.a.createElement(B.a,{label:"Not Available",className:i.errorChip,size:"small"})),r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},e.description)),r.a.createElement(T.a,null,r.a.createElement(y.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(y.a,{variant:"contained",color:"primary",size:"large",startIcon:r.a.createElement(F.a,null),onClick:t,disabled:!o||!c||n},n?"Processing...":"Process Image")))},L=t(153),M=t(150),W=t(105),_=t(154),J=t(142),H=t(164),U=t(163),V=t(145),G=t(146),Y=t(147),q=t(167),K=t(160),Q=t(151),X=t(169),Z=t(152),$=t(161);const ee=Object(C.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),[g,E]=Object(n.useState)(!1),[b,f]=Object(n.useState)("image"),[v,x]=Object(n.useState)(""),[S,C]=Object(n.useState)([]),[j,w]=Object(n.useState)(!1),[T,I]=Object(n.useState)(null),{model:k,data:P}=a,D=()=>{E(!1)},A=()=>"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(u.a,{variant:"h6",gutterBottom:!0},"Vector Database Actions"),r.a.createElement(s.a,{display:"flex",alignItems:"center",mb:2},r.a.createElement(y.a,{variant:"contained",color:"primary",onClick:async()=>{o(!0),d(null);try{let e;if(e="vit"===k?await fetch("/api/add-to-collection",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({image:P.image,metadata:{model:"vit",classifications:P.classifications}})}):await fetch("/api/add-detected-objects",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({image:P.image,objects:P.detections,imageId:A()})}),!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(h.a,{size:20,color:"inherit",style:{marginRight:8}}),"Saving..."):"Save to Vector DB"),r.a.createElement(y.a,{variant:"outlined",color:"primary",onClick:()=>{E(!0),C([]),I(null)},className:t.button},"Search Similar")),m&&r.a.createElement($.a,{severity:"error",style:{marginTop:8}},m),r.a.createElement(H.a,{open:c,autoHideDuration:5e3,onClose:()=>i(!1)},r.a.createElement($.a,{severity:"success"},"vit"===k?"Image and classifications successfully saved to vector DB!":"Detected objects successfully saved to vector DB!")),r.a.createElement(U.a,{open:g,onClose:D,maxWidth:"md",fullWidth:!0},r.a.createElement(V.a,null,"Search Vector Database"),r.a.createElement(G.a,null,r.a.createElement(Y.a,{className:t.formControl},r.a.createElement(q.a,{id:"search-type-label"},"Search Type"),r.a.createElement(K.a,{labelId:"search-type-label",id:"search-type",value:b,onChange:e=>{f(e.target.value),C([]),I(null)}},r.a.createElement(Q.a,{value:"image"},"Search by Current Image"),r.a.createElement(Q.a,{value:"class"},"Search by Class Name"))),"class"===b&&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})),T&&r.a.createElement($.a,{severity:"error",style:{marginBottom:16}},T),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(h.a,null),r.a.createElement(u.a,{variant:"body1",style:{marginLeft:16}},"Searching...")):r.a.createElement(r.a.Fragment,null,console.log("Search dialog render - searchResults:",S),S.length>0?(console.log("Rendering search results:",S),console.log("Search results length:",S.length),0===S.length?(console.log("No results to render"),r.a.createElement(u.a,{variant:"body1"},"No results found.")):r.a.createElement(p.a,{container:!0,spacing:2},S.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(O.a,{className:t.resultCard},e.metadata&&e.metadata.image_data?r.a.createElement(J.a,{className:t.resultImage,component:"img",height:"200",image:"data:image/jpeg;base64,".concat(e.metadata.image_data),alt:e.metadata&&e.metadata.class?e.metadata.class:"Object"}):r.a.createElement(s.a,{className:t.resultImage,style:{backgroundColor:"#f0f0f0",display:"flex",alignItems:"center",justifyContent:"center",height:200}},r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},e.metadata&&e.metadata.class?e.metadata.class:"Object"," Image")),r.a.createElement(N.a,null,r.a.createElement(s.a,{display:"flex",justifyContent:"space-between",alignItems:"center",mb:1},r.a.createElement(u.a,{variant:"subtitle1"},"Result #",a+1),r.a.createElement(B.a,{label:"Similarity: ".concat(n.toFixed(2),"%"),className:t.similarityChip,size:"small"})),r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},r.a.createElement("strong",null,"Class:")," ",e.metadata.class||"N/A"),e.metadata.confidence&&r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},r.a.createElement("strong",null,"Confidence:")," ",(100*e.metadata.confidence).toFixed(2),"%"),r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},r.a.createElement("strong",null,"Object ID:")," ",e.id))))}))):r.a.createElement(u.a,{variant:"body1"},"No results found. Please try another search.")))),r.a.createElement(Z.a,null,r.a.createElement(y.a,{onClick:D,color:"default"},"Close"),r.a.createElement(y.a,{onClick:async()=>{w(!0),I(null);try{let e={};if("image"===b)e={searchType:"image",image:P.image,n_results:5};else{if(!v.trim())throw new Error("Please enter a class name");e={searchType:"class",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);if(console.log("Search API response:",t),!t.success||!Array.isArray(t.results))throw console.error("Unexpected API response format:",t),new Error("Unexpected API response format");console.log("Setting search results array:",t.results),console.log("Results array length:",t.results.length),console.log("First result item:",t.results[0]),C(t.results)}catch(e){console.error("Error searching vector DB:",e),I("Error searching vector DB: ".concat(e.message))}finally{w(!1)}},color:"primary",variant:"contained",disabled:j||"class"===b&&!v.trim()},"Search"))))};const te=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 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(u.a,{color:"error"},l.error));const o=()=>l.performance?r.a.createElement(s.a,{className:"performance-info"},r.a.createElement(L.a,{className:t.dividerMargin}),r.a.createElement(u.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(u.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(u.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(u.a,{variant:"subtitle1",gutterBottom:!0},"Detected Objects:"),l.detections&&l.detections.length>0?r.a.createElement(M.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(u.a,{variant:"body1",component:"span"},e.class),r.a.createElement(B.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(L.a,null)))):r.a.createElement(u.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(u.a,{variant:"h6",gutterBottom:!0},"ViT Classification Results"),r.a.createElement(u.a,{variant:"subtitle1",gutterBottom:!0},"Top Predictions:"),l.top_predictions&&l.top_predictions.length>0?r.a.createElement(M.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(u.a,{variant:"body1",component:"span"},e.rank,". ",e.class),r.a.createElement(B.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(L.a,null)))):r.a.createElement(u.a,{variant:"body1"},"No classifications available"),o(),r.a.createElement(ae,{results:a})):null};const re=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 le=e=>{let{visionResults:a,model:t}=e;const l=re(),[o,c]=Object(n.useState)(""),[i,m]=Object(n.useState)(!1),[d,g]=Object(n.useState)(null),[p,b]=Object(n.useState)(null);return a?r.a.createElement(E.a,{className:l.paper},r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"Ask AI about the ","vit"===t?"Classification":"Detection"," Results"),r.a.createElement(u.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(y.a,{variant:"contained",color:"primary",onClick:async()=>{if(o.trim()){m(!0),b(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?b(t.error):g(t)}catch(e){console.error("Error analyzing with LLM:",e),b("Error analyzing with LLM: ".concat(e.message))}finally{m(!1)}}},disabled:i||!o.trim()},"Analyze with AI",i&&r.a.createElement(h.a,{size:24,className:l.buttonProgress})),p&&r.a.createElement(s.a,{mt:2},r.a.createElement(u.a,{color:"error"},p)),d&&r.a.createElement(r.a.Fragment,null,r.a.createElement(L.a,{className:l.dividerMargin}),r.a.createElement(u.a,{variant:"subtitle1",gutterBottom:!0},"AI Analysis:"),r.a.createElement(s.a,{className:l.responseBox},r.a.createElement(u.a,{variant:"body1"},d.response)),d.performance&&r.a.createElement(s.a,{mt:1},r.a.createElement(u.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};var oe=function(){const[e,a]=Object(n.useState)("gpt-4"),[t,l]=Object(n.useState)(""),[o,c]=Object(n.useState)("You are a helpful assistant."),[i,s]=Object(n.useState)(""),[m,d]=Object(n.useState)(""),[g,h]=Object(n.useState)(!1),[b,f]=Object(n.useState)("");return r.a.createElement(E.a,{style:{padding:16}},r.a.createElement(u.a,{variant:"h5",gutterBottom:!0},"OpenAI Chat (OpenAI API)"),r.a.createElement(u.a,{variant:"body2",color:"textSecondary",gutterBottom:!0},"If the server env var OPENAI_API_KEY is set, the API Key field is optional."),r.a.createElement(p.a,{container:!0,spacing:2},r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(X.a,{label:"Model",value:e,onChange:e=>a(e.target.value),fullWidth:!0,variant:"outlined",size:"small"})),r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(X.a,{label:"OpenAI API Key (optional)",value:t,onChange:e=>l(e.target.value),fullWidth:!0,variant:"outlined",size:"small",type:"password",placeholder:"sk-..."})),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(X.a,{label:"System Prompt (optional)",value:o,onChange:e=>c(e.target.value),fullWidth:!0,variant:"outlined",size:"small"})),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(X.a,{label:"User Question",value:i,onChange:e=>s(e.target.value),fullWidth:!0,multiline:!0,rows:4,variant:"outlined"})),b&&r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(u.a,{color:"error"},b)),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement("div",{style:{display:"flex",gap:8}},r.a.createElement(y.a,{color:"primary",variant:"contained",onClick:async()=>{f(""),d("");const a=(i||"").trim();if(a){h(!0);try{const r=await fetch("/api/openai/chat",{method:"POST",headers:{"Content-Type":"application/json"},credentials:"include",body:JSON.stringify({prompt:a,model:(e||"").trim()||"gpt-4",api_key:t||void 0,system:o||void 0})});if(!r.ok){let e=await r.text();try{e=JSON.stringify(JSON.parse(e),null,2)}catch(n){}throw new Error(e)}const l=await r.json(),c="Model: ".concat(l.model," | Latency: ").concat(l.latency_sec,"s")+(l.usage?" | Usage: ".concat(JSON.stringify(l.usage)):"");d((l.response||"(Empty response)")+"\n\n---\n"+c)}catch(r){f("Error: "+r.message)}finally{h(!1)}}else f("Please enter a question.")},disabled:g},g?"Sending...":"Send Question"),r.a.createElement(y.a,{variant:"outlined",onClick:()=>{s(""),d(""),f("")}},"Clear"))),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(L.a,{style:{margin:"12px 0"}}),r.a.createElement(u.a,{variant:"subtitle2",color:"textSecondary"},"Response"),r.a.createElement("pre",{style:{whiteSpace:"pre-wrap",fontFamily:"ui-monospace, monospace"}},m))))};t(102);const ce=Object(c.a)({palette:{primary:{main:"#3f51b5"},secondary:{main:"#f50057"}},typography:{fontFamily:"Roboto, Arial, sans-serif"}});var ie=function(){const[e,a]=Object(n.useState)(null),[t,l]=Object(n.useState)(""),[o,c]=Object(n.useState)(!1),[y,b]=Object(n.useState)(null),[f,v]=Object(n.useState)(null),[x,S]=Object(n.useState)({yolo:!1,detr:!1,vit:!1});return Object(n.useEffect)(()=>{fetch("/api/status").then(e=>e.json()).then(e=>{S(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:ce},r.a.createElement(s.a,{style:{flexGrow:1}},r.a.createElement(m.a,{position:"static"},r.a.createElement(d.a,null,r.a.createElement(u.a,{variant:"h6",style:{flexGrow:1}},"Multi-Model Object Detection Demo"))),r.a.createElement(g.a,{maxWidth:"lg",style:{marginTop:ce.spacing(4),marginBottom:ce.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:ce.spacing(2)}},r.a.createElement(u.a,{variant:"h5",gutterBottom:!0},"Upload an image to see how each model performs!"),r.a.createElement(u.a,{variant:"body1",paragraph:!0},"This demo showcases three different object detection and image classification models:"),r.a.createElement(u.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),b(null),v(null)}})),r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(z,{onModelSelect:e=>{l(e),b(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();b({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})),f&&r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(E.a,{style:{padding:ce.spacing(2),backgroundColor:"#ffebee"}},r.a.createElement(u.a,{color:"error"},f))),o&&r.a.createElement(p.a,{item:!0,xs:12,style:{textAlign:"center",margin:"".concat(ce.spacing(4),"px 0")}},r.a.createElement(h.a,null),r.a.createElement(u.a,{variant:"h6",style:{marginTop:ce.spacing(2)}},"Processing image...")),y&&r.a.createElement(r.a.Fragment,null,r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(ne,{results:y})),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(le,{visionResults:y.data,model:y.model}))),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(oe,null))))))};var se=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(ie,null)),document.getElementById("root")),se()},92:function(e,a,t){e.exports=t(103)},97:function(e,a,t){}},[[92,1,2]]]);
|
2 |
+
//# sourceMappingURL=main.fc0e2dc7.chunk.js.map
|
static/static/js/main.fc0e2dc7.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","components/OpenAIChat.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","breakpoints","down","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","maxHeight","objectFit","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","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","imageId","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","log","length","index","similarity","distance","image_data","CardMedia","class","toFixed","confidence","DialogActions","requestBody","n_results","trim","class_name","success","Array","isArray","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","OpenAIChat","setModel","apiKey","setApiKey","system","setSystem","prompt","setPrompt","setResponse","loading","setLoading","multiline","rows","res","credentials","api_key","txt","text","parse","_unused","meta","latency_sec","usage","onClear","fontFamily","createMuiTheme","typography","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.fc0e2dc7.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 // Use viewport-based height so any aspect ratio fits inside\n height: '60vh',\n [theme.breakpoints.down('sm')]: {\n height: '45vh',\n },\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 style={{\n width: '100%',\n height: 'auto',\n maxHeight: '100%',\n objectFit: 'contain',\n display: 'block'\n }}\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-to-collection', {\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 imageId: 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 searchType: 'image',\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 searchType: 'class',\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 console.log('Search API response:', result);\n \n // The backend responds with {success, searchType, results} structure, so extract only the results array\n if (result.success && Array.isArray(result.results)) {\n console.log('Setting search results array:', result.results);\n console.log('Results array length:', result.results.length);\n console.log('First result item:', result.results[0]);\n setSearchResults(result.results);\n } else {\n console.error('Unexpected API response format:', result);\n throw new Error('Unexpected API response format');\n }\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 console.log('Rendering search results:', searchResults);\n console.log('Search results length:', searchResults.length);\n \n if (searchResults.length === 0) {\n console.log('No results to render');\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 {result.metadata && result.metadata.image_data ? (\n <CardMedia\n className={classes.resultImage}\n component=\"img\"\n height=\"200\"\n image={`data:image/jpeg;base64,${result.metadata.image_data}`}\n alt={result.metadata && result.metadata.class ? result.metadata.class : 'Object'}\n />\n ) : (\n <Box \n className={classes.resultImage}\n style={{ \n backgroundColor: '#f0f0f0', \n display: 'flex', \n alignItems: 'center', \n justifyContent: 'center',\n height: 200\n }}\n >\n <Typography variant=\"body2\" color=\"textSecondary\">\n {result.metadata && result.metadata.class ? result.metadata.class : 'Object'} Image\n </Typography>\n </Box>\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 <>\n {console.log('Search dialog render - searchResults:', searchResults)}\n {searchResults.length > 0 ? renderSearchResults() : \n <Typography variant=\"body1\">No results found. Please try another search.</Typography>\n }\n </>\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 } from 'react';\nimport { \n Paper,\n Typography,\n Grid,\n TextField,\n Button,\n Divider\n} from '@material-ui/core';\n\nfunction OpenAIChat() {\n const [model, setModel] = useState('gpt-4');\n const [apiKey, setApiKey] = useState('');\n const [system, setSystem] = useState('You are a helpful assistant.');\n const [prompt, setPrompt] = useState('');\n const [response, setResponse] = useState('');\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState('');\n\n const onSend = async () => {\n setError('');\n setResponse('');\n const p = (prompt || '').trim();\n if (!p) { setError('Please enter a question.'); return; }\n\n setLoading(true);\n try {\n const res = await fetch('/api/openai/chat', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n credentials: 'include',\n body: JSON.stringify({\n prompt: p,\n model: (model || '').trim() || 'gpt-4',\n api_key: (apiKey || undefined),\n system: (system || undefined)\n })\n });\n\n if (!res.ok) {\n let txt = await res.text();\n try { txt = JSON.stringify(JSON.parse(txt), null, 2); } catch {}\n throw new Error(txt);\n }\n const data = await res.json();\n const meta = `Model: ${data.model} | Latency: ${data.latency_sec}s` + (data.usage ? ` | Usage: ${JSON.stringify(data.usage)}` : '');\n setResponse((data.response || '(Empty response)') + '\\n\\n---\\n' + meta);\n } catch (e) {\n setError('Error: ' + e.message);\n } finally {\n setLoading(false);\n }\n };\n\n const onClear = () => {\n setPrompt('');\n setResponse('');\n setError('');\n };\n\n return (\n <Paper style={{ padding: 16 }}>\n <Typography variant=\"h5\" gutterBottom>\n OpenAI Chat (OpenAI API)\n </Typography>\n <Typography variant=\"body2\" color=\"textSecondary\" gutterBottom>\n If the server env var OPENAI_API_KEY is set, the API Key field is optional.\n </Typography>\n <Grid container spacing={2}>\n <Grid item xs={12} md={6}>\n <TextField\n label=\"Model\"\n value={model}\n onChange={(e) => setModel(e.target.value)}\n fullWidth\n variant=\"outlined\"\n size=\"small\"\n />\n </Grid>\n <Grid item xs={12} md={6}>\n <TextField\n label=\"OpenAI API Key (optional)\"\n value={apiKey}\n onChange={(e) => setApiKey(e.target.value)}\n fullWidth\n variant=\"outlined\"\n size=\"small\"\n type=\"password\"\n placeholder=\"sk-...\"\n />\n </Grid>\n <Grid item xs={12}>\n <TextField\n label=\"System Prompt (optional)\"\n value={system}\n onChange={(e) => setSystem(e.target.value)}\n fullWidth\n variant=\"outlined\"\n size=\"small\"\n />\n </Grid>\n <Grid item xs={12}>\n <TextField\n label=\"User Question\"\n value={prompt}\n onChange={(e) => setPrompt(e.target.value)}\n fullWidth\n multiline\n rows={4}\n variant=\"outlined\"\n />\n </Grid>\n {error && (\n <Grid item xs={12}>\n <Typography color=\"error\">{error}</Typography>\n </Grid>\n )}\n <Grid item xs={12}>\n <div style={{ display: 'flex', gap: 8 }}>\n <Button color=\"primary\" variant=\"contained\" onClick={onSend} disabled={loading}>\n {loading ? 'Sending...' : 'Send Question'}\n </Button>\n <Button variant=\"outlined\" onClick={onClear}>Clear</Button>\n </div>\n </Grid>\n <Grid item xs={12}>\n <Divider style={{ margin: '12px 0' }} />\n <Typography variant=\"subtitle2\" color=\"textSecondary\">Response</Typography>\n <pre style={{ whiteSpace: 'pre-wrap', fontFamily: 'ui-monospace, monospace' }}>{response}</pre>\n </Grid>\n </Grid>\n </Paper>\n );\n}\n\nexport default OpenAIChat;\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 OpenAIChat from './components/OpenAIChat';\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\n {/* OpenAI Chat section at the end */}\n <Grid item xs={12}>\n <OpenAIChat />\n </Grid>\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":""}
|