DermoLabeler / app.py
goktug14's picture
downloadable csv
3de602a
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)