goktug14 commited on
Commit
886f869
·
1 Parent(s): 720db5b

adding app file

Browse files
Files changed (1) hide show
  1. app.py +273 -0
app.py ADDED
@@ -0,0 +1,273 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import pandas as pd
3
+ from PIL import Image
4
+ import os
5
+
6
+ # Define your sections and labels
7
+ SECTION_LABELS = {
8
+ "Oil Pore Related Issues": [
9
+ "Very Large Pores (Not Red)",
10
+ "Whiteheads (Clogged Pores)",
11
+ "Blackheads (Clogged Pores)",
12
+ "Shinny Skin",
13
+ "Sebaceous Filaments (Sebum)"
14
+ ],
15
+ "Acne and Blemishes": [
16
+ "Pustules",
17
+ "Papules",
18
+ "Nodules",
19
+ "Cysts",
20
+ "Acne",
21
+ "Telangiectasia",
22
+ "Milia",
23
+ "Scars",
24
+ "Ice Berg Scars",
25
+ ],
26
+ "Redness and Irritation": [
27
+ "Redness",
28
+ "Irritation",
29
+ ],
30
+ "Dryness and Texture Issues": [
31
+ "Dryness",
32
+ "Fine Lines / Wrinkles",
33
+ "Skin Flakes"
34
+ ],
35
+ "Aging and Elasticity Issues": [
36
+ "Loose Skin",
37
+ "Deep Wrinkles"
38
+ ],
39
+ "Pigmentation Issues": [
40
+ "Dark Spots",
41
+ "Melasma",
42
+ "Freckles"
43
+ ],
44
+ "Rosacea": [
45
+ "Erythematous Telangiectasia Rosacea",
46
+ "Papillopustular Rosacea",
47
+ "Fimatoz Rosacea",
48
+ "Ocular Rosacea"
49
+ ],
50
+ "Eczema": [
51
+ "Seboreik Dermatit"
52
+ ]
53
+ }
54
+
55
+ # Define the sections for each column to control UI layout
56
+ column1_sections = ["Oil Pore Related Issues", "Dryness and Texture Issues"]
57
+ column2_sections = ["Acne and Blemishes"]
58
+ column3_sections = ["Redness and Irritation", "Pigmentation Issues", "Aging and Elasticity Issues"]
59
+ column4_sections = ["Rosacea", "Egzama"]
60
+
61
+ # Combine all section lists to define the exact UI order
62
+ UI_ORDERED_SECTIONS = column1_sections + column2_sections + column3_sections + column4_sections
63
+
64
+ # Flattened labels list, created in the SAME order as the UI checkboxes will be. This is the fix.
65
+ ALL_LABELS = [
66
+ label
67
+ for section_name in UI_ORDERED_SECTIONS
68
+ for label in SECTION_LABELS.get(section_name, [])
69
+ ]
70
+
71
+
72
+ # Global state
73
+ images = []
74
+ current_index = 0
75
+ results = []
76
+ annotations = {}
77
+
78
+ # Core functions
79
+
80
+ def display_image(idx):
81
+ """Displays the image at the given index and its saved annotations."""
82
+ if images:
83
+ img_path = images[idx]
84
+ img = Image.open(img_path)
85
+ fname = os.path.basename(img_path)
86
+ tick = ' ✅' if idx in annotations else ''
87
+ caption = f"{fname} ({idx+1}/{len(images)}){tick}"
88
+ states = annotations.get(idx, [False] * len(ALL_LABELS))
89
+ return [img, caption] + states
90
+ return [None, "No images uploaded"] + [False] * len(ALL_LABELS)
91
+
92
+
93
+ def navigate(delta):
94
+ """Navigates to the next or previous image."""
95
+ global current_index
96
+ if not images:
97
+ return [None, "No images uploaded"] + [False] * len(ALL_LABELS)
98
+ current_index = (current_index + delta + len(images)) % len(images)
99
+ return display_image(current_index)
100
+
101
+
102
+ def submit(*selections):
103
+ """Saves the current annotations to the state and writes to a CSV file."""
104
+ if not images:
105
+ return "No image to label", None
106
+
107
+ # Save selections to our annotations dictionary
108
+ annotations[current_index] = list(selections)
109
+ fname = os.path.basename(images[current_index])
110
+ chosen_labels = [lbl for lbl, sel in zip(ALL_LABELS, selections) if sel]
111
+
112
+ global results
113
+ # Remove any previous entry for this image to avoid duplicates
114
+ results = [r for r in results if r['image'] != fname]
115
+ results.append({'image': fname, 'labels': ', '.join(chosen_labels)})
116
+
117
+ # Write the updated results to a CSV file
118
+ df = pd.DataFrame(results)
119
+ df.to_csv('image_labels.csv', index=False)
120
+
121
+ return "Labels saved!", 'image_labels.csv'
122
+
123
+
124
+ def upload_images(files):
125
+ """Handles image uploads, resetting the application state."""
126
+ global images, current_index, results, annotations
127
+ images = [f.name for f in files]
128
+ current_index = 0
129
+ results = []
130
+ annotations = {}
131
+ outputs = display_image(0)
132
+ # Hide the uploader component after a successful upload
133
+ return outputs + [gr.update(visible=False), gr.update(visible=True)]
134
+
135
+ def load_annotations(csv_file):
136
+ """Loads annotations from an uploaded CSV file."""
137
+ global annotations, results
138
+ if csv_file is None or not images:
139
+ # If no CSV is uploaded or no images are loaded, do nothing.
140
+ return display_image(current_index)
141
+
142
+ try:
143
+ df = pd.read_csv(csv_file.name)
144
+ # Create a quick lookup map from filename to its index in the `images` list
145
+ image_map = {os.path.basename(name): i for i, name in enumerate(images)}
146
+
147
+ # Reset existing annotations and results
148
+ annotations = {}
149
+ results = df.to_dict('records')
150
+
151
+ for _, row in df.iterrows():
152
+ fname = row['image']
153
+ # Check if the image from the CSV is in the currently uploaded images
154
+ if fname in image_map:
155
+ img_idx = image_map[fname]
156
+ # Handle cases where labels might be empty (NaN)
157
+ if pd.notna(row['labels']):
158
+ saved_labels = set(l.strip() for l in row['labels'].split(','))
159
+ else:
160
+ saved_labels = set()
161
+
162
+ # Create the boolean state list for the checkboxes
163
+ states = [label in saved_labels for label in ALL_LABELS]
164
+ annotations[img_idx] = states
165
+ except Exception as e:
166
+ print(f"Error loading annotations: {e}")
167
+ # In case of error, just refresh the current view without changes
168
+ return display_image(current_index)
169
+
170
+ # After loading, refresh the view to show the annotations for the current image
171
+ return display_image(current_index)
172
+
173
+
174
+ # --- Gradio UI Definition ---
175
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
176
+ gr.Markdown("## Dermatology Annotation Tool")
177
+ with gr.Row():
178
+ image_upload = gr.File(label="1. Upload Images", file_count="multiple", file_types=["image"])
179
+ csv_upload = gr.File(label="2. (Optional) Upload Annotations CSV", file_types=[".csv"], visible=False)
180
+
181
+ checkbox_components = []
182
+
183
+ with gr.Row(): # Main row for the four columns of labels
184
+ # Column 1
185
+ with gr.Column(scale=1, min_width=0):
186
+ for section_name in column1_sections:
187
+ if section_name in SECTION_LABELS:
188
+ with gr.Group():
189
+ gr.Markdown(f"### {section_name}")
190
+ for lbl in SECTION_LABELS[section_name]:
191
+ cb = gr.Checkbox(label=lbl)
192
+ checkbox_components.append(cb)
193
+
194
+ # Column 2
195
+ with gr.Column(scale=1, min_width=0):
196
+ for section_name in column2_sections:
197
+ if section_name in SECTION_LABELS:
198
+ with gr.Group():
199
+ gr.Markdown(f"### {section_name}")
200
+ for lbl in SECTION_LABELS[section_name]:
201
+ cb = gr.Checkbox(label=lbl)
202
+ checkbox_components.append(cb)
203
+
204
+ # Column 3
205
+ with gr.Column(scale=1, min_width=0):
206
+ for section_name in column3_sections:
207
+ if section_name in SECTION_LABELS:
208
+ with gr.Group():
209
+ gr.Markdown(f"### {section_name}")
210
+ for lbl in SECTION_LABELS[section_name]:
211
+ cb = gr.Checkbox(label=lbl)
212
+ checkbox_components.append(cb)
213
+
214
+ # Column 4
215
+ with gr.Column(scale=1, min_width=0):
216
+ for section_name in column4_sections:
217
+ if section_name in SECTION_LABELS:
218
+ with gr.Group():
219
+ gr.Markdown(f"### {section_name}")
220
+ for lbl in SECTION_LABELS[section_name]:
221
+ cb = gr.Checkbox(label=lbl)
222
+ checkbox_components.append(cb)
223
+
224
+ # Image display and controls
225
+ with gr.Row():
226
+ with gr.Column(scale=2): # Image display column
227
+ img = gr.Image(label="Image")
228
+ caption = gr.Label(value="No images uploaded")
229
+ with gr.Row():
230
+ prev_btn = gr.Button("⬅️ Previous")
231
+ next_btn = gr.Button("Next ➡️")
232
+ with gr.Column(scale=1): # Controls and download column
233
+ submit_btn = gr.Button("Submit Labels")
234
+ status = gr.Label()
235
+ csv_downloader = gr.File(label="Download labels CSV")
236
+
237
+ # --- Event Handling ---
238
+
239
+ # When images are uploaded
240
+ image_upload.upload(
241
+ fn=upload_images,
242
+ inputs=image_upload,
243
+ outputs=[img, caption] + checkbox_components + [image_upload, csv_upload]
244
+ )
245
+
246
+ # When a CSV annotation file is uploaded
247
+ csv_upload.upload(
248
+ fn=load_annotations,
249
+ inputs=csv_upload,
250
+ outputs=[img, caption] + checkbox_components
251
+ )
252
+
253
+ # When previous button is clicked
254
+ prev_btn.click(
255
+ fn=lambda: navigate(-1),
256
+ outputs=[img, caption] + checkbox_components
257
+ )
258
+
259
+ # When next button is clicked
260
+ next_btn.click(
261
+ fn=lambda: navigate(1),
262
+ outputs=[img, caption] + checkbox_components
263
+ )
264
+
265
+ # When submit button is clicked
266
+ submit_btn.click(
267
+ fn=submit,
268
+ inputs=checkbox_components,
269
+ outputs=[status, csv_downloader]
270
+ )
271
+
272
+ if __name__ == "__main__":
273
+ demo.launch(server_name="0.0.0.0", server_port=7860)