|
import streamlit as st |
|
import threading |
|
from functions import * |
|
from streamlit.runtime.scriptrunner.script_run_context import add_script_run_ctx |
|
from streamlit.components.v1 import declare_component |
|
|
|
st.set_page_config(layout="wide") |
|
|
|
car_map = declare_component("car_map", path="./car_map") |
|
|
|
if "reset" not in st.session_state: |
|
st.session_state["reset"] = False |
|
if "parts_filter" not in st.session_state: |
|
st.session_state["parts_filter"] = [] |
|
|
|
with st.sidebar: |
|
name = st.text_input("Name *", value=st.session_state.get("name", "")) |
|
name = name.strip().lower() |
|
name_error_placeholder = st.empty() |
|
|
|
st.session_state["parts_filter"] = st.multiselect("Filter images by damaged parts (optional)", get_car_parts_count(), help="Select one or more parts to filter the images by.", default=st.session_state["parts_filter"]) |
|
|
|
if st.button("Start annotation"): |
|
if name: |
|
st.session_state["name"] = name |
|
st.session_state["img"], st.session_state["img_name"] = get_random_image(st.session_state["parts_filter"]) |
|
st.rerun() |
|
else: |
|
name_error_placeholder.error("Name is required") |
|
|
|
with st.expander("Annotation statistics", expanded=True): |
|
st.write(f"**Total images**: {len(df)}") |
|
st.write(f"**Total annotated images**: {len(df[df['validated'] == True])}") |
|
st.write(f"**Images left to annotate**: {len(df[df['validated'] == False])}") |
|
if "name" in st.session_state: |
|
st.write(f"**Images annotated by you**: {len(df[(df['annotator_name'] == st.session_state['name']) & (df['validated'] == True)])}") |
|
|
|
with st.expander("Annotation instructions", expanded=False): |
|
st.markdown("- Rotate the image of the car if necessary using the rotation buttons.") |
|
st.markdown("- Switch between viewing the car map from the back and the front for a better view.") |
|
st.markdown("- Check the image for damages and mark the damaged parts on the car map on the right.") |
|
st.markdown("- Right click on the car map to increase the severity level of the damage, and left click to decrease it.") |
|
st.markdown("- Severity **level 1** is for scratches, **level 2** is for dents, and **level 3** is for cracks, breaks and severe damages.") |
|
st.markdown("**β Skip this image**: If you are not sure about the damages, the image will be shown to another annotator.") |
|
st.markdown("**β Not car**: If the image is taken inside of the car or does not contain a car at all, or the image is not clear (blurry, zoomed-in, bad lighting, etc.)") |
|
st.markdown("**β
Validate**: save the annotations and move to the next image.") |
|
st.markdown("π: Go back to your previous annotation.") |
|
|
|
|
|
|
|
if "name" in st.session_state: |
|
if st.session_state["img"]: |
|
col1, col2 = st.columns([10, 8]) |
|
|
|
rotation_btns = col1.columns([1, 1]) |
|
left_btn = rotation_btns[0].button("βΆ 90Β°", help="Rotate image 90Β° left", use_container_width=True) |
|
right_btn = rotation_btns[1].button("β· 90Β°", help="Rotate image 90Β° right", use_container_width=True) |
|
|
|
if "rotation" not in st.session_state: |
|
st.session_state["rotation"] = 0 |
|
if left_btn: |
|
st.session_state["img"] = st.session_state["img"].rotate(90, expand=True).resize((1000, 800)) |
|
st.session_state["rotation"] = (st.session_state["rotation"] + 90) % 360 |
|
if right_btn: |
|
st.session_state["img"] = st.session_state["img"].rotate(-90, expand=True).resize((1000, 800)) |
|
st.session_state["rotation"] = (st.session_state["rotation"] - 90) % 360 |
|
|
|
reset_annotation_btn = col2.button("π§Ή Reset annotation", help="Remove all the annotation and start over", use_container_width=True) |
|
if reset_annotation_btn: |
|
st.session_state["reset"] = True |
|
car_map_view = col2.radio("Car map view", ["Back", "Front"], index=0, horizontal=True, label_visibility="collapsed") |
|
|
|
img = st.session_state.get("img") |
|
img_name = st.session_state.get("img_name") |
|
col1_c1, col1_c2 = col1.columns([1, 10]) |
|
prev_img_name = st.session_state["prev_img_name"] if "prev_img_name" in st.session_state else None |
|
previous_btn = col1_c1.button("π", use_container_width=True, help="Go back to the previous image", disabled=(not prev_img_name)) |
|
if previous_btn: |
|
st.session_state["img"] = st.session_state["prev_img"] |
|
st.session_state["img_name"] = st.session_state["prev_img_name"] |
|
st.session_state["rotation"] = st.session_state["prev_rotation"] |
|
st.session_state["prev_img_name"] = None |
|
st.session_state["prev_rotation"] = None |
|
st.rerun() |
|
col1_c2.button(img_name, use_container_width=True, disabled=True) |
|
|
|
col1.image(img, use_column_width=True) |
|
|
|
with col2: |
|
img_damages = get_img_damages(img_name) |
|
annotated_damages = car_map(damages=img_damages, img_name=img_name, view=car_map_view, reset=st.session_state["reset"]) |
|
bnt_cols = st.columns(3) |
|
skip_btn = bnt_cols[0].button("β Skip this image", use_container_width=True, help="Skip this image if you are not sure") |
|
not_car_btn = bnt_cols[1].button("β Not car", use_container_width=True, help="Click this if the image does not contain a car") |
|
next_btn = bnt_cols[2].button("β
Validate", use_container_width=True, type="primary", help="Validate annotations and move to the next one") |
|
|
|
with open('custom_style.css') as f: |
|
st.markdown(f'<style>{f.read()}</style>', unsafe_allow_html=True) |
|
|
|
if not_car_btn or next_btn or skip_btn: |
|
annot_name = st.session_state["name"] |
|
rotation = st.session_state["rotation"] |
|
process_thread = threading.Thread(target=process_image, args=(img_name, annot_name, not not_car_btn, skip_btn, rotation, annotated_damages), daemon=True) |
|
add_script_run_ctx(process_thread) |
|
process_thread.start() |
|
st.session_state["prev_img_name"] = img_name |
|
st.session_state["prev_img"] = img |
|
st.session_state["prev_rotation"] = st.session_state["rotation"] |
|
st.session_state["img"], st.session_state["img_name"] = get_random_image(st.session_state["parts_filter"]) |
|
st.session_state["rotation"] = 0 |
|
st.session_state["reset"] = False |
|
st.rerun() |
|
|
|
else: |
|
st.write("No images left to annotate. Thank you for your contribution!") |