File size: 8,619 Bytes
52e44f1
0b97d29
52e44f1
 
 
 
6904fc7
 
52e44f1
 
0b97d29
c4c02ca
 
 
52e44f1
6904fc7
 
52e44f1
 
c4c02ca
6904fc7
 
 
c4c02ca
 
 
6904fc7
 
52e44f1
 
c4c02ca
52e44f1
 
 
 
 
 
0b97d29
c4c02ca
 
 
52e44f1
 
 
 
 
6904fc7
 
52e44f1
 
 
 
c4c02ca
6904fc7
 
c4c02ca
6904fc7
 
c4c02ca
6904fc7
52e44f1
 
6904fc7
 
 
 
 
 
52e44f1
 
 
c4c02ca
6904fc7
 
52e44f1
6904fc7
c4c02ca
6904fc7
 
 
 
 
52e44f1
 
 
6904fc7
 
 
c4c02ca
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6904fc7
 
c4c02ca
 
 
 
 
 
 
6904fc7
c4c02ca
 
52e44f1
 
6904fc7
 
 
52e44f1
 
6904fc7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c4c02ca
 
 
6904fc7
 
 
 
 
 
 
 
 
 
 
c4c02ca
6904fc7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52e44f1
 
6904fc7
 
 
 
 
 
 
 
 
52e44f1
6904fc7
 
52e44f1
6904fc7
 
52e44f1
 
 
 
6904fc7
 
52e44f1
 
 
 
 
 
 
6904fc7
 
52e44f1
 
 
 
6904fc7
 
52e44f1
 
 
 
6904fc7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c4c02ca
6904fc7
c4c02ca
 
 
 
 
6904fc7
c4c02ca
6904fc7
 
 
52e44f1
6904fc7
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
import gradio as gr
import spaces
from PIL import Image

from dataset import ImageDataset
from labelizer import get_task_response
from packager import create_dataset_zip
from documentation import DOC_CONTENT


@spaces.GPU
def auto_label(
    image: Image.Image, imid: int, dataset: ImageDataset
) -> tuple[str, ImageDataset]:
    text = get_task_response("<MORE_DETAILED_CAPTION>", image)
    ds = dataset.update_label(imid, text)
    return text, ds


def label_changed(label: str, imid: int, dataset: ImageDataset) -> ImageDataset:
    return dataset.update_label(imid, label)


def update_single_label(
    dataset: ImageDataset, label_text: str, image_id: int
) -> ImageDataset:
    """Update single image label in dataset."""
    return dataset.update_label(image_id, label_text)


def uploaded(files: list, current_dataset: ImageDataset | None) -> ImageDataset:
    """Handle file upload - return new dataset instance."""
    if current_dataset is None:
        current_dataset = ImageDataset()
    return current_dataset.add_images(files)


@spaces.GPU
def labelize_all_images(
    dataset: ImageDataset, label: str, progress=gr.Progress(True)
) -> tuple[ImageDataset, str]:
    """Generate labels for all images and return new dataset instance."""

    # Generate actual labels
    labels_dict = {}
    for imdata in progress.tqdm(dataset.images):
        text = get_task_response("<MORE_DETAILED_CAPTION>", Image.open(imdata["path"]))  # type: ignore
        labels_dict[imdata["id"]] = text  # type: ignore

    return dataset.update_all_labels(labels_dict), label


def create_dataset_zipfile(dataset: ImageDataset, organize_in_folders: bool):
    """Create and return zip file for download."""
    zip_path = create_dataset_zip(dataset, organize_in_folders)
    return gr.update(visible=True, value=zip_path)


def update_buttons_states(dataset: ImageDataset, labeling_in_progress=False):
    """Update all button states based on dataset and labeling progress."""
    count = len(dataset.images)
    return (
        gr.update(interactive=count == 0 and not labeling_in_progress),  # upload
        gr.update(interactive=count > 0 and not labeling_in_progress),  # label all
        gr.update(visible=labeling_in_progress),  # progressbar
        gr.update(interactive=count > 0 and not labeling_in_progress),  # remove all
        gr.update(interactive=count > 0 and not labeling_in_progress),  # download
        labeling_in_progress,  # is_labeling_in_progress
    )


def start_labeling(dataset: ImageDataset):
    """Start labeling process - disable buttons and show progress."""
    return update_buttons_states(dataset, labeling_in_progress=True)


def finish_labeling(dataset: ImageDataset):
    """Finish labeling process - enable buttons and hide progress."""
    return update_buttons_states(dataset, labeling_in_progress=False)


with gr.Blocks(title="Labelizer", fill_width=True) as demo:
    dataset = gr.State()
    with gr.Sidebar():
        gr.Markdown("# ๐Ÿ–ผ๏ธ Image Labeling Tool")
        with gr.Group():
            gr.Markdown("Upload images and add labels to build your dataset.")

            upload_button = gr.UploadButton(
                "๐Ÿ“ Upload images",
                file_count="multiple",
            )
            label_all = gr.Button(
                "๐Ÿท๏ธ Labelize all images",
                interactive=False,
            )
            is_labeling_in_progress = gr.State(
                False,
            )
            progressbar = gr.Label(
                "",
                visible=False,
                label="Preparing...",
            )
            remove_all = gr.Button(
                "๐Ÿ—‘๏ธ Remove all",
                interactive=False,
            )

        with gr.Group():
            organize_files = gr.Checkbox(
                label="๐Ÿ“‚ Organize in folders", value=False, render=False
            )
            download_button = gr.Button(
                "๐Ÿ’พ Create zip file to download",
                interactive=False,
                size="lg",
            )
            download_file = gr.File(label="Generated datasets", visible=False)
            organize_files.render()

    @gr.render(inputs=[dataset, is_labeling_in_progress])
    def render_grid(ds, is_labeling_in_progress):
        if not ds or len(ds.images) == 0:
            gr.Markdown(DOC_CONTENT)
            return

        # Hidden component to trigger label refresh
        with gr.Row(equal_height=True):
            for im in ds.images:
                with (
                    gr.Column(
                        elem_classes="label-image-box",
                        preserved_by_key=[
                            f"image_{im['id']}",
                            f"text_{im['id']}",
                            f"button_{im['id']}",
                            f"button_clicked_{im['id']}",
                            f"label_changed_{im['id']}",
                        ],
                    ),
                ):
                    # Hidden component to store current image ID
                    current_image_id = gr.State(value=im["id"])

                    image = gr.Image(
                        im["path"],
                        type="pil",
                        container=False,
                        sources=None,
                        buttons=["fullscreen"],
                        height=300,
                        key=f"image_{im['id']}",
                    )

                    label = gr.Text(
                        im["label"],
                        placeholder="Description...",
                        lines=5,
                        container=False,
                        interactive=not is_labeling_in_progress,
                        key=f"text_{im['id']}",
                    )

                    button = gr.Button(
                        "โœจ Generate label",
                        interactive=not is_labeling_in_progress,
                        key=f"button_{im['id']}",
                    )

                    button.click(
                        auto_label,
                        inputs=[image, current_image_id, dataset],
                        outputs=[label, dataset],
                        key=f"button_clicked_{im['id']}",
                    )

                    # Update dataset when label is changed
                    label.change(
                        label_changed,
                        inputs=[label, current_image_id, dataset],
                        outputs=[dataset],
                        key=f"label_changed_{im['id']}",
                    )

    # Remove everything
    remove_all.click(
        lambda: ImageDataset(),
        inputs=None,
        outputs=dataset,
    ).then(
        update_buttons_states,
        inputs=[dataset, is_labeling_in_progress],
        outputs=[
            upload_button,
            label_all,
            progressbar,
            remove_all,
            download_button,
            is_labeling_in_progress,
        ],
    )

    # Label all images
    label_all.click(
        fn=start_labeling,
        inputs=[dataset],
        outputs=[
            upload_button,
            label_all,
            progressbar,
            remove_all,
            download_button,
            is_labeling_in_progress,
        ],
    ).then(
        fn=labelize_all_images,
        inputs=[dataset, progressbar],
        outputs=[dataset, progressbar],
    ).then(
        fn=finish_labeling,
        inputs=[dataset],
        outputs=[
            upload_button,
            label_all,
            progressbar,
            remove_all,
            download_button,
            is_labeling_in_progress,
        ],
    )

    # Upload images
    upload_button.upload(
        uploaded,
        inputs=[upload_button, dataset],
        outputs=dataset,
    ).then(
        update_buttons_states,
        inputs=[dataset, is_labeling_in_progress],
        outputs=[
            upload_button,
            label_all,
            progressbar,
            remove_all,
            download_button,
            is_labeling_in_progress,
        ],
    )
    # create the zip file and set the download file section ready to use
    download_button.click(
        lambda: gr.update(visible=True),
        inputs=None,
        outputs=download_file,
    ).then(
        create_dataset_zipfile,
        inputs=[dataset, organize_files],
        outputs=download_file,
    )


if __name__ == "__main__":
    CSS = """
    .gr-group {
        padding: .2rem;
    }
    .label-image-box {
    }
    """
    demo.queue().launch(css=CSS)