Meenu047 commited on
Commit
ee925c2
·
1 Parent(s): 5de2f5d

Add application file

Browse files
Files changed (1) hide show
  1. app.js +314 -0
app.js ADDED
@@ -0,0 +1,314 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState } from 'react';
2
+ import { Upload, X, Loader2, Image } from 'lucide-react';
3
+
4
+ export default function ModelTester() {
5
+ const [file, setFile] = useState(null);
6
+ const [preview, setPreview] = useState(null);
7
+ const [loading, setLoading] = useState(false);
8
+ const [result, setResult] = useState(null);
9
+ const [error, setError] = useState(null);
10
+ const [dragActive, setDragActive] = useState(false);
11
+
12
+ // Your model ID from the repo
13
+ const MODEL_ID = 'Meenu047/RGTB_Aerial_view_detection';
14
+
15
+ const handleDrag = (e) => {
16
+ e.preventDefault();
17
+ e.stopPropagation();
18
+ if (e.type === "dragenter" || e.type === "dragover") {
19
+ setDragActive(true);
20
+ } else if (e.type === "dragleave") {
21
+ setDragActive(false);
22
+ }
23
+ };
24
+
25
+ const handleDrop = (e) => {
26
+ e.preventDefault();
27
+ e.stopPropagation();
28
+ setDragActive(false);
29
+
30
+ if (e.dataTransfer.files && e.dataTransfer.files[0]) {
31
+ handleFile(e.dataTransfer.files[0]);
32
+ }
33
+ };
34
+
35
+ const handleChange = (e) => {
36
+ e.preventDefault();
37
+ if (e.target.files && e.target.files[0]) {
38
+ handleFile(e.target.files[0]);
39
+ }
40
+ };
41
+
42
+ const handleFile = (uploadedFile) => {
43
+ const validTypes = ['image/jpeg', 'image/png', 'image/jpg', 'image/webp'];
44
+
45
+ if (!validTypes.includes(uploadedFile.type)) {
46
+ setError('Please upload a valid image file (JPEG, PNG, WebP)');
47
+ return;
48
+ }
49
+
50
+ setFile(uploadedFile);
51
+ setError(null);
52
+ setResult(null);
53
+
54
+ const reader = new FileReader();
55
+ reader.onloadend = () => {
56
+ setPreview(reader.result);
57
+ };
58
+ reader.readAsDataURL(uploadedFile);
59
+ };
60
+
61
+ const handlePredict = async () => {
62
+ if (!file) {
63
+ setError('Please upload an image first');
64
+ return;
65
+ }
66
+
67
+ setLoading(true);
68
+ setError(null);
69
+ setResult(null);
70
+
71
+ try {
72
+ // Read file as blob
73
+ const formData = new FormData();
74
+ formData.append('file', file);
75
+
76
+ const response = await fetch(
77
+ `https://api-inference.huggingface.co/models/${MODEL_ID}`,
78
+ {
79
+ method: 'POST',
80
+ body: file,
81
+ }
82
+ );
83
+
84
+ if (!response.ok) {
85
+ if (response.status === 503) {
86
+ setError('Model is loading, please wait 20-30 seconds and try again');
87
+ } else {
88
+ const errorData = await response.json();
89
+ throw new Error(errorData.error || `HTTP error! status: ${response.status}`);
90
+ }
91
+ setLoading(false);
92
+ return;
93
+ }
94
+
95
+ const data = await response.json();
96
+ setResult(data);
97
+ } catch (err) {
98
+ setError(err.message || 'Failed to get prediction. Make sure the model is public and loaded.');
99
+ } finally {
100
+ setLoading(false);
101
+ }
102
+ };
103
+
104
+ const clearImage = () => {
105
+ setFile(null);
106
+ setPreview(null);
107
+ setResult(null);
108
+ setError(null);
109
+ };
110
+
111
+ return (
112
+ <div className="min-h-screen bg-gradient-to-br from-purple-50 via-blue-50 to-indigo-100 p-8">
113
+ <div className="max-w-5xl mx-auto">
114
+ <div className="bg-white rounded-2xl shadow-2xl p-8">
115
+ <div className="flex items-center justify-between mb-6">
116
+ <div className="flex items-center gap-3">
117
+ <Image className="w-8 h-8 text-indigo-600" />
118
+ <h1 className="text-3xl font-bold text-gray-800">
119
+ RGTB Aerial View Detection
120
+ </h1>
121
+ </div>
122
+ <div className="bg-indigo-100 px-4 py-2 rounded-lg">
123
+ <span className="text-sm font-medium text-indigo-700">Model Ready</span>
124
+ </div>
125
+ </div>
126
+
127
+ <div className="mb-6 p-4 bg-gray-50 rounded-lg border border-gray-200">
128
+ <p className="text-sm text-gray-600">
129
+ <span className="font-semibold">Model:</span> {MODEL_ID}
130
+ </p>
131
+ </div>
132
+
133
+ {/* Drag & Drop Area */}
134
+ {!preview ? (
135
+ <div
136
+ onDragEnter={handleDrag}
137
+ onDragLeave={handleDrag}
138
+ onDragOver={handleDrag}
139
+ onDrop={handleDrop}
140
+ className={`border-2 border-dashed rounded-xl p-16 text-center transition-all ${
141
+ dragActive
142
+ ? 'border-indigo-600 bg-indigo-50 scale-105'
143
+ : 'border-gray-300 bg-gray-50 hover:border-indigo-400'
144
+ }`}
145
+ >
146
+ <input
147
+ type="file"
148
+ id="file-upload"
149
+ className="hidden"
150
+ onChange={handleChange}
151
+ accept="image/*"
152
+ />
153
+ <label
154
+ htmlFor="file-upload"
155
+ className="cursor-pointer flex flex-col items-center"
156
+ >
157
+ <div className="w-20 h-20 bg-indigo-100 rounded-full flex items-center justify-center mb-4">
158
+ <Upload className="w-10 h-10 text-indigo-600" />
159
+ </div>
160
+ <p className="text-xl font-semibold text-gray-700 mb-2">
161
+ Drop your aerial image here
162
+ </p>
163
+ <p className="text-sm text-gray-500 mb-4">
164
+ or click to browse files
165
+ </p>
166
+ <div className="flex gap-2 text-xs text-gray-400">
167
+ <span className="px-3 py-1 bg-white rounded-full border border-gray-200">JPEG</span>
168
+ <span className="px-3 py-1 bg-white rounded-full border border-gray-200">PNG</span>
169
+ <span className="px-3 py-1 bg-white rounded-full border border-gray-200">WebP</span>
170
+ </div>
171
+ </label>
172
+ </div>
173
+ ) : (
174
+ <div className="space-y-4">
175
+ {/* Image Preview */}
176
+ <div className="relative rounded-xl overflow-hidden border-2 border-gray-200 bg-gray-900">
177
+ <img
178
+ src={preview}
179
+ alt="Preview"
180
+ className="w-full h-auto max-h-[500px] object-contain"
181
+ />
182
+ <button
183
+ onClick={clearImage}
184
+ className="absolute top-4 right-4 bg-red-500 text-white p-2 rounded-full hover:bg-red-600 transition-all shadow-lg hover:scale-110"
185
+ >
186
+ <X className="w-5 h-5" />
187
+ </button>
188
+ <div className="absolute bottom-4 left-4 bg-black/70 backdrop-blur-sm text-white px-4 py-2 rounded-lg text-sm">
189
+ {file.name}
190
+ </div>
191
+ </div>
192
+
193
+ {/* Predict Button */}
194
+ <button
195
+ onClick={handlePredict}
196
+ disabled={loading}
197
+ className="w-full bg-gradient-to-r from-indigo-600 to-purple-600 text-white py-4 rounded-xl font-semibold hover:from-indigo-700 hover:to-purple-700 transition-all disabled:from-gray-400 disabled:to-gray-400 disabled:cursor-not-allowed flex items-center justify-center gap-3 shadow-lg hover:shadow-xl transform hover:scale-[1.02]"
198
+ >
199
+ {loading ? (
200
+ <>
201
+ <Loader2 className="w-6 h-6 animate-spin" />
202
+ <span>Analyzing Image...</span>
203
+ </>
204
+ ) : (
205
+ <>
206
+ <Image className="w-6 h-6" />
207
+ <span>Run Detection</span>
208
+ </>
209
+ )}
210
+ </button>
211
+ </div>
212
+ )}
213
+
214
+ {/* Error Display */}
215
+ {error && (
216
+ <div className="mt-6 bg-red-50 border-l-4 border-red-500 rounded-lg p-4">
217
+ <div className="flex items-start gap-3">
218
+ <div className="flex-shrink-0">
219
+ <svg className="w-5 h-5 text-red-500 mt-0.5" fill="currentColor" viewBox="0 0 20 20">
220
+ <path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clipRule="evenodd"/>
221
+ </svg>
222
+ </div>
223
+ <div>
224
+ <p className="text-red-800 font-semibold">Error</p>
225
+ <p className="text-red-700 text-sm mt-1">{error}</p>
226
+ </div>
227
+ </div>
228
+ </div>
229
+ )}
230
+
231
+ {/* Results Display */}
232
+ {result && (
233
+ <div className="mt-6 bg-gradient-to-br from-green-50 to-emerald-50 border-2 border-green-200 rounded-xl p-6 shadow-lg">
234
+ <h2 className="text-2xl font-bold text-gray-800 mb-4 flex items-center gap-2">
235
+ <svg className="w-6 h-6 text-green-600" fill="currentColor" viewBox="0 0 20 20">
236
+ <path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clipRule="evenodd"/>
237
+ </svg>
238
+ Detection Results
239
+ </h2>
240
+
241
+ {/* Check if it's object detection format (with boxes) */}
242
+ {Array.isArray(result) && result[0]?.box ? (
243
+ <div className="space-y-3">
244
+ <p className="text-gray-700 font-medium mb-3">
245
+ Detected {result.length} object(s):
246
+ </p>
247
+ {result.map((item, idx) => (
248
+ <div key={idx} className="bg-white rounded-lg p-4 shadow border border-gray-200">
249
+ <div className="flex justify-between items-center mb-2">
250
+ <span className="font-bold text-lg text-gray-800">{item.label}</span>
251
+ <span className="bg-green-100 text-green-800 px-3 py-1 rounded-full text-sm font-semibold">
252
+ {(item.score * 100).toFixed(1)}%
253
+ </span>
254
+ </div>
255
+ <div className="text-xs text-gray-500 grid grid-cols-2 gap-2">
256
+ <div>Box: x={Math.round(item.box.xmin)}, y={Math.round(item.box.ymin)}</div>
257
+ <div>Size: {Math.round(item.box.xmax - item.box.xmin)}×{Math.round(item.box.ymax - item.box.ymin)}</div>
258
+ </div>
259
+ </div>
260
+ ))}
261
+ </div>
262
+ ) : Array.isArray(result) && result[0]?.label ? (
263
+ // Classification results
264
+ <div className="space-y-3">
265
+ {result.slice(0, 5).map((item, idx) => (
266
+ <div key={idx} className="bg-white rounded-lg p-3 shadow">
267
+ <div className="flex items-center gap-3 mb-2">
268
+ <span className="font-semibold text-gray-700 min-w-[100px]">
269
+ {item.label}
270
+ </span>
271
+ <div className="flex-1 bg-gray-200 rounded-full h-8 overflow-hidden">
272
+ <div
273
+ className="bg-gradient-to-r from-indigo-500 to-purple-500 h-full rounded-full flex items-center justify-end px-3 transition-all duration-500"
274
+ style={{ width: `${(item.score * 100).toFixed(1)}%` }}
275
+ >
276
+ <span className="text-xs text-white font-bold">
277
+ {(item.score * 100).toFixed(1)}%
278
+ </span>
279
+ </div>
280
+ </div>
281
+ </div>
282
+ </div>
283
+ ))}
284
+ </div>
285
+ ) : (
286
+ // Raw JSON output
287
+ <div className="bg-white rounded-lg p-4 shadow max-h-96 overflow-auto">
288
+ <pre className="text-sm text-gray-800 whitespace-pre-wrap">
289
+ {JSON.stringify(result, null, 2)}
290
+ </pre>
291
+ </div>
292
+ )}
293
+ </div>
294
+ )}
295
+ </div>
296
+
297
+ {/* Info Card */}
298
+ <div className="mt-6 bg-white rounded-xl shadow-lg p-6">
299
+ <h3 className="font-bold text-gray-800 mb-3 text-lg">How to use:</h3>
300
+ <ol className="list-decimal list-inside space-y-2 text-gray-600">
301
+ <li>Drag and drop an aerial image or click to upload</li>
302
+ <li>Click "Run Detection" to analyze the image</li>
303
+ <li>View detected objects with confidence scores and bounding boxes</li>
304
+ </ol>
305
+ <div className="mt-4 p-3 bg-blue-50 rounded-lg border border-blue-200">
306
+ <p className="text-sm text-blue-800">
307
+ <strong>Note:</strong> First run may take 20-30 seconds while the model loads
308
+ </p>
309
+ </div>
310
+ </div>
311
+ </div>
312
+ </div>
313
+ );
314
+ }