ychafiqui's picture
fixed bug that clears selected parts to filter after validating annotation
5357c74
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.")
# st.download_button("Download annotations", data=df.to_csv(index=False), file_name="annotations.csv", mime="text/csv")
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_c2.write(img_name)
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!")