ychafiqui commited on
Commit
1ecc339
1 Parent(s): de916a9
.gitignore ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ .streamlit/secrets.toml
2
+ todo.txt
3
+ CSVs/*
.streamlit/config.toml ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ [theme]
2
+ base="light"
app.py ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import threading
3
+ from functions import *
4
+ from streamlit.runtime.scriptrunner.script_run_context import add_script_run_ctx
5
+ from streamlit.components.v1 import declare_component
6
+
7
+ st.set_page_config(layout="wide")
8
+
9
+ car_map = declare_component("car_map", path="./car_map")
10
+
11
+ if "reset" not in st.session_state:
12
+ st.session_state["reset"] = False
13
+
14
+ with st.sidebar:
15
+ name = st.text_input("What's your name?", value=st.session_state.get("name", ""))
16
+ name = name.strip().lower()
17
+ name_error_placeholder = st.empty()
18
+
19
+ if st.button("Start annotation"):
20
+ if name:
21
+ st.session_state["name"] = name
22
+ st.session_state["img"], st.session_state["img_name"] = get_random_image()
23
+ st.rerun()
24
+ else:
25
+ name_error_placeholder.error("Name is required")
26
+
27
+ with st.expander("Annotation statistics", expanded=True):
28
+ st.write(f"**Total images**: {len(df)}")
29
+ st.write(f"**Total annotated images**: {len(df[df['validated'] == True])}")
30
+ st.write(f"**Images left to annotate**: {len(df[df['validated'] == False])}")
31
+ if "name" in st.session_state:
32
+ st.write(f"**Images annotated by you**: {len(df[(df['annotator_name'] == st.session_state['name']) & (df['validated'] == True)])}")
33
+
34
+ with st.expander("Annotation instructions", expanded=False):
35
+ st.markdown("- Rotate the image of the car if necessary using the rotation buttons.")
36
+ st.markdown("- Switch between viewing the car map from the back and the front for a better view.")
37
+ st.markdown("- Check the image for damages and mark the damaged parts on the car map on the right.")
38
+ st.markdown("- Right click on the car map to increase the severity level of the damage, and left click to decrease it.")
39
+ st.markdown("- Severity **level 1** is for scratches, **level 2** is for dents, and **level 3** is for cracks, breaks and severe damages.")
40
+ st.markdown("**⏭ Skip this image**: If you are not sure about the damages, the image will be shown to another annotator.")
41
+ 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.)")
42
+ st.markdown("**✅ Validate**: save the annotations and move to the next image.")
43
+ st.markdown("🔙: Go back to your previous annotation.")
44
+
45
+ # st.download_button("Download annotations", data=df.to_csv(index=False), file_name="annotations.csv", mime="text/csv")
46
+
47
+ if "name" in st.session_state:
48
+ if st.session_state["img"]:
49
+ col1, col2 = st.columns([10, 8])
50
+
51
+ rotation_btns = col1.columns([1, 1])
52
+ left_btn = rotation_btns[0].button("↶ 90°", help="Rotate image 90° left", use_container_width=True)
53
+ right_btn = rotation_btns[1].button("↷ 90°", help="Rotate image 90° right", use_container_width=True)
54
+
55
+ if "rotation" not in st.session_state:
56
+ st.session_state["rotation"] = 0
57
+ if left_btn:
58
+ st.session_state["img"] = st.session_state["img"].rotate(90, expand=True).resize((1000, 800))
59
+ st.session_state["rotation"] = (st.session_state["rotation"] + 90) % 360
60
+ if right_btn:
61
+ st.session_state["img"] = st.session_state["img"].rotate(-90, expand=True).resize((1000, 800))
62
+ st.session_state["rotation"] = (st.session_state["rotation"] - 90) % 360
63
+
64
+ reset_annotation_btn = col2.button("🧹 Reset annotation", help="Remove all the annotation and start over", use_container_width=True)
65
+ if reset_annotation_btn:
66
+ st.session_state["reset"] = True
67
+ car_map_view = col2.radio("Car map view", ["Back", "Front"], index=0, horizontal=True, label_visibility="collapsed")
68
+
69
+ img = st.session_state.get("img")
70
+ img_name = st.session_state.get("img_name")
71
+ col1_c1, col1_c2 = col1.columns([1, 10])
72
+ prev_img_name = st.session_state["prev_img_name"] if "prev_img_name" in st.session_state else None
73
+ previous_btn = col1_c1.button("🔙", use_container_width=True, help="Go back to the previous image", disabled=(not prev_img_name))
74
+ if previous_btn:
75
+ st.session_state["img"] = st.session_state["prev_img"]
76
+ st.session_state["img_name"] = st.session_state["prev_img_name"]
77
+ st.session_state["rotation"] = st.session_state["prev_rotation"]
78
+ st.session_state["prev_img_name"] = None
79
+ st.session_state["prev_rotation"] = None
80
+ st.rerun()
81
+ col1_c2.button(img_name, use_container_width=True, disabled=True)
82
+ # col1_c2.write(img_name)
83
+ col1.image(img, use_column_width=True)
84
+
85
+ with col2:
86
+ img_damages = get_img_damages(img_name)
87
+ annotated_damages = car_map(damages=img_damages, img_name=img_name, view=car_map_view, reset=st.session_state["reset"])
88
+ bnt_cols = st.columns(3)
89
+ skip_btn = bnt_cols[0].button("⏭ Skip this image", use_container_width=True, help="Skip this image if you are not sure")
90
+ not_car_btn = bnt_cols[1].button("❌ Not car", use_container_width=True, help="Click this if the image does not contain a car")
91
+ next_btn = bnt_cols[2].button("✅ Validate", use_container_width=True, type="primary", help="Validate annotations and move to the next one")
92
+
93
+ with open('custom_style.css') as f:
94
+ st.markdown(f'<style>{f.read()}</style>', unsafe_allow_html=True)
95
+
96
+ if not_car_btn or next_btn or skip_btn:
97
+ annot_name = st.session_state["name"]
98
+ rotation = st.session_state["rotation"]
99
+ process_thread = threading.Thread(target=process_image, args=(img_name, annot_name, not not_car_btn, skip_btn, rotation, annotated_damages), daemon=True)
100
+ add_script_run_ctx(process_thread)
101
+ process_thread.start()
102
+ st.session_state["prev_img_name"] = img_name
103
+ st.session_state["prev_img"] = img
104
+ st.session_state["prev_rotation"] = st.session_state["rotation"]
105
+ st.session_state["img"], st.session_state["img_name"] = get_random_image()
106
+ st.session_state["rotation"] = 0
107
+ st.session_state["reset"] = False
108
+ st.rerun()
109
+
110
+ else:
111
+ st.write("No images left to annotate. Thank you for your contribution!")
car_map/index.html ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <html>
2
+ <head>
3
+ <link href='https://fonts.googleapis.com/css?family=Source Sans Pro' rel='stylesheet'>
4
+ <link rel="stylesheet" type="text/css" href="styles.css">
5
+ </head>
6
+ <body>
7
+ <!-- Set up your HTML here -->
8
+ <div id="details-box"></div>
9
+ <div id="view-assist">
10
+ <p id="front">Front</p>
11
+ <img id="arrow" src="https://www.svgrepo.com/show/342408/arrow-top.svg">
12
+ <p id="back">Back</p>
13
+ </div>
14
+ <svg viewBox="0 -180 1600 2250" xmlns="http://www.w3.org/2000/svg" id="car-map">
15
+ <g id="layer2" transform="matrix(0, 1, -1, 0, 254.000085527972, -254.000194186645)" style="transform-origin: 555.665px 834.02px;">
16
+ <g id="g4113" transform="translate(-13.78 3.524)">
17
+ <path id="path3070" style="stroke: rgb(0, 0, 0); stroke-width: 5; fill: rgb(255, 255, 255);" d="M 357.43 1085.43 C 393.14 1062.57 494.57 1006.86 494.57 1006.86 C 536 991.14 634.57 995.43 686 994 C 737.43 992.57 777.248 996.088 818.148 1012.998 C 859.148 1029.928 936.567 1087.874 953.267 1093.074 C 976.067 1100.214 1080.3 1101.14 1118.9 1114 C 1157.4 1126.86 1144.6 1146.86 1144.6 1146.86 L 1114.6 1142.57 L 1108.8 1195.49 C 1108.8 1195.49 1146 1196.86 1150.3 1211.14 C 1154.6 1225.43 1156 1242.57 1147.4 1252.57 C 1138.9 1262.6 1133.1 1251.14 1128.9 1265.4 C 1124.6 1279.7 1126 1294 1101.7 1292.6 C 1077.4 1291.1 1003.1 1292.6 1003.1 1292.6 C 1003.1 1292.6 987.4 1184 904.6 1186.86 C 821.7 1189.71 808.9 1292.6 808.9 1292.6 L 361.599 1292.6" data-name="Aile arriere gauche"/>
18
+ <path id="path3070" style="stroke: rgb(0, 0, 0); stroke-width: 5; fill: rgb(255, 255, 255);" d="M 361.599 1292.6 L 308.86 1292.6 C 308.86 1292.6 303.14 1188.29 211.71 1186.86 C 120.29 1185.43 113.14 1292.6 113.14 1292.6 L 47.43 1292.6 L 28.86 1254 C 28.34 1254 2.61 1254 7.43 1236.86 C 12.08 1220.3 3.14 1195.43 24.57 1195.43 C 46 1195.43 71.71 1196.86 71.71 1196.86 L 87.43 1152.57 L 46 1149.71 C 46 1149.71 80.29 1125.43 164.57 1116.86 C 248.86 1108.29 321.71 1108.29 357.43 1085.43 L 361.599 1292.6 Z" data-name="Aile avant gauche">
19
+ <title>Path</title>
20
+ </path>
21
+ <path id="path3900" style="stroke: rgb(0, 0, 0); stroke-width: 5; fill: rgb(255, 255, 255);" d="M 886.971 1038.45 L 878.308 983.581 L 873.976 934.969 L 877.826 867.105 L 881.196 845.446 L 886.971 740.039 L 834.99 740.521 L 775.789 746.778 L 752.205 751.109 L 735.841 759.292 L 670.383 797.315 L 599.64 837.66 C 585.56 868.63 581.34 909.45 581.34 931.97 C 581.34 954.49 585.56 1024.944 603.86 1038.944 L 886.971 1038.45 Z" transform="translate(-254 254)" data-name="Porte avant gauche"/>
22
+ <path id="path3900-6" d="M 680.781 993.783 L 732.762 994.746 L 779.2 1000.17 C 797.5 1012.84 835.5 1050.84 872.1 1104.33 C 908.7 1157.82 880.5 1163.45 853.8 1178.93 C 827 1194.42 804.5 1221.641 786.2 1291.981 L 632.78 1292.419 C 627.15 1267.119 620.12 1209.9 620.12 1187.4 C 621.29 1151.3 622.6 1118.8 626.27 1100.1 C 628.74 1075 631.38 1000.204 632.78 994.574 L 680.781 993.783 Z" style="stroke: rgb(0, 0, 0); stroke-width: 5; fill: rgb(255, 255, 255);" data-name="Porte arriere gauche"/>
23
+ <path id="path3884" style="stroke: rgb(0, 0, 0); stroke-width: 5; fill: rgb(255, 255, 255);" d="m618.57 847.14c15.72-18.57 123.38-81.52 151.43-88.57 29.5-7.41 103-7.14 103-7.14l-7.14 95.71z" transform="translate(-254 254)"/>
24
+ <path id="path3892" style="stroke: rgb(0, 0, 0); stroke-width: 5; fill: rgb(255, 255, 255);" d="m898.7 752.84-4.29 94.32h207.19c-11.3-20.75-46.6-74.9-72.9-88.57-14.3-10-82.86-4.33-130-5.75z" transform="translate(-254 254)"/>
25
+ </g>
26
+ <path id="path4381-0" style="stroke: rgb(0, 0, 0); stroke-width: 5; fill: rgb(255, 255, 255);" d="M 1267.042 624.97 C 1267.042 624.97 1336.7 623.56 1336.7 646.09 L 1336.7 1017.7 C 1336.7 1040.2 1264.271 1043 1264.271 1043" data-name="Pare-choc arriere"/>
27
+ <g id="g4451" transform="translate(-13.78 15.524)">
28
+ <path id="path4175" style="stroke:#000000;stroke-width:5;fill:none" d="m736.17 416.79s94.31 5.63 152.02 5.63c106.98 0 201.31-5.63 201.31-5.63m-1.4 297s-77.4-5.63-199.91-5.63c-68.97 0-152.02 7.04-152.02 7.04" transform="translate(-254 254)"/>
29
+ <path id="path4135-9" d="m345.78 632.78h-294.19l-14.076 102.76-7.038 11.26v142.17l7.038 14.07 15.483 101.36h288.55" style="stroke: rgb(0, 0, 0); stroke-width: 5; fill: rgb(255, 255, 255);" data-name="Capot"/>
30
+ <path id="path-1" style="stroke: rgb(0, 0, 0); stroke-width: 5; fill: rgb(255, 255, 255);" d="M 1282.678 961.782 L 1282.677 677.454 L 1214.68 677.456 L 1214.68 961.786 L 1282.678 961.782 Z M 832.7 670.79 C 832.7 670.79 820 718.65 820 743.98 L 820 890.37 C 820 915.71 832.7 967.79 832.7 967.79 L 1038.2 1005.8 L 1146.6 1005.8 C 1146.6 1005.8 1160.6 924.16 1160.6 890.37 L 1160.6 742.58 C 1160.6 704.57 1146.6 632.78 1146.6 632.78 L 1038.2 632.78 L 832.7 670.79 Z" data-name="Malle"/>
31
+ <path id="path4165" style="stroke: rgb(0, 0, 0); stroke-width: 5; fill: rgb(255, 255, 255);" d="M 595.41 378.78 L 736.17 416.79 C 736.17 416.79 727.73 466.01 727.73 488.75 L 727.73 636.37 C 727.73 667.34 736.17 715.2 736.17 715.2 L 595.41 748.98 C 595.41 748.98 578.17 687.08 578.17 656.08 C 578.55 622.28 580.28 470.28 580.28 470.28 C 578.87 439.31 595.41 378.78 595.41 378.78 L 595.41 378.78 Z" transform="translate(-254 254)" data-name="Pare-brise avant"/>
32
+ <path id="path4205" style="stroke: rgb(0, 0, 0); stroke-width: 5; fill: rgb(255, 255, 255);" d="m1097.9 435.09s-8.4 40.82-8.4 56.3v144.98c0 19.71 7 57.72 7 57.72s94.3 26.74 123.9 26.74h42.2v-312.49h-47.8c-25.4 0-116.9 26.75-116.9 26.75z" transform="translate(-254 254)" data-name="Pare-brise arriere"/>
33
+ </g>
34
+ <path id="path4245" style="stroke: rgb(0, 0, 0); stroke-width: 5; fill: rgb(255, 255, 255);" d="M -74.052 1042.25 L -45.91 1042.1 C -17.754 1042.1 -23.38 1019.6 -23.38 1019.6 L -23.38 923.9 C -36.06 923.9 -34.65 914.29 -34.65 907.01 L -34.65 757.81 C -34.2 738.92 -24.79 740.92 -24.79 740.92 L -23.38 649.42 C -23.38 649.42 -16.855 626.389 -47.821 624.989 C -78.791 623.579 -75.919 624.665 -76.336 624.523 C -76.753 624.38 -144.37 622.97 -144.37 645.49 L -144.37 1017.1 C -144.37 1039.6 -74.052 1042.25 -74.052 1042.25 Z" data-name="Pare-choc avant"/>
35
+ <g id="g4428" transform="translate(-13.78 15.524)">
36
+ <path id="path3852-6" d="M 373.313 355.9 L 308.86 355.9 C 308.86 355.9 303.14 460.19 211.71 461.61 C 120.29 463.04 113.14 355.9 113.14 355.9 L 47.429 355.9 L 28.857 394.47 C 28.342 394.47 2.614 394.47 7.429 411.61 C 12.08 428.18 3.143 453.04 24.571 453.04 C 46 453.04 71.714 451.61 71.714 451.61 L 87.429 495.9 L 46 498.76 C 46 498.76 80.286 523.04 164.57 531.61 C 248.86 540.19 321.71 540.19 357.43 563.04" style="stroke: rgb(0, 0, 0); stroke-width: 5px; fill: rgb(255, 255, 255);" data-name="Aile avant droit"/>
37
+ <path id="path3852-6" d="M 373.313 355.9 L 808.86 355.9 C 808.86 355.9 821.71 458.76 904.57 461.61 C 987.43 464.47 1003.1 355.9 1003.1 355.9 C 1003.1 355.9 1077.4 357.33 1101.7 355.9 C 1126 354.47 1124.6 368.76 1128.9 383.04 C 1133.1 397.33 1138.9 385.9 1147.4 395.9 C 1156 405.9 1154.6 423.04 1150.3 437.33 C 1146 451.61 1108.8 452.98 1108.8 452.98 L 1114.6 505.9 L 1144.6 501.61 C 1144.6 501.61 1157.4 521.61 1118.9 534.47 C 1080.3 547.33 975.802 558.017 953.002 565.157 C 936.302 570.357 853.501 620.391 812.511 637.321 C 771.551 654.231 737.43 655.9 686 654.47 C 634.57 653.04 536 657.33 494.57 641.61 C 494.57 641.61 393.14 585.9 357.43 563.04" style="stroke: rgb(0, 0, 0); stroke-width: 5px; fill: rgb(255, 255, 255);" data-name="Aile arriere droit"/>
38
+ <path id="path3900-4" d="M 345.64 556.81 C 331.56 525.84 327.34 485.02 327.34 462.5 C 327.34 439.98 331.56 371.01 349.86 356.93 L 635.128 355.533 L 626.117 398.01 L 620.325 451.428 L 622.256 506.133 L 627.405 560.195 L 633.197 655.446 L 570.769 654.802 L 508.984 647.723 L 474.23 632.277 L 345.64 556.81 Z" style="stroke: rgb(0, 0, 0); stroke-width: 5; fill: rgb(255, 255, 255);" data-name="Porte avant droite"/>
39
+ <path id="path3942-2" d="M 720.688 654.344 L 632.78 655.34 C 631.38 649.71 628.74 573.44 626.27 548.41 C 622.6 529.69 621.29 497.19 620.12 461.09 C 620.12 438.57 627.15 380.86 632.78 355.52 L 786.21 356.23 C 804.51 426.61 827.03 454.06 853.78 469.54 C 880.52 485.02 908.67 490.65 872.08 544.14 C 835.48 597.63 797.47 635.64 779.17 648.3 L 720.688 654.344 Z" style="stroke: rgb(0, 0, 0); stroke-width: 5; fill: rgb(255, 255, 255);" data-name="Porte arriere droite"/>
40
+ <path id="path3892-5" d="m644.7 641.63-4.29-94.32h207.17c-11.31 20.75-46.62 74.9-72.86 88.57-14.29 10-82.88 4.33-130.02 5.75z" style="stroke: rgb(0, 0, 0); stroke-width: 5; fill: rgb(255, 255, 255);"/>
41
+ <path id="path3884-4" d="m364.57 547.33c15.72 18.57 123.38 81.52 151.43 88.57 29.5 7.41 103 7.14 103 7.14l-7.14-95.71z" style="stroke: rgb(0, 0, 0); stroke-width: 5; fill: rgb(255, 255, 255);"/>
42
+ </g>
43
+ <path id="path4361" style="stroke: rgb(0, 0, 0); stroke-width: 5px; paint-order: stroke; fill: rgb(255, 255, 255);" d="M -71.88 985.879 L -71.88 948.339 L -49.35 948.339 L -26.83 948.339 L -26.83 985.879 L -26.83 1023.376 L -49.35 1023.376 L -71.88 1023.376 L -71.88 985.879 Z" data-name="Feu avant gauche"/>
44
+ <path id="path4363" style="stroke-width: 5px; stroke: rgb(0, 0, 0); paint-order: stroke; fill: rgb(255, 255, 255);" d="M -71.88 681.701 L -71.88 642.757 L -49.35 642.757 L -26.83 642.757 L -26.83 681.701 L -26.83 720.644 L -49.35 720.644 L -71.88 720.644 L -71.88 681.701 Z" data-name="Feu avant droit"/>
45
+ <path id="path4610" style="stroke:#000000;stroke-width:2;fill:#c8c8c8" d="m195.66 202.84a76.01 76.01 0 1 1 -152.02 0 76.01 76.01 0 1 1 152.02 0z" transform="translate(78.489 1074.7)"/>
46
+ <path id="path4612" style="stroke:#000000;stroke-width:2;fill:#ffffff" d="m147.8 72.633a44.339 44.339 0 1 1 -88.681 0 44.339 44.339 0 1 1 88.681 0z" transform="translate(94.676 1204.9)"/>
47
+ <path id="path4610-9" style="stroke: rgb(0, 0, 0); stroke-width: 2; fill: rgb(200, 200, 200);" d="m195.66 202.84a76.01 76.01 0 1 1 -152.02 0 76.01 76.01 0 1 1 152.02 0z" transform="translate(78.489 187.66)"/>
48
+ <path id="path4612-4" style="stroke:#000000;stroke-width:2;fill:#ffffff" d="m147.8 72.633a44.339 44.339 0 1 1 -88.681 0 44.339 44.339 0 1 1 88.681 0z" transform="translate(94.676 317.86)"/>
49
+ <path id="path4610-5" style="stroke:#000000;stroke-width:2;fill:#c8c8c8" d="m195.66 202.84a76.01 76.01 0 1 1 -152.02 0 76.01 76.01 0 1 1 152.02 0z" transform="translate(773.03 187.66)"/>
50
+ <path id="path4612-1" style="stroke:#000000;stroke-width:2;fill:#ffffff" d="m147.8 72.633a44.339 44.339 0 1 1 -88.681 0 44.339 44.339 0 1 1 88.681 0z" transform="translate(789.21 317.86)"/>
51
+ <path id="path4610-4" style="stroke:#000000;stroke-width:2;fill:#c8c8c8" d="m195.66 202.84a76.01 76.01 0 1 1 -152.02 0 76.01 76.01 0 1 1 152.02 0z" transform="translate(773.03 1074.7)"/>
52
+ <path id="path4612-3" style="stroke:#000000;stroke-width:2;fill:#ffffff" d="m147.8 72.633a44.339 44.339 0 1 1 -88.681 0 44.339 44.339 0 1 1 88.681 0z" transform="translate(789.21 1204.9)"/>
53
+ <path id="path4499" style="stroke: rgb(0, 0, 0); stroke-width: 5; fill: rgb(255, 255, 255);" d="M 1199.9 633.86 L 1199.9 707.05 L 1267.5 707.05 L 1267.5 624 L 1242.2 609.93 L 1199.9 633.86 Z" data-name="Feu arriere droit"/>
54
+ <path id="path4501" style="stroke: rgb(0, 0, 0); stroke-width: 5; fill: rgb(255, 255, 255);" d="M 1199.9 963.24 L 1267.5 963.24 L 1267.5 1042.1 L 1243.6 1056.1 L 1199.9 1037.8 L 1199.9 963.24 Z" data-name="Feu arriere gauche"/>
55
+ <path id="path-2" style="stroke-width: 5px; stroke: rgb(0, 0, 0); paint-order: stroke; fill: rgb(255, 255, 255);" d="M -126.593 832.725 L -126.593 735.712 L -113.949 735.712 L -101.311 735.712 L -101.311 832.725 L -101.311 929.735 L -113.949 929.735 L -126.593 929.735 L -126.593 832.725 Z" data-name="Plaque immatriculation avant"></path>
56
+ <path id="path-3" style="stroke-width: 5px; stroke: rgb(0, 0, 0); paint-order: stroke; fill: rgb(255, 255, 255);" d="M 1292.264 833.899 L 1292.264 736.886 L 1304.908 736.886 L 1317.546 736.886 L 1317.546 833.899 L 1317.546 930.909 L 1304.908 930.909 L 1292.264 930.909 L 1292.264 833.899 Z" data-name="Plaque immatriculation arriere"></path>
57
+ </g>
58
+ </svg>
59
+
60
+ <script src="script.js"></script>
61
+ </body>
62
+ </html>
car_map/script.js ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Just copy & paste these functions as-is:
2
+ function sendMessageToStreamlitClient(type, data) {
3
+ var outData = Object.assign({
4
+ isStreamlitMessage: true,
5
+ type: type,
6
+ }, data);
7
+ window.parent.postMessage(outData, "*");
8
+ }
9
+ function init() {
10
+ sendMessageToStreamlitClient("streamlit:componentReady", {apiVersion: 1});
11
+ }
12
+ function setFrameHeight(height) {
13
+ sendMessageToStreamlitClient("streamlit:setFrameHeight", {height: height});
14
+ }
15
+ // The `data` argument can be any JSON-serializable value.
16
+ function sendDataToPython(data) {
17
+ // console.log("Sending data: ", data.value);
18
+ sendMessageToStreamlitClient("streamlit:setComponentValue", data);
19
+ }
20
+
21
+ // Now modify this part of the code to fit your needs:
22
+ let damaged_parts = {};
23
+ let severity_colors = {0: "#FFFFFF", 1: "#FFFF00", 2: "#FFA500", 3: "#FF0000"};
24
+
25
+ var paths = document.querySelectorAll("[data-name]");
26
+ paths.forEach(function (path) {
27
+ path.setAttribute('data-severity', 0);
28
+ });
29
+
30
+ function getDamagedParts() {
31
+ var damaged_parts = {};
32
+ paths.forEach(function (path) {
33
+ var severity = parseInt(path.getAttribute('data-severity'));
34
+ var part_name = path.dataset.name;
35
+ damaged_parts[part_name] = severity;
36
+ });
37
+ return damaged_parts;
38
+ }
39
+
40
+ paths.forEach(function (path) {
41
+ path.addEventListener("click", function () {
42
+ let severity = parseInt(path.getAttribute('data-severity'));
43
+ severity = (severity + 1) % 4;
44
+ path.setAttribute('data-severity', severity);
45
+ path.style.fill = severity_colors[severity];
46
+ // damaged_parts[path.dataset.name] = severity; // Update the damaged parts state
47
+ document.getElementById("details-box").innerHTML = `${path.dataset.name} - ${severity}`;
48
+ sendDataToPython({
49
+ value: getDamagedParts(),
50
+ dataType: "json",
51
+ });
52
+ });
53
+ });
54
+
55
+ paths.forEach(function (path) {
56
+ path.addEventListener("contextmenu", function (e) {
57
+ e.preventDefault();
58
+ let severity = (parseInt(path.getAttribute('data-severity')) + 3) % 4;
59
+ path.setAttribute('data-severity', severity);
60
+ path.style.fill = severity_colors[severity];
61
+ // damaged_parts[path.dataset.name] = severity; // Update the damaged parts state
62
+ document.getElementById("details-box").innerHTML = `${path.dataset.name} - ${severity}`;
63
+ sendDataToPython({
64
+ value: getDamagedParts(),
65
+ dataType: "json",
66
+ });
67
+ });
68
+ });
69
+
70
+ var tooltipSpan = document.getElementById('details-box');
71
+ document.addEventListener('mouseover', function (e) {
72
+ if (e.target.tagName == 'path') {
73
+ var severity = parseInt(e.target.getAttribute('data-severity'));
74
+ var part_name = e.target.dataset.name;
75
+ if (part_name == undefined) {
76
+ document.getElementById("details-box").style.opacity = "0%";
77
+ }
78
+ else {
79
+ document.getElementById("details-box").innerHTML = part_name + " - " + severity;
80
+ document.getElementById("details-box").style.opacity = "100%";
81
+ document.getElementById("details-box").style.display = "block";
82
+ }
83
+ }
84
+ else {
85
+ document.getElementById("details-box").style.opacity = "0%";
86
+ }
87
+ });
88
+
89
+ window.onmousemove = function (e) {
90
+ var x = e.clientX,
91
+ y = e.clientY;
92
+ tooltipSpan.style.top = (y + 20) + 'px';
93
+ tooltipSpan.style.left = (x) + 'px';
94
+ };
95
+
96
+ // data is any JSON-serializable value you sent from Python, and it's already deserialized for you.
97
+ function onDataFromPython(event) {
98
+ if (event.data.type !== "streamlit:render") return;
99
+ damaged_parts = event.data.args.damages; // Access values sent from Python here!
100
+ img_name = event.data.args.img_name;
101
+ reset = event.data.args.reset;
102
+ view = event.data.args.view;
103
+ if (view == "Front") {
104
+ document.getElementById("car-map").style.transform = `rotate(180deg)`;
105
+ document.getElementById("front").style.top = `90%`;
106
+ document.getElementById("back").style.bottom = `90%`;
107
+ document.getElementById("arrow").style.rotate = `180deg`;
108
+ }
109
+
110
+ // Reset annotations if requested
111
+ if (reset) {
112
+ for (var key in damaged_parts) {
113
+ damaged_parts[key] = 0; // Set each key's value to 0
114
+ }
115
+ paths.forEach(function (path) {
116
+ path.setAttribute('data-severity', 0);
117
+ path.style.fill = severity_colors[0]; // Reset to default color
118
+ });
119
+ }
120
+
121
+ for (var key in damaged_parts) {
122
+ try {
123
+ var path = document.querySelector("[data-name='" + unescape(encodeURIComponent(key)) + "']");
124
+ path.style.fill = severity_colors[damaged_parts[key]];
125
+ path.setAttribute('data-severity', damaged_parts[key]);
126
+ }
127
+ catch (error) {
128
+ console.log("Error: " + key);
129
+ }
130
+ }
131
+ sendDataToPython({
132
+ value: damaged_parts,
133
+ dataType: "json",
134
+ });
135
+ }
136
+
137
+ // Hook things up!
138
+ window.addEventListener("message", onDataFromPython);
139
+ init();
140
+
141
+ // Hack to autoset the iframe height.
142
+ window.addEventListener("load", function() {
143
+ window.setTimeout(function() {
144
+ setFrameHeight(document.documentElement.clientHeight)
145
+ }, 0);
146
+ });
147
+
148
+ // Optionally, if the automatic height computation fails you, give this component a height manually
149
+ setFrameHeight(440);
car_map/styles.css ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @import url('https://fonts.googleapis.com/css2?family=Poppins&display=swap');
2
+
3
+ path {
4
+ stroke: white;
5
+ fill:white;
6
+ transition: fill .4s ease;
7
+ cursor: pointer;
8
+ }
9
+
10
+ #car-map {
11
+ position: absolute;
12
+ top: 0;
13
+ left: 0;
14
+ width: 100%;
15
+ transform-origin: 50% 35%;
16
+ }
17
+
18
+ #details-box {
19
+ padding: 0.5rem;
20
+ border-radius: 8px;
21
+ font-size: 18px;
22
+ position: fixed;
23
+ color: white;
24
+ font-family: "Source Sans Pro";
25
+ background-color: black;
26
+ width: fit-content;
27
+ transform: translateX(-50%);
28
+ transition: opacity .4s ease;
29
+ z-index: 1;
30
+ display: none;
31
+ }
32
+
33
+ #view-assist {
34
+ position: absolute;
35
+ height: 100%;
36
+ width: 100%;
37
+ }
38
+ #view-assist #front {
39
+ font-family: "Source Sans Pro";
40
+ position: absolute;
41
+ top: 20px;
42
+ right: 25px;
43
+ margin: 0;
44
+ }
45
+ #view-assist #back {
46
+ font-family: "Source Sans Pro";
47
+ position: absolute;
48
+ bottom: 20px;
49
+ right: 25px;
50
+ margin: 8px 0;
51
+ }
52
+ #view-assist > #arrow {
53
+ position: absolute;
54
+ right: 18px;
55
+ top: 50%;
56
+ transform: translateY(-50%);
57
+ width: 50px;
58
+ height: 50%;
59
+ object-fit: cover;
60
+ transform-origin: 50% 0%;
61
+ }
custom_style.css ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .st-emotion-cache-1jicfl2 {
2
+ padding-top: 4rem;
3
+ }
4
+
5
+ /* .st-emotion-cache-vdokb0 p {
6
+ text-align: center;
7
+ margin-top: 7px;
8
+ } */
9
+
10
+ .st-dc, .st-cx {
11
+ margin-top: 5px !important;
12
+ }
13
+ .st-af {
14
+ justify-content: center;
15
+ margin-top: 0px;
16
+ margin-bottom: 0px;
17
+ }
18
+ div.row-widget.stRadio > div[role="radiogroup"] > label[data-baseweb="radio"] {
19
+ background-color: #f4ca9a;
20
+ padding-right: 38px;
21
+ padding-left: 30px;
22
+ padding-bottom: 6px;
23
+ padding-top: 4px;
24
+ margin: 0 6px;
25
+ }
26
+ div.row-widget.stRadio > div[role="radiogroup"] > label[data-baseweb="radio"]:hover {
27
+ background-color: #eb9a68;
28
+ }
29
+ .st-cj {
30
+ background-color: rgb(255, 75, 75);
31
+ }
functions.py ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import boto3
2
+ from PIL import Image
3
+ import pandas as pd
4
+ import streamlit as st
5
+ import random
6
+ import io
7
+
8
+ s3_client = boto3.client('s3',
9
+ aws_access_key_id=st.secrets["aws_access_key_id"],
10
+ aws_secret_access_key=st.secrets["aws_secret_access_key"],
11
+ region_name='eu-west-3')
12
+ bucket_name = "sygma-global-data-storage"
13
+ folder = "car-damage-detection/scrappedImages/"
14
+
15
+ csv_folder = "car-damage-detection/CSVs/"
16
+ s3_df_path = csv_folder + "70k_old_annotations_fixed.csv"
17
+ response = s3_client.get_object(Bucket=bucket_name, Key=s3_df_path)
18
+ # df = pd.read_csv("CSVs/70k_old_annotations_fixed.csv", low_memory=False)
19
+ with io.BytesIO(response['Body'].read()) as bio:
20
+ df = pd.read_csv(bio, low_memory=False)
21
+ df = df[df['s3_available'] == True]
22
+
23
+ def get_random_image():
24
+ not_validated_imgs = df[df["validated"] == False]["img_name"].tolist()
25
+ if len(not_validated_imgs) == 0:
26
+ return None, None
27
+ image_name = random.choice(not_validated_imgs)
28
+ s3_image_path = folder + image_name
29
+ try:
30
+ response = s3_client.get_object(Bucket=bucket_name, Key=s3_image_path)
31
+ image = Image.open(io.BytesIO(response['Body'].read())).resize((1000, 800))
32
+ return image, image_name
33
+ except:
34
+ return get_random_image()
35
+
36
+ def get_img_damages(img_name):
37
+ img_row = df.loc[df["img_name"] == img_name]
38
+ damages = img_row.iloc[0, 6:].to_dict()
39
+ return damages
40
+
41
+ def process_image(img_name, annotator_name, is_car, skip, rotation, damaged_parts):
42
+ df.loc[df["img_name"] == img_name, "annotator_name"] = annotator_name
43
+ df.loc[df["img_name"] == img_name, "is_car"] = is_car
44
+ df.loc[df["img_name"] == img_name, "rotation"] = rotation
45
+ if not skip:
46
+ df.loc[df["img_name"] == img_name, damaged_parts.keys()] = damaged_parts.values()
47
+ df.loc[df["img_name"] == img_name, "validated"] = not skip
48
+ # df.to_csv("CSVs/70k_old_annotations_fixed.csv", index=False)
49
+ s3_client.put_object(Bucket=bucket_name, Key=s3_df_path, Body=df.to_csv(index=False))
requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ boto3