streamlit-label-kit 0.0.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,16 @@
1
+ import os
2
+ import streamlit.components.v1 as components
3
+ from typing import List
4
+
5
+ import numpy as np
6
+ import matplotlib.pyplot as plt
7
+ from streamlit_label_kit import IS_RELEASE
8
+
9
+ if IS_RELEASE:
10
+ absolute_path = os.path.dirname(os.path.abspath(__file__))
11
+ build_path = os.path.join(absolute_path, "frontend/build")
12
+ _component_func = components.declare_component("st-label-kit", path=build_path)
13
+ else:
14
+ _component_func = components.declare_component(
15
+ "st-label-kit", url="http://localhost:3000"
16
+ )
@@ -0,0 +1,169 @@
1
+ from streamlit.components.v1.components import CustomComponent
2
+ from typing import List
3
+
4
+ import streamlit.elements.image as st_image
5
+ from PIL import Image
6
+ import numpy as np
7
+ import matplotlib.pyplot as plt
8
+ from hashlib import md5
9
+ from typing import Literal, Union
10
+
11
+ from . import _component_func
12
+
13
+ def get_colormap(label_names, colormap_name="gist_rainbow"):
14
+ colormap = {}
15
+ cmap = plt.get_cmap(colormap_name)
16
+ for idx, l in enumerate(label_names):
17
+ rgb = [int(d) for d in np.array(cmap(float(idx) / len(label_names))) * 255][:3]
18
+ colormap[l] = "#%02x%02x%02x" % tuple(rgb)
19
+ return colormap
20
+
21
+ SELECT_HEIGHT = 60
22
+ RADIO_HEGIHT = 34
23
+ UI_HEIGHT = 34
24
+ UI_WIDTH = 168
25
+
26
+ def _calc_size(size) :
27
+ if isinstance(size, (int, float)):
28
+ return size, size
29
+
30
+ match size:
31
+ case "small":
32
+ ui_height = UI_HEIGHT
33
+ ui_width = UI_WIDTH
34
+ case "medium":
35
+ ui_height = int(2 * UI_HEIGHT)
36
+ ui_width = int(1.25 * UI_WIDTH)
37
+ case "large":
38
+ ui_height = int(4 * UI_HEIGHT)
39
+ ui_width = int(1.5 * UI_WIDTH)
40
+ case _:
41
+ ui_height = int(2 * UI_HEIGHT)
42
+ ui_width = int(1.25 * UI_WIDTH)
43
+
44
+ return ui_height, ui_width
45
+
46
+ def annotation(
47
+
48
+ # add "Output Mode" = "px values", **"relative values mode", "yolo", etc.
49
+ # "original pixel value mode", "resized pixel value mode" ==> on output, give metadata as well.
50
+ image_path: str = None,
51
+ label_list: list[str] = [],
52
+ default_label_index: int=None,
53
+ image_height: int=512,
54
+ image_width: int=512,
55
+
56
+ classification: bool = False,
57
+ multi_select: bool = False,
58
+
59
+ ui_position: Literal["right", "left"] = "right",
60
+ class_select_position: Literal["right", "left", "bottom"] = None,
61
+ meta_editor_position: Literal["right", "left"] = None,
62
+
63
+ class_select_type: Literal["select", "radio"] = "radio",
64
+ meta_editor: bool = False,
65
+ edit_description: bool = False,
66
+
67
+ ui_size: Literal["small", "medium", "large"]= "small",
68
+ ui_left_size: Union[Literal["small", "medium", "large"], int] = None,
69
+ ui_bottom_size: Union[Literal["small", "medium", "large"], int] = None,
70
+ ui_right_size: Union[Literal["small", "medium", "large"], int] = None,
71
+
72
+ ui_bottom_fill_width: bool = False,
73
+ ui_height: int = None,
74
+
75
+ read_only: bool = False,
76
+
77
+ key=None,
78
+ ) -> CustomComponent:
79
+ if (image_path):
80
+ image = Image.open(image_path)
81
+ image.thumbnail(size=(image_width, image_height))
82
+
83
+ if (not classification and not meta_editor):
84
+ return None
85
+
86
+ _class_select_pos = class_select_position or ui_position
87
+ _meta_editor_pos = meta_editor_position or ui_position
88
+ _edit_meta = not edit_description and meta_editor
89
+
90
+ _ui_height, _ui_width = _calc_size(ui_size)
91
+ _, _left_size = _calc_size(ui_left_size or ui_size)
92
+ _bottom_size, _ = _calc_size(ui_bottom_size or ui_size)
93
+ _, _right_size = _calc_size(ui_right_size or ui_size)
94
+
95
+ _select_type = "radio" if class_select_type != "select" else "select"
96
+
97
+ if image_path == None:
98
+ _image_url = ""
99
+ _default_label_list = []
100
+ _image_size = [0,0]
101
+
102
+ else:
103
+ _image_url = st_image.image_to_url(
104
+ image,
105
+ image.size[0],
106
+ True,
107
+ "RGB",
108
+ "PNG",
109
+ f"annotation-{md5(image.tobytes()).hexdigest()}-{key}",
110
+ )
111
+ _image_size = image.size
112
+ if _image_url.startswith("/"):
113
+ _image_url = _image_url[1:]
114
+
115
+ if multi_select and isinstance(default_label_index, list):
116
+ _default_label_list = [label_list[i] for i in default_label_index]
117
+ else:
118
+ _default_label_list = []
119
+
120
+
121
+ if ui_bottom_fill_width:
122
+ _ui_width = "100vw"
123
+
124
+ if ui_height:
125
+ _ui_height = ui_height
126
+
127
+ component_value = _component_func(
128
+ image_url=_image_url,
129
+ image_size=_image_size,
130
+ label_list=label_list,
131
+
132
+ ui_height=_ui_height,
133
+ ui_width=_ui_width,
134
+
135
+ default_label_idx=default_label_index,
136
+ key=key,
137
+
138
+ multi_select=multi_select,
139
+
140
+ edit_class=classification,
141
+ edit_meta=_edit_meta,
142
+ edit_description=meta_editor and edit_description,
143
+
144
+ class_select_type=_select_type,
145
+ meta_editor=meta_editor,
146
+
147
+ class_select_position=_class_select_pos,
148
+ meta_editor_position=_meta_editor_pos,
149
+
150
+ ui_left_size=_left_size,
151
+ ui_bottom_size=_bottom_size,
152
+ ui_right_size=_right_size,
153
+
154
+ read_only=read_only,
155
+
156
+ default_multi_label_list=_default_label_list,
157
+ label_type="annotation"
158
+ )
159
+
160
+ # component_value = _component_func(
161
+ # image_url=image_url,
162
+ # image_size=image.size,
163
+ # label_list=label_list,
164
+ # ui_width=ui_width,
165
+ # ui_height=RADIO_HEGIHT,
166
+ # key=image_url,
167
+ # label_type="detection"
168
+ # )
169
+ return component_value
@@ -0,0 +1,293 @@
1
+ import os
2
+ import streamlit.components.v1 as components
3
+ from streamlit.components.v1.components import CustomComponent
4
+ from typing import List
5
+
6
+ import streamlit as st
7
+ import streamlit.elements.image as st_image
8
+ from PIL import Image
9
+ import numpy as np
10
+ import matplotlib.pyplot as plt
11
+ from hashlib import md5
12
+ from typing import Literal, Union
13
+
14
+ from .__init__ import _component_func
15
+
16
+
17
+ def _get_colormap(label_names, colormap_name="gist_rainbow"):
18
+ colormap = {}
19
+ cmap = plt.get_cmap(colormap_name)
20
+ for idx, l in enumerate(label_names):
21
+ rgb = [int(d) for d in np.array(cmap(float(idx) / len(label_names))) * 255][:3]
22
+ colormap[l] = "#%02x%02x%02x" % tuple(rgb)
23
+ return colormap
24
+
25
+
26
+ SELECT_HEIGHT = 60
27
+ RADIO_HEGIHT = 34
28
+ UI_HEIGHT = 34
29
+ UI_WIDTH = 198
30
+
31
+ #'''
32
+ # bboxes:
33
+ # [[x,y,w,h],[x,y,w,h]]
34
+ # labels:
35
+ # [0,3]
36
+ #'''
37
+
38
+
39
+ def convert_bbox_format(
40
+ bbox: tuple[float, float, float, float],
41
+ input_format: Literal["XYWH", "XYXY", "CXYWH"],
42
+ output_format: Literal["XYWH", "XYXY", "CXYWH"],
43
+ ) -> tuple[float, float, float, float]:
44
+ """
45
+ Convert bounding box between specified formats.
46
+
47
+ Args:
48
+ bbox (Tuple[float, float, float, float]): The bounding box coordinates.
49
+ input_format (str): The format of the input bounding box.
50
+ output_format (str): The format to convert the bounding box to.
51
+
52
+ Returns:
53
+ Tuple[float, float, float, float]: The bounding box in the new format.
54
+ """
55
+ if input_format == output_format:
56
+ return bbox
57
+
58
+ x, y, w, h = 0, 0, 0, 0
59
+
60
+ # Unpack the bounding box based on the input format
61
+ if input_format == "XYXY":
62
+ x1, y1, x2, y2 = bbox
63
+ x, y, w, h = x1, y1, x2 - x1, y2 - y1
64
+ elif input_format == "XYWH":
65
+ x, y, w, h = bbox
66
+ elif input_format == "CXYWH":
67
+ cx, cy, w, h = bbox
68
+ x, y = cx - w / 2, cy - h / 2
69
+
70
+ # Convert to the output format
71
+ if output_format == "XYXY":
72
+ x1, y1, x2, y2 = x, y, x + w, y + h
73
+ return (x1, y1, x2, y2)
74
+ elif output_format == "XYWH":
75
+ return (x, y, w, h)
76
+ elif output_format == "CXYWH":
77
+ cx, cy = x + w / 2, y + h / 2
78
+ return (cx, cy, w, h)
79
+
80
+
81
+ def relative_to_absolute(
82
+ bbox: tuple[float, float, float, float], image_width: int, image_height: int
83
+ ) -> tuple[float, float, float, float]:
84
+ """
85
+ Convert relative bbox coordinates to absolute pixel coordinates.
86
+
87
+ Args:
88
+ bbox (Tuple[float, float, float, float]): The bounding box in relative format.
89
+ image_width (int): The width of the image in pixels.
90
+ image_height (int): The height of the image in pixels.
91
+
92
+ Returns:
93
+ Tuple[float, float, float, float]: The bounding box in absolute pixel coordinates.
94
+ """
95
+ rx1, ry1, rx2, ry2 = bbox # Assuming bbox in format REL_XYXY
96
+ ax1, ay1 = rx1 * image_width, ry1 * image_height
97
+ ax2, ay2 = rx2 * image_width, ry2 * image_height
98
+ return (ax1, ay1, ax2, ay2)
99
+
100
+
101
+ def absolute_to_relative(
102
+ bbox: tuple[float, float, float, float], image_width: int, image_height: int
103
+ ) -> tuple[float, float, float, float]:
104
+ """
105
+ Convert absolute pixel bbox coordinates to relative coordinates.
106
+
107
+ Args:
108
+ bbox (Tuple[float, float, float, float]): The bounding box in absolute pixel format.
109
+ image_width (int): The width of the image in pixels.
110
+ image_height (int): The height of the image in pixels.
111
+
112
+ Returns:
113
+ Tuple[float, float, float, float]: The bounding box in relative coordinates.
114
+ """
115
+ ax1, ay1, ax2, ay2 = bbox # Assuming bbox in format XYXY
116
+ rx1, ry1 = ax1 / image_width, ay1 / image_height
117
+ rx2, ry2 = ax2 / image_width, ay2 / image_height
118
+ return (rx1, ry1, rx2, ry2)
119
+
120
+
121
+ def _calc_size(size):
122
+ if isinstance(size, (int, float)):
123
+ return size, size
124
+
125
+ match size:
126
+ case "small":
127
+ ui_height = UI_HEIGHT
128
+ ui_width = UI_WIDTH
129
+ case "medium":
130
+ ui_height = int(2 * UI_HEIGHT)
131
+ ui_width = int(1.25 * UI_WIDTH)
132
+ case "large":
133
+ ui_height = int(4 * UI_HEIGHT)
134
+ ui_width = int(1.5 * UI_WIDTH)
135
+ case _:
136
+ ui_height = int(2 * UI_HEIGHT)
137
+ ui_width = int(1.25 * UI_WIDTH)
138
+
139
+ return ui_height, ui_width
140
+
141
+
142
+ def detection(
143
+ image_path,
144
+ label_list,
145
+ bboxes=None,
146
+ labels=[],
147
+ read_only=False,
148
+ infoDict={},
149
+ metaDatas: list[list[str]] = [],
150
+ bbox_format: Literal["XYWH", "XYXY", "CXYWH", "REL_XYWH", "REL_XYXY"] = "XYWH",
151
+ image_height=512,
152
+ image_width=512,
153
+ line_width=1.0,
154
+ ui_position: Literal["right", "left"] = "right",
155
+ class_select_position: Literal["right", "left", "bottom"] = None,
156
+ item_editor_position: Literal["right", "left"] = None,
157
+ item_selector_position: Literal["right", "left"] = None,
158
+ class_select_type: Literal["select", "radio"] = "select",
159
+ item_editor: bool = True,
160
+ item_selector: bool = True,
161
+ edit_meta: bool = True,
162
+ edit_description: bool = False,
163
+ ui_size: Literal["small", "medium", "large"] = "small",
164
+ ui_left_size: Union[Literal["small", "medium", "large"], int] = None,
165
+ ui_bottom_size: Union[Literal["small", "medium", "large"], int] = None,
166
+ ui_right_size: Union[Literal["small", "medium", "large"], int] = None,
167
+ key=None,
168
+ ) -> CustomComponent:
169
+
170
+ # Load Image and convert size
171
+ image = Image.open(image_path)
172
+ original_image_size = image.size
173
+ image.thumbnail(size=(image_width, image_height))
174
+
175
+ image_url = st_image.image_to_url(
176
+ image,
177
+ image.size[0],
178
+ True,
179
+ "RGB",
180
+ "PNG",
181
+ f"annotation-{md5(image.tobytes()).hexdigest()}-{key}",
182
+ )
183
+ if image_url.startswith("/"):
184
+ image_url = image_url[1:]
185
+
186
+ color_map = _get_colormap(label_list, colormap_name="gist_rainbow")
187
+
188
+ resized_image_size = image.size
189
+ scale = original_image_size[0] / resized_image_size[0]
190
+
191
+ # Configure UI position and size
192
+ _class_select_pos = class_select_position or ui_position
193
+ _item_editor_pos = item_editor_position or ui_position
194
+ _item_selector_pos = item_selector_position or ui_position
195
+ _edit_meta = not edit_description
196
+
197
+ _, _left_size = _calc_size(ui_left_size or ui_size)
198
+ _bottom_size, _ = _calc_size(ui_bottom_size or ui_size)
199
+ _, _right_size = _calc_size(ui_right_size or ui_size)
200
+
201
+ _select_type = "radio" if class_select_type != "select" else "select"
202
+
203
+ # Configure default labels, metaDatas
204
+ num_bboxes = len(bboxes)
205
+ if len(labels) > num_bboxes:
206
+ labels = labels[:num_bboxes]
207
+ else:
208
+ labels.extend(["0"] * (num_bboxes - len(labels)))
209
+
210
+ if len(metaDatas) > num_bboxes:
211
+ metaDatas = metaDatas[:num_bboxes]
212
+ else:
213
+ metaDatas.extend([[]] * (num_bboxes - len(metaDatas)))
214
+
215
+ # Convert BBOX Format to XYWH
216
+ if "REL" in bbox_format:
217
+ bboxes = [
218
+ relative_to_absolute(bbox, original_image_size[0], original_image_size[1])
219
+ for bbox in bboxes
220
+ ]
221
+ original_format = bbox_format.replace("REL_", "")
222
+ else:
223
+ original_format = bbox_format
224
+ bboxes = [convert_bbox_format(bbox, original_format, "XYWH") for bbox in bboxes]
225
+
226
+ bbox_info = [
227
+ {
228
+ "bbox": [b / scale for b in item[0]],
229
+ "label_id": item[1],
230
+ "label": label_list[item[1]],
231
+ "meta": item[2],
232
+ }
233
+ for item in zip(bboxes, labels, metaDatas)
234
+ ]
235
+
236
+ component_value = _component_func(
237
+ image_url=image_url,
238
+ image_size=image.size,
239
+ label_list=label_list,
240
+ bbox_info=bbox_info,
241
+ color_map=color_map,
242
+ line_width=line_width,
243
+ ui_width=20,
244
+ ui_height=20,
245
+ edit_meta=_edit_meta,
246
+ edit_description=True,
247
+ class_select_type=_select_type,
248
+ item_editor=item_editor,
249
+ item_selector=item_selector,
250
+ class_select_position=_class_select_pos,
251
+ item_editor_position=_item_editor_pos,
252
+ item_selector_position=_item_selector_pos,
253
+ ui_left_size=_left_size,
254
+ ui_bottom_size=_bottom_size,
255
+ ui_right_size=_right_size,
256
+ key=key,
257
+ read_only=read_only,
258
+ label_type="detection",
259
+ )
260
+
261
+ bboxes = None
262
+ if component_value is not None:
263
+ bboxes = component_value["bbox"]
264
+ bboxes = [
265
+ {
266
+ "bbox": [b * scale for b in item["bbox"]],
267
+ "label_id": item["label_id"],
268
+ "label": item["label"],
269
+ "meta": item["meta"],
270
+ }
271
+ for item in bboxes
272
+ ]
273
+
274
+ # Convert back to original format
275
+ if "REL" in bbox_format:
276
+ bboxes = [
277
+ convert_bbox_format(bbox["bbox"], "XYWH", original_format)
278
+ for bbox in bboxes
279
+ ]
280
+ bboxes = [
281
+ absolute_to_relative(bbox, original_image_size[0], original_image_size[1])
282
+ for bbox in bboxes
283
+ ]
284
+ else:
285
+ bboxes = [
286
+ convert_bbox_format(bbox["bbox"], "XYWH", original_format)
287
+ for bbox in bboxes
288
+ ]
289
+
290
+ return {
291
+ "bbox": bboxes,
292
+ "image_size": original_image_size,
293
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "files": {
3
+ "main.js": "./static/js/main.1def89e5.js",
4
+ "index.html": "./index.html",
5
+ "main.1def89e5.js.map": "./static/js/main.1def89e5.js.map"
6
+ },
7
+ "entrypoints": [
8
+ "static/js/main.1def89e5.js"
9
+ ]
10
+ }