File size: 4,709 Bytes
85e4b4a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3de602a
85e4b4a
3de602a
85e4b4a
 
 
 
 
 
 
 
 
 
3de602a
 
 
 
85e4b4a
 
 
 
 
 
3de602a
 
 
 
 
 
 
85e4b4a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3de602a
85e4b4a
 
 
 
 
 
 
 
 
 
 
3de602a
85e4b4a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3de602a
85e4b4a
 
 
3de602a
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
import gradio as gr
import pandas as pd
from PIL import Image

# Define your sections and labels
SECTION_LABELS = {
    "Oil Pore Related Issues": [
        "Oily Sheen",
        "Very Large Pores (Not Red)",
        "Whiteheads (Clogged Pores)",
        "Blackheads (Clogged Pores)",
        "Shinny Skin",
        "Sebaceous Filaments (Sebum)"
    ],
    "Acne and Blemishes": [
        "Pustules",
        "Papules",
        "Nodules",
        "Cysts"
    ],
    "Redness and Irritation": [
        "Redness",
        "Irritation",
        "Itching",
        "Burning Sensation",
        "Allergic Reactions"
    ],
    "Dryness and Texture Issues": [
        "Dryness",
        "Fine Lines / Wrinkles",
        "Flakiness"
    ],
    "Aging and Elasticity Issues": [
        "Loss of Elasticity",
        "Sagging Skin",
        "Wrinkle Prone Areas"
    ],
    "Pigmentation Issues": [
        "Dark Spots",
        "Uneven Skin Tone",
        "Melasma",
        "Freckles"
    ]
}

# Flattened labels list
ALL_LABELS = [label for labels in SECTION_LABELS.values() for label in labels]

# Global state
images = []
current_index = 0
results = []
annotations = {}

# Core functions

def display_image(idx):
    if images:
        img = Image.open(images[idx])
        fname = images[idx].split('/')[-1]
        tick = ' ✅' if idx in annotations else ''
        caption = f"{fname} ({idx+1}/{len(images)}){tick}"
        states = annotations.get(idx, [False] * len(ALL_LABELS))
        return [img, caption] + states
    return [None, "No images uploaded"] + [False] * len(ALL_LABELS)


def navigate(delta):
    global current_index
    current_index = (current_index + delta) % len(images)
    return display_image(current_index)


def submit(*selections):
    if not images:
        # Return status and no file
        return "No image to label", None

    # Save selections
    annotations[current_index] = list(selections)
    fname = images[current_index].split('/')[-1]
    chosen = [lbl for lbl, sel in zip(ALL_LABELS, selections) if sel]
    global results
    results = [r for r in results if r['image'] != fname]
    results.append({'image': fname, 'labels': ', '.join(chosen)})

    # Write CSV
    df = pd.DataFrame(results)
    df.to_csv('image_labels.csv', index=False)

    # Return status message and CSV path
    return "Labels saved!", 'image_labels.csv'


def upload(files):
    global images, current_index, results, annotations
    images = [f.name if hasattr(f, 'name') else f for f in files]
    current_index = 0
    results = []
    annotations = {}
    outputs = display_image(0)
    # hide uploader after upload
    return outputs + [gr.update(visible=False)]


with gr.Blocks() as demo:
    gr.Markdown("## Dermo Annotator")
    file_upload = gr.File(label="Upload Images", file_count="multiple", file_types=["image"])

    # Create checkbox components in layout order
    checkbox_components = []

    # First row: first 3 sections
    with gr.Row():
        for section in list(SECTION_LABELS.keys())[:3]:
            with gr.Column():
                gr.Markdown(f"### {section}")
                for lbl in SECTION_LABELS[section]:
                    cb = gr.Checkbox(label=lbl)
                    checkbox_components.append(cb)

    # Second row: next 3 sections
    with gr.Row():
        for section in list(SECTION_LABELS.keys())[3:]:
            with gr.Column():
                gr.Markdown(f"### {section}")
                for lbl in SECTION_LABELS[section]:
                    cb = gr.Checkbox(label=lbl)
                    checkbox_components.append(cb)

    # Image display and controls
    with gr.Row():
        with gr.Column(scale=2):
            img = gr.Image(label="Image")
            caption = gr.Label(value="No images uploaded")
            with gr.Row():
                prev_btn = gr.Button("⬅️ Previous")
                next_btn = gr.Button("Next ➡️")

        with gr.Column(scale=1):
            submit_btn = gr.Button("Submit Labels")
            status = gr.Label()
            csv_downloader = gr.File(label="Download labels CSV")

    # Wire events
    file_upload.upload(
        fn=upload,
        inputs=file_upload,
        outputs=[img, caption] + checkbox_components + [file_upload]
    )

    prev_btn.click(
        fn=lambda: navigate(-1),
        outputs=[img, caption] + checkbox_components
    )

    next_btn.click(
        fn=lambda: navigate(1),
        outputs=[img, caption] + checkbox_components
    )

    submit_btn.click(
        fn=submit,
        inputs=checkbox_components,
        outputs=[status, csv_downloader]
    )

if __name__ == "__main__":
    demo.launch(server_name="0.0.0.0", server_port=7860)