dillonlaird commited on
Commit
8040aeb
1 Parent(s): 6723494
instance-labeler DELETED
@@ -1 +0,0 @@
1
- Subproject commit f4dde8b7122daba0a4cf869dc78d450fe80ffe81
 
 
instance-labeler/.eslintrc.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ {
2
+ "extends": "next/core-web-vitals"
3
+ }
instance-labeler/.gitignore ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2
+
3
+ # dependencies
4
+ /node_modules
5
+ /.pnp
6
+ .pnp.js
7
+
8
+ # testing
9
+ /coverage
10
+
11
+ # next.js
12
+ /.next/
13
+ /out/
14
+
15
+ # production
16
+ /build
17
+
18
+ # misc
19
+ .DS_Store
20
+ *.pem
21
+
22
+ # debug
23
+ npm-debug.log*
24
+ yarn-debug.log*
25
+ yarn-error.log*
26
+
27
+ # local env files
28
+ .env*.local
29
+
30
+ # vercel
31
+ .vercel
32
+
33
+ # typescript
34
+ *.tsbuildinfo
35
+ next-env.d.ts
instance-labeler/README.md ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
2
+
3
+ ## Getting Started
4
+
5
+ First, run the development server:
6
+
7
+ ```bash
8
+ npm run dev
9
+ # or
10
+ yarn dev
11
+ # or
12
+ pnpm dev
13
+ ```
14
+
15
+ Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
16
+
17
+ You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
18
+
19
+ This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
20
+
21
+ ## Learn More
22
+
23
+ To learn more about Next.js, take a look at the following resources:
24
+
25
+ - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
26
+ - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
27
+
28
+ You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
29
+
30
+ ## Deploy on Vercel
31
+
32
+ The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
33
+
34
+ Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
instance-labeler/app/canvas.tsx ADDED
@@ -0,0 +1,362 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use client';
2
+ import { useState, useRef, useEffect } from 'react';
3
+ import axios from 'axios';
4
+ import useImage from 'use-image';
5
+ import Konva from 'konva';
6
+ import { Stage, Image, Layer } from 'react-konva';
7
+ import { BASE_URL } from './constants';
8
+ import Dropdown from './dropdown';
9
+
10
+
11
+ interface RGB {
12
+ r: number;
13
+ g: number;
14
+ b: number;
15
+ }
16
+
17
+ interface Box {
18
+ x: number;
19
+ y: number;
20
+ width: number;
21
+ height: number;
22
+ }
23
+
24
+ const maskFilter = (imageData: ImageData, color: RGB) => {
25
+ const nPixels = imageData.data.length;
26
+ for (let i = 0; i < nPixels; i += 4) {
27
+ const r = imageData.data[i];
28
+ const g = imageData.data[i + 1];
29
+ const b = imageData.data[i + 2];
30
+
31
+ if (r === 0 && g === 0 && b === 0) {
32
+ imageData.data[i + 3] = 0;
33
+ } else {
34
+ imageData.data[i] = color.r;
35
+ imageData.data[i + 1] = color.g;
36
+ imageData.data[i + 2] = color.b;
37
+ imageData.data[i + 3] = 128;
38
+ }
39
+ }
40
+ };
41
+
42
+ export default function Canvas({ imageUrl, imageName }: { imageUrl: string, imageName: string }) {
43
+ const [image] = useImage(imageUrl, 'anonymous');
44
+ const stageRef = useRef<Konva.Stage>(null);
45
+ const layerRef = useRef<Konva.Layer>(null);
46
+ const groupRef = useRef<Konva.Group[]>([]);
47
+
48
+ const pointsRef = useRef<Array<[number, number]>>([]);
49
+ const labelsRef = useRef<Array<number>>([]);
50
+
51
+ const instanceColorsRef = useRef<string[]>([]);
52
+ const [classList, setClassList] = useState<string[]>([]);
53
+ const classOptionsRef = useRef<string[]>([]);
54
+ const classSelectionRef = useRef<string>('');
55
+
56
+ const [useLatest, setUseLatest] = useState<boolean>(false);
57
+ const selectedInstanceRef = useRef<number>(-1);
58
+
59
+ const [selectedInstanceStyle, setSelectedInstanceStyle] = useState<boolean>(false);
60
+
61
+ const clearLayers = () => {
62
+ groupRef.current.forEach((group) => {
63
+ group.destroy();
64
+ });
65
+ groupRef.current = [];
66
+ layerRef.current?.draw();
67
+ pointsRef.current = [];
68
+ labelsRef.current = [];
69
+ instanceColorsRef.current = [Konva.Util.getRandomColor()];
70
+ setClassList(_ => ([]));
71
+ }
72
+
73
+ useEffect(() => {
74
+ clearLayers();
75
+ }, [imageUrl]);
76
+
77
+ const showMask = (data: string, group: Konva.Group) => {
78
+ const layer = layerRef.current;
79
+ if (!layer) return;
80
+
81
+ const color = Konva.Util.getRGB(
82
+ instanceColorsRef.current[instanceColorsRef.current.length - 1]
83
+ );
84
+
85
+ const width = image?.width;
86
+ const height = image?.height;
87
+
88
+ const maskObj = new window.Image();
89
+ maskObj.onload = () => {
90
+ const image = new Konva.Image({
91
+ x: 0,
92
+ y: 0,
93
+ image: maskObj,
94
+ width: width,
95
+ height: height,
96
+ });
97
+ image.filters([(imageData: ImageData) => maskFilter(imageData, color)]);
98
+ image.cache();
99
+
100
+ group.add(image);
101
+
102
+ if (groupRef.current.length === 0)
103
+ groupRef.current.push(group);
104
+
105
+ layer.add(group);
106
+ layer.draw()
107
+ };
108
+ maskObj.src = `data:image/png;base64,${data}`;
109
+ setUseLatest(true);
110
+ }
111
+
112
+ const showBox = (box: Box, group: Konva.Group) => {
113
+ const layer = layerRef.current;
114
+ if (!layer) return;
115
+
116
+ const color = Konva.Util.getRGB(
117
+ instanceColorsRef.current[instanceColorsRef.current.length - 1]
118
+ );
119
+
120
+ const width = image?.width;
121
+ const height = image?.height;
122
+
123
+ function rgbToHex(r, g, b) {
124
+ return '#' + (1 << 24 | r << 16 | g << 8 | b).toString(16).slice(1);
125
+ }
126
+
127
+ const rect = new Konva.Rect({
128
+ x: box.x,
129
+ y: box.y,
130
+ width: box.width,
131
+ height: box.height,
132
+ stroke: rgbToHex(color.r, color.g, color.b),
133
+ storkeWidth: 2,
134
+ });
135
+
136
+ if (groupRef.current.length === 0)
137
+ groupRef.current.push(group);
138
+ group.add(rect);
139
+ layer.add(group);
140
+ layer.draw();
141
+ setUseLatest(true);
142
+ }
143
+
144
+ const handleStageClick = async (e: Konva.KonvaEventObject<MouseEvent>) => {
145
+ const stage = stageRef.current;
146
+ const layer = layerRef.current;
147
+ if (!stage || !layer) return;
148
+
149
+ const pos = e.target.getStage()?.getPointerPosition();
150
+ if (!pos) return;
151
+
152
+ let labels = labelsRef.current;
153
+ if (e.evt.button === 2) {
154
+ labels = [...labels, 0];
155
+ } else {
156
+ labels = [...labels, 1];
157
+ }
158
+ labelsRef.current = labels;
159
+
160
+ // re-draw last layer so we don't overlap masks on clicks
161
+ if (groupRef.current.length > 0) {
162
+ groupRef.current[groupRef.current.length - 1].destroy();
163
+ } else {
164
+ groupRef.current.push(new Konva.Group());
165
+ }
166
+
167
+ layer.draw();
168
+
169
+ let points = pointsRef.current;
170
+ points = [...points, [pos.x, pos.y]];
171
+ pointsRef.current = points;
172
+
173
+ const res = await axios.post(
174
+ `${BASE_URL}/v1/get_label_preds/${imageName}`,
175
+ {
176
+ points: points,
177
+ labels: labels,
178
+ }
179
+ );
180
+ showMask(res.data.masks[0], groupRef.current[groupRef.current.length - 1]);
181
+ }
182
+
183
+ const pinInstance = async () => {
184
+ const layer = layerRef.current;
185
+ if (!layer) return;
186
+
187
+ const groupList = groupRef.current;
188
+ // if (groupList.length === 0) return;
189
+
190
+ if (classSelectionRef.current === '') {
191
+ alert('Please select a class');
192
+ } else {
193
+ groupList.push(new Konva.Group());
194
+ pointsRef.current = [];
195
+ labelsRef.current = [];
196
+ instanceColorsRef.current.push(Konva.Util.getRandomColor());
197
+ setClassList(prev => ([...prev, classSelectionRef.current]))
198
+ setUseLatest(false);
199
+ }
200
+ }
201
+
202
+ const deleteSelectedInstance = () => {
203
+ const layer = layerRef.current;
204
+ if (!layer) return;
205
+ if (selectedInstanceRef.current === -1) return;
206
+
207
+ // always 1 extra group for the next instance
208
+ if (groupRef.current.length <= 1) return;
209
+
210
+ groupRef.current[selectedInstanceRef.current].destroy();
211
+ groupRef.current.splice(selectedInstanceRef.current, 1);
212
+ setClassList([
213
+ ...classList.slice(0, selectedInstanceRef.current),
214
+ ...classList.slice(selectedInstanceRef.current + 1)
215
+ ]);
216
+ instanceColorsRef.current.splice(selectedInstanceRef.current, 1);
217
+ layer.draw()
218
+ // reset selectedInstanceRef?
219
+ selectedInstanceRef.current = -1;
220
+ }
221
+
222
+ const submitLabels = async () => {
223
+ const groupList = groupRef.current;
224
+ if (groupList.length === 0) return;
225
+
226
+ const masks = [];
227
+ const length = useLatest ? groupList.length : groupList.length - 1;
228
+ for (let i = 0; i < length; i++) {
229
+ const labelData = groupList[i].toDataURL({ x: 0, y: 0, width: image?.width, height: image?.height });
230
+ masks.push(labelData);
231
+ }
232
+
233
+ const res = await axios.put(`${BASE_URL}/v1/label_image/${imageName}`,
234
+ {
235
+ masks: masks,
236
+ labels: classList,
237
+ }
238
+ );
239
+
240
+ pointsRef.current = [];
241
+ labelsRef.current = [];
242
+ clearLayers();
243
+ }
244
+
245
+ const getLabels = async () => {
246
+ clearLayers();
247
+ try {
248
+ const res = await axios.get(`${BASE_URL}/v1/get_labels/${imageName}`);
249
+ classOptionsRef.current = [...new Set(res.data.labels)];
250
+ // add an initial group to start with
251
+ groupRef.current.push(new Konva.Group());
252
+ for (let i = 0; i < res.data.masks.length; i++) {
253
+ showMask(res.data.masks[i], groupRef.current[groupRef.current.length - 1]);
254
+ classSelectionRef.current = res.data.labels[i];
255
+ pinInstance();
256
+ }
257
+ } catch (err) {
258
+ console.log(err);
259
+ }
260
+ }
261
+
262
+ const predLabels = async () => {
263
+ const length = useLatest ? groupRef.current.length : groupRef.current.length - 1;
264
+ if (groupRef.current.length === 0) {
265
+ alert('Please pin an instance');
266
+ return
267
+ }
268
+ const mask = groupRef.current[length - 1].toDataURL({ x: 0, y: 0, width: image?.width, height: image?.height });
269
+
270
+ clearLayers();
271
+ try {
272
+ const res = await axios.post(`${BASE_URL}/v1/get_multi_label_preds/${imageName}`, {
273
+ mask: mask,
274
+ label: classList[length - 1],
275
+ });
276
+ groupRef.current.push(new Konva.Group());
277
+ for (let i = 0; i < res.data.masks.length; i++) {
278
+ showMask(res.data.masks[i], groupRef.current[groupRef.current.length - 1]);
279
+ classSelectionRef.current = res.data.labels[i];
280
+ pinInstance();
281
+ }
282
+ } catch (err) {
283
+ console.log(err);
284
+ }
285
+ }
286
+
287
+ const getBboxes = async () => {
288
+ clearLayers();
289
+ try {
290
+ const res = await axios.get(`${BASE_URL}/v1/get_labels/${imageName}`);
291
+ classOptionsRef.current = [...new Set(res.data.labels)];
292
+ groupRef.current.push(new Konva.Group());
293
+ for (let i = 0; i < res.data.bboxes.length; i++) {
294
+ showBox(res.data.bboxes[i], groupRef.current[groupRef.current.length - 1]);
295
+ classSelectionRef.current = res.data.labels[i];
296
+ pinInstance();
297
+ }
298
+ } catch (err) {
299
+ console.log(err);
300
+ }
301
+ }
302
+
303
+ return (
304
+ <div className="flex flex-row columns-2 gap-20">
305
+ <div className="">
306
+ <h1>Instance Class List</h1>
307
+ <ul>
308
+ {classList.map((item, index) => (
309
+ <li key={index}>
310
+ <div
311
+ // style={{ background: instanceColorsRef.current[index], border: '2px solid red'}}
312
+ style={{
313
+ background: instanceColorsRef.current[index],
314
+ border: selectedInstanceRef.current === index && selectedInstanceStyle ? '2px solid red' : 'none'
315
+ }}
316
+ onClick={() => {
317
+ selectedInstanceRef.current = index;
318
+ setSelectedInstanceStyle(prev => !prev)
319
+ }}
320
+ >{item}</div>
321
+ </li>
322
+ ))}
323
+ </ul>
324
+ <br />
325
+ <Dropdown
326
+ options={classOptionsRef}
327
+ selectedOption={classSelectionRef}
328
+ />
329
+ </div>
330
+ <div className="">
331
+ <Stage
332
+ width={image?.width}
333
+ height={image?.height}
334
+ ref={stageRef}
335
+ onClick={handleStageClick}
336
+ onContextMenu={(e) => e.evt.preventDefault()}
337
+ >
338
+ <Layer ref={layerRef}>
339
+ <Image image={image} alt="image" />
340
+ </Layer>
341
+ </Stage>
342
+ <br />
343
+ <button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-2"
344
+ onClick={clearLayers}>Clear All</button>&nbsp;
345
+
346
+ <button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-2"
347
+ onClick={getBboxes}>Load BBoxes</button>&nbsp;
348
+ <button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-2"
349
+ onClick={getLabels}>Load Masks</button>&nbsp;
350
+
351
+ <button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-2"
352
+ onClick={deleteSelectedInstance}>Delete Selected Instance</button>&nbsp;
353
+ <button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-2"
354
+ onClick={pinInstance}>Pin Instance</button>&nbsp;
355
+ <button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-2"
356
+ onClick={submitLabels}>Save All</button>&nbsp;
357
+ <button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-2"
358
+ onClick={predLabels}>Auto Label</button>
359
+ </div>
360
+ </div>
361
+ );
362
+ };
instance-labeler/app/constants.tsx ADDED
@@ -0,0 +1 @@
 
 
1
+ export const BASE_URL = 'http://localhost:7860'
instance-labeler/app/dropdown.tsx ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState } from 'react';
2
+
3
+ interface DropdownProps {
4
+ options: React.RefObject<string[]>;
5
+ selectedOption: React.RefObject<string>;
6
+ }
7
+
8
+
9
+ export default function Dropdown({ options, selectedOption }: DropdownProps) {
10
+ const [value, setValue] = useState<string>('');
11
+ const [newValue, setNewValue] = useState<string>('');
12
+
13
+ const handleAddItem = () => {
14
+ options.current?.push(newValue);
15
+ selectedOption.current = newValue;
16
+ setNewValue('');
17
+ };
18
+
19
+ const handleSelectItem = (event: React.ChangeEvent<HTMLSelectElement>) => {
20
+ selectedOption.current = event.target.value;
21
+ setValue(event.target.value);
22
+ };
23
+
24
+ return (
25
+ <div>
26
+ <select
27
+ value={value}
28
+ onChange={(event) => handleSelectItem(event)}
29
+ style={{ color: 'black' }}
30
+ >
31
+ {options.current?.map((option: string, index: number) => (
32
+ <option
33
+ value={option}
34
+ key={index}
35
+ style={{ color: 'black' }}
36
+ >
37
+ {option}
38
+ </option>
39
+ ))}
40
+ </select>
41
+ <br />
42
+ <br />
43
+ <input
44
+ type="text"
45
+ style={{ color: 'black' }}
46
+ value={newValue}
47
+ onChange={(e) => setNewValue(e.target.value)}
48
+ />
49
+ <br />
50
+ <br />
51
+ <button className="bg-gray-400 hover:bg-blue-700 text-white font-bold py-1 px-1"
52
+ onClick={handleAddItem}>Add</button>&nbsp;
53
+ </div>
54
+ );
55
+ };
instance-labeler/app/favicon.ico ADDED
instance-labeler/app/globals.css ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+
5
+ :root {
6
+ --foreground-rgb: 0, 0, 0;
7
+ --background-start-rgb: 214, 219, 220;
8
+ --background-end-rgb: 255, 255, 255;
9
+ }
10
+
11
+ @media (prefers-color-scheme: dark) {
12
+ :root {
13
+ --foreground-rgb: 255, 255, 255;
14
+ --background-start-rgb: 0, 0, 0;
15
+ --background-end-rgb: 0, 0, 0;
16
+ }
17
+ }
18
+
19
+ body {
20
+ color: rgb(var(--foreground-rgb));
21
+ background: linear-gradient(
22
+ to bottom,
23
+ transparent,
24
+ rgb(var(--background-end-rgb))
25
+ )
26
+ rgb(var(--background-start-rgb));
27
+ }
instance-labeler/app/layout.tsx ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import './globals.css'
2
+ import { Inter } from 'next/font/google'
3
+
4
+ const inter = Inter({ subsets: ['latin'] })
5
+
6
+ export const metadata = {
7
+ title: 'Create Next App',
8
+ description: 'Generated by create next app',
9
+ }
10
+
11
+ export default function RootLayout({
12
+ children,
13
+ }: {
14
+ children: React.ReactNode
15
+ }) {
16
+ return (
17
+ <html lang="en">
18
+ <body className={inter.className}>{children}</body>
19
+ </html>
20
+ )
21
+ }
instance-labeler/app/page.tsx ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use client';
2
+ import { useState, useEffect, useRef, ChangeEvent } from 'react';
3
+ import axios from 'axios';
4
+ import Canvas from './canvas';
5
+ import { BASE_URL } from './constants';
6
+
7
+
8
+ export default function Home() {
9
+ const [images, setImages] = useState<string[]>([]);
10
+ const [displayImage, setDisplayImage] = useState<string>('/loading.png');
11
+ const [displayIndex, setDisplayIndex] = useState<number>(0);
12
+
13
+ const getAllImages = async () => {
14
+ const res = await axios.get(`${BASE_URL}/v1/get_all_images`);
15
+ setImages(res.data.images);
16
+ }
17
+
18
+ const setCurrentImage = async () => {
19
+ if (images[displayIndex] === undefined) return;
20
+ const res = await axios.get(`${BASE_URL}/v1/get_image/${images[displayIndex]}`);
21
+ const contentType = res.headers['content-type'];
22
+ setDisplayImage(`data:${contentType};base64,${res.data}`);
23
+ }
24
+
25
+ const handleFileUpload = async (e: ChangeEvent<HTMLInputElement>) => {
26
+ const reader = new FileReader();
27
+ reader.addEventListener("load", async () => {
28
+ const newImageName = `img${images.length + 1}`;
29
+ const res = await axios.put(`${BASE_URL}/v1/upload_image/${newImageName}`,
30
+ {
31
+ image: reader.result,
32
+ }
33
+ );
34
+ setImages([...images, newImageName]);
35
+ setDisplayIndex(images.length);
36
+ }, false);
37
+
38
+ if (e.target.files) {
39
+ for (let i = 0; i < e.target.files.length; i++) {
40
+ reader.readAsDataURL(e.target.files[i]);
41
+ }
42
+ }
43
+ }
44
+
45
+ useEffect(() => {
46
+ getAllImages();
47
+ }, [])
48
+
49
+ useEffect(() => {
50
+ setCurrentImage();
51
+ }, [images, displayIndex])
52
+
53
+ return (
54
+ <>
55
+ <Canvas imageUrl={displayImage} imageName={images[displayIndex]} />
56
+ <br />
57
+ <br />
58
+ <div className="float right">
59
+ <button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-2"
60
+ onClick={() => {
61
+ setDisplayIndex(displayIndex - 1 >= 0 ? displayIndex - 1 : images.length - 1);
62
+ }}>Previous</button>&nbsp;
63
+ <button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-2"
64
+ onClick={() => {
65
+ setDisplayIndex(displayIndex + 1 < images.length ? displayIndex + 1 : 0);
66
+ }}>Next</button>
67
+ </div>
68
+ <br />
69
+ <input type="file" id="image" accept="image/png, image/jpeg" onChange={handleFileUpload} />
70
+ </>
71
+ )
72
+ };
instance-labeler/next.config.js ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /** @type {import('next').NextConfig} */
2
+ const nextConfig = {
3
+ output: 'export',
4
+ basePath: '',
5
+ reactStrictMode: true,
6
+ experimental: {
7
+ appData: true,
8
+ esmExternals: "loose", // required to make Konva and react-konva work
9
+ },
10
+ typescript: {
11
+ ignoreBuildErrors: true,
12
+ },
13
+ webpack: (config) => {
14
+ config.externals = [...config.externals, { canvas: "canvas" }]; // required to make Konva and react-konva work
15
+ return config;
16
+ },
17
+ };
18
+
19
+ module.exports = nextConfig
instance-labeler/package.json ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "instance-labeler",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "dev": "next dev",
7
+ "build": "next build",
8
+ "start": "next start",
9
+ "lint": "next lint"
10
+ },
11
+ "dependencies": {
12
+ "@types/node": "20.2.5",
13
+ "@types/react": "18.2.9",
14
+ "@types/react-dom": "18.2.4",
15
+ "autoprefixer": "10.4.14",
16
+ "axios": "^1.4.0",
17
+ "canvas": "^2.11.2",
18
+ "eslint": "8.42.0",
19
+ "eslint-config-next": "13.4.4",
20
+ "konva": "^9.2.0",
21
+ "next": "13.4.4",
22
+ "postcss": "8.4.24",
23
+ "react": "18.2.0",
24
+ "react-dom": "18.2.0",
25
+ "react-konva": "^18.2.9",
26
+ "tailwindcss": "3.3.2",
27
+ "typescript": "5.1.3",
28
+ "use-image": "^1.1.0"
29
+ }
30
+ }
instance-labeler/postcss.config.js ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ module.exports = {
2
+ plugins: {
3
+ tailwindcss: {},
4
+ autoprefixer: {},
5
+ },
6
+ }
instance-labeler/public/loading.png ADDED
instance-labeler/public/next.svg ADDED
instance-labeler/public/vercel.svg ADDED
instance-labeler/tailwind.config.js ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /** @type {import('tailwindcss').Config} */
2
+ module.exports = {
3
+ content: [
4
+ './pages/**/*.{js,ts,jsx,tsx,mdx}',
5
+ './components/**/*.{js,ts,jsx,tsx,mdx}',
6
+ './app/**/*.{js,ts,jsx,tsx,mdx}',
7
+ ],
8
+ theme: {
9
+ extend: {
10
+ backgroundImage: {
11
+ 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
12
+ 'gradient-conic':
13
+ 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
14
+ },
15
+ },
16
+ },
17
+ plugins: [],
18
+ }
instance-labeler/tsconfig.json ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "compilerOptions": {
3
+ "target": "es2020",
4
+ "lib": ["dom", "dom.iterable", "esnext"],
5
+ "allowJs": true,
6
+ "skipLibCheck": true,
7
+ "strict": true,
8
+ "forceConsistentCasingInFileNames": true,
9
+ "noEmit": true,
10
+ "esModuleInterop": true,
11
+ "module": "esnext",
12
+ "moduleResolution": "node",
13
+ "resolveJsonModule": true,
14
+ "isolatedModules": true,
15
+ "jsx": "preserve",
16
+ "incremental": true,
17
+ "plugins": [
18
+ {
19
+ "name": "next"
20
+ }
21
+ ],
22
+ "paths": {
23
+ "@/*": ["./*"]
24
+ }
25
+ },
26
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
27
+ "exclude": ["node_modules"]
28
+ }
instance-labeler/yarn.lock ADDED
The diff for this file is too large to render. See raw diff