wesam0099 commited on
Commit
1512569
·
1 Parent(s): 5f6c42f

Back to original working version

Browse files
.gitattributes CHANGED
@@ -14,4 +14,3 @@
14
  *.mp4 filter=lfs diff=lfs merge=lfs -text
15
  # Documentation
16
  *.pdf filter=lfs diff=lfs merge=lfs -text
17
- models/trained/*.onnx filter=lfs diff=lfs merge=lfs -text
 
14
  *.mp4 filter=lfs diff=lfs merge=lfs -text
15
  # Documentation
16
  *.pdf filter=lfs diff=lfs merge=lfs -text
 
Dockerfile CHANGED
@@ -23,11 +23,11 @@ RUN mkdir -p output/reports logs
23
  # Expose port 7860 for Hugging Face Spaces
24
  EXPOSE 7860
25
 
26
- # Run streamlit with proper configuration
27
- CMD streamlit run app.py \
28
- --server.port=7860 \
29
- --server.address=0.0.0.0 \
30
- --server.headless=true \
31
- --server.runOnSave=false \
32
- --server.fileWatcherType=none \
33
- --browser.gatherUsageStats=false
 
23
  # Expose port 7860 for Hugging Face Spaces
24
  EXPOSE 7860
25
 
26
+ # Environment variables
27
+ ENV STREAMLIT_SERVER_PORT=7860
28
+ ENV STREAMLIT_SERVER_ADDRESS=0.0.0.0
29
+ ENV STREAMLIT_SERVER_HEADLESS=true
30
+ ENV STREAMLIT_BROWSER_GATHER_USAGE_STATS=false
31
+
32
+ # Run the app
33
+ CMD ["streamlit", "run", "app.py", "--server.port=7860", "--server.address=0.0.0.0"]
README.md DELETED
@@ -1,28 +0,0 @@
1
- ---
2
- title: CrashLens AI Accident Analyzer
3
- emoji: 🚗💥
4
- colorFrom: blue
5
- colorTo: indigo
6
- sdk: docker
7
- app_port: 7860
8
- ---
9
-
10
- # 🚗 CrashLens - AI Traffic Accident Reconstruction
11
-
12
- **Huawei AI Innovation Challenge 2026**
13
-
14
- AI-powered traffic accident analysis system using MindSpore framework.
15
-
16
- ## Features
17
- - 🧠 AI collision type prediction
18
- - 📊 Fault percentage calculation
19
- - 🗺️ Interactive accident mapping
20
- - 📈 2D animated simulations
21
- - 📄 Comprehensive HTML reports
22
-
23
- ## Technology
24
- - **AI**: MindSpore → ONNX
25
- - **Framework**: Streamlit
26
- - **Institution**: Jubail Industrial College
27
-
28
- Built by Wes - AI Engineer
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app.py CHANGED
@@ -1,169 +1,1107 @@
1
  """
2
- CrashLens - Fixed Version (No Rerun Loops)
 
 
 
 
 
 
3
  """
 
4
  import streamlit as st
5
  import sys
6
  from pathlib import Path
7
 
 
8
  sys.path.insert(0, str(Path(__file__).parent))
9
 
 
 
 
 
 
 
 
 
 
 
 
10
  st.set_page_config(
11
- page_title="CrashLens AI",
12
- page_icon="🚗",
13
- layout="wide"
 
14
  )
15
 
16
- # Initialize session state
17
- if 'analysis_done' not in st.session_state:
18
- st.session_state.analysis_done = False
19
- if 'results' not in st.session_state:
20
- st.session_state.results = None
21
-
22
- # Header
23
- st.title("🚗 CrashLens - AI Traffic Accident Analyzer")
24
- st.markdown("**Huawei AI Innovation Challenge 2026** | Jubail Industrial College")
25
- st.markdown("---")
26
-
27
- # Sidebar
28
- with st.sidebar:
29
- st.image("assets/mindspore_logo.png", width=200)
30
- st.markdown("### 🧠 Powered by MindSpore AI")
31
- st.markdown("---")
32
- st.markdown("### 📋 How to Use")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  st.markdown("""
34
- 1. Enter accident location
35
- 2. Add vehicle details
36
- 3. Click Analyze
37
- 4. View AI predictions
38
- """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
 
40
- # Main content - Use tabs instead of steps
41
- tab1, tab2, tab3 = st.tabs(["📍 Location", "🚗 Vehicles", "🔬 Analysis"])
 
42
 
43
- with tab1:
44
- st.subheader("Accident Location")
 
 
 
 
 
 
 
 
 
 
 
45
 
46
- col1, col2 = st.columns(2)
47
  with col1:
48
- location_name = st.text_input("Location Name", "Seef District Roundabout")
49
- latitude = st.number_input("Latitude", value=26.2397, format="%.4f")
50
- longitude = st.number_input("Longitude", value=50.5369, format="%.4f")
51
 
52
  with col2:
53
- weather = st.selectbox("Weather", ["clear", "cloudy", "rainy", "foggy"])
54
- road_condition = st.selectbox("Road Condition", ["dry", "wet", "sandy", "oily"])
55
- road_type = st.selectbox("Road Type", ["roundabout", "crossroad", "highway", "urban_road"])
56
 
57
- st.success("✅ Location configured")
 
 
 
 
 
 
 
 
 
58
 
59
- with tab2:
60
- st.subheader("Vehicle Information")
 
 
 
 
 
 
 
61
 
 
 
 
 
 
 
 
 
62
  col1, col2 = st.columns(2)
63
 
64
  with col1:
65
- st.markdown("**🚗 Vehicle 1 (Red)**")
66
- v1_type = st.selectbox("Type", ["sedan", "suv", "truck"], key="v1_type")
67
- v1_speed = st.slider("Speed (km/h)", 0, 120, 50, key="v1_speed")
68
- v1_direction = st.selectbox("Direction", ["north", "south", "east", "west"], key="v1_dir")
69
- v1_action = st.selectbox("Action", ["going_straight", "turning_left", "turning_right"], key="v1_action")
70
 
71
  with col2:
72
- st.markdown("**🚙 Vehicle 2 (Blue)**")
73
- v2_type = st.selectbox("Type", ["sedan", "suv", "truck"], key="v2_type")
74
- v2_speed = st.slider("Speed (km/h)", 0, 120, 50, key="v2_speed")
75
- v2_direction = st.selectbox("Direction", ["north", "south", "east", "west"], key="v2_dir")
76
- v2_action = st.selectbox("Action", ["going_straight", "turning_left", "turning_right"], key="v2_action")
77
 
78
- st.success("✅ Vehicles configured")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
 
80
- with tab3:
81
- st.subheader("AI Analysis")
82
 
83
- if st.button("🧠 Run AI Analysis", type="primary", use_container_width=True):
84
-
85
- with st.spinner("Analyzing accident scenario..."):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
  try:
87
- # Prepare data
88
- accident_info = {
89
- 'location': {'name': location_name, 'lat': latitude, 'lng': longitude},
90
- 'weather': weather,
91
- 'road_condition': road_condition,
92
- 'road_type': road_type,
93
- 'visibility': 1.0,
94
- 'lighting': 'daylight'
95
- }
96
 
97
- vehicle_1 = {
98
- 'type': v1_type,
99
- 'speed': v1_speed,
100
- 'direction': v1_direction,
101
- 'action': v1_action,
102
- 'braking': False,
103
- 'signaling': False
104
- }
105
 
106
- vehicle_2 = {
107
- 'type': v2_type,
108
- 'speed': v2_speed,
109
- 'direction': v2_direction,
110
- 'action': v2_action,
111
- 'braking': False,
112
- 'signaling': False
113
- }
114
 
115
- # Run analysis
116
  from analysis.scenario_analyzer import analyze_accident
117
 
118
  results = analyze_accident(
119
- accident_info=accident_info,
120
- vehicle_1=vehicle_1,
121
- vehicle_2=vehicle_2
122
  )
123
 
124
- st.session_state.results = results
125
- st.session_state.analysis_done = True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
 
127
  except Exception as e:
128
- st.error(f"Analysis error: {e}")
129
- import traceback
130
- with st.expander("Error Details"):
131
- st.code(traceback.format_exc())
132
 
133
- # Display results
134
- if st.session_state.analysis_done and st.session_state.results:
135
- st.success("✅ Analysis Complete!")
 
 
 
 
 
 
 
 
 
 
 
 
 
136
 
137
- results = st.session_state.results
 
 
 
 
 
138
 
139
- # Show collision type
140
- st.markdown("### 🎯 Predicted Collision Type")
141
- collision_type = results.get('collision_type', 'Unknown')
142
- st.info(f"**{collision_type.replace('_', ' ').title()}**")
 
 
143
 
144
- # Show scenarios
145
- st.markdown("### 📊 Scenarios")
146
- scenarios = results.get('scenarios', [])
 
 
 
147
 
 
 
148
  for i, scenario in enumerate(scenarios[:3], 1):
149
- with st.expander(f"Scenario {i}: {scenario.get('type', 'Unknown').replace('_', ' ').title()}"):
150
- col1, col2 = st.columns(2)
151
-
152
- with col1:
153
- st.metric("Probability", f"{scenario.get('probability', 0)*100:.1f}%")
154
- st.write("**Fault Distribution:**")
155
- st.write(f"- Vehicle 1: {scenario.get('fault_percentage_1', 50)}%")
156
- st.write(f"- Vehicle 2: {scenario.get('fault_percentage_2', 50)}%")
157
-
158
- with col2:
159
- # Show animation if exists
160
- gif_path = f"output/visualizations/{scenario.get('type', 'head_on_collision')}.gif"
161
- if Path(gif_path).exists():
162
- st.image(gif_path, caption="Simulation")
163
-
164
- # Download report
165
- if st.button("📄 Download Report"):
166
- st.info("Report generation coming soon!")
167
-
168
- st.markdown("---")
169
- st.markdown("© 2026 Jubail Industrial College | Huawei AI Innovation Challenge")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  """
2
+ Traffic Accident Reconstruction System
3
+ ======================================
4
+ Main Streamlit Application
5
+ Huawei AI Innovation Challenge 2026
6
+
7
+ This system uses MindSpore AI to analyze traffic accidents
8
+ and generate probable scenarios with 2D simulation.
9
  """
10
+
11
  import streamlit as st
12
  import sys
13
  from pathlib import Path
14
 
15
+ # Add project root to path
16
  sys.path.insert(0, str(Path(__file__).parent))
17
 
18
+ from config import STREAMLIT_CONFIG, CASE_STUDY_LOCATION, COLORS
19
+ from ui.components import render_sidebar, render_header, render_footer
20
+ from ui.map_viewer import render_map_section
21
+ from ui.vehicle_input import render_vehicle_input
22
+ from ui.party_input import render_party_input, render_evidence_upload, render_party_summary
23
+ from ui.results_display import render_results
24
+
25
+ # ============================================================
26
+ # PAGE CONFIGURATION
27
+ # ============================================================
28
+
29
  st.set_page_config(
30
+ page_title=STREAMLIT_CONFIG["page_title"],
31
+ page_icon=STREAMLIT_CONFIG["page_icon"],
32
+ layout=STREAMLIT_CONFIG["layout"],
33
+ initial_sidebar_state=STREAMLIT_CONFIG["initial_sidebar_state"]
34
  )
35
 
36
+ # ============================================================
37
+ # CUSTOM CSS - CrashLens Modern Design
38
+ # ============================================================
39
+
40
+ st.markdown("""
41
+ <style>
42
+ /* Layout - Dark theme with modern spacing */
43
+ .block-container {
44
+ padding-top: 1.4rem;
45
+ padding-bottom: 2.5rem;
46
+ max-width: 1200px;
47
+ }
48
+
49
+ [data-testid="stSidebar"] {
50
+ min-width: 340px;
51
+ max-width: 340px;
52
+ background: linear-gradient(180deg, #0e1117 0%, #1a1f2e 100%);
53
+ }
54
+
55
+ /* Main container with gradient background */
56
+ .main {
57
+ background: linear-gradient(135deg, #0a0e27 0%, #1a1f3a 50%, #0f1419 100%);
58
+ color: rgba(255, 255, 255, 0.95);
59
+ }
60
+
61
+ /* Header styling - Modern minimal */
62
+ .main-header {
63
+ background: linear-gradient(135deg, rgba(30, 58, 95, 0.3) 0%, rgba(45, 90, 135, 0.2) 100%);
64
+ padding: 2rem;
65
+ border-radius: 16px;
66
+ border: 1px solid rgba(255, 255, 255, 0.12);
67
+ color: white;
68
+ margin-bottom: 2rem;
69
+ text-align: center;
70
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
71
+ }
72
+
73
+ .main-header h1 {
74
+ margin: 0;
75
+ font-size: 2.8rem;
76
+ font-weight: 800;
77
+ letter-spacing: 0.5px;
78
+ background: linear-gradient(135deg, #60a5fa 0%, #3b82f6 100%);
79
+ -webkit-background-clip: text;
80
+ -webkit-text-fill-color: transparent;
81
+ }
82
+
83
+ .main-header p {
84
+ margin: 0.8rem 0 0 0;
85
+ opacity: 0.75;
86
+ font-size: 1.05rem;
87
+ color: rgba(255, 255, 255, 0.72);
88
+ }
89
+
90
+ /* Card styling - Glass morphism effect */
91
+ .info-card {
92
+ background: rgba(255, 255, 255, 0.04);
93
+ backdrop-filter: blur(10px);
94
+ padding: 1.5rem;
95
+ border-radius: 16px;
96
+ border: 1px solid rgba(255, 255, 255, 0.12);
97
+ margin: 1rem 0;
98
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
99
+ }
100
+
101
+ /* Step indicator - Modern pills */
102
+ .step-indicator {
103
+ display: flex;
104
+ justify-content: space-between;
105
+ margin: 2rem 0;
106
+ gap: 0.5rem;
107
+ }
108
+
109
+ .step {
110
+ flex: 1;
111
+ text-align: center;
112
+ padding: 1rem;
113
+ background: rgba(255, 255, 255, 0.05);
114
+ border: 1px solid rgba(255, 255, 255, 0.1);
115
+ margin: 0;
116
+ border-radius: 12px;
117
+ position: relative;
118
+ transition: all 0.3s ease;
119
+ }
120
+
121
+ .step.active {
122
+ background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
123
+ border-color: #3b82f6;
124
+ color: white;
125
+ box-shadow: 0 8px 24px rgba(59, 130, 246, 0.3);
126
+ }
127
+
128
+ .step.completed {
129
+ background: linear-gradient(135deg, #10b981 0%, #059669 100%);
130
+ border-color: #10b981;
131
+ color: white;
132
+ }
133
+
134
+ /* Vehicle cards - Dark theme */
135
+ .vehicle-card {
136
+ background: rgba(255, 255, 255, 0.06);
137
+ border: 1px solid rgba(255, 255, 255, 0.15);
138
+ border-radius: 12px;
139
+ padding: 1.5rem;
140
+ margin: 1rem 0;
141
+ }
142
+
143
+ .vehicle-card.vehicle-1 {
144
+ border-color: rgba(255, 75, 75, 0.4);
145
+ background: rgba(255, 75, 75, 0.08);
146
+ }
147
+
148
+ .vehicle-card.vehicle-2 {
149
+ border-color: rgba(75, 123, 255, 0.4);
150
+ background: rgba(75, 123, 255, 0.08);
151
+ }
152
+
153
+ /* Results styling - Modern cards */
154
+ .scenario-card {
155
+ background: rgba(255, 255, 255, 0.06);
156
+ border-radius: 12px;
157
+ padding: 1.5rem;
158
+ margin: 1rem 0;
159
+ box-shadow: 0 4px 12px rgba(0,0,0,0.2);
160
+ border: 1px solid rgba(255, 255, 255, 0.1);
161
+ }
162
+
163
+ .probability-high {
164
+ color: #10b981;
165
+ font-weight: bold;
166
+ }
167
+
168
+ .probability-medium {
169
+ color: #f59e0b;
170
+ font-weight: bold;
171
+ }
172
+
173
+ .probability-low {
174
+ color: #ef4444;
175
+ font-weight: bold;
176
+ }
177
+
178
+ /* Button styling - Modern gradient */
179
+ .stButton > button {
180
+ width: 100%;
181
+ background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
182
+ color: white;
183
+ border: none;
184
+ padding: 0.85rem 1.5rem;
185
+ border-radius: 12px;
186
+ font-weight: 600;
187
+ font-size: 1rem;
188
+ transition: all 0.3s ease;
189
+ box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
190
+ }
191
+
192
+ .stButton > button:hover {
193
+ background: linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%);
194
+ box-shadow: 0 6px 16px rgba(59, 130, 246, 0.4);
195
+ transform: translateY(-2px);
196
+ }
197
+
198
+ /* Input fields - Dark theme with better contrast */
199
+ .stTextInput > div > div > input,
200
+ .stTextArea > div > div > textarea,
201
+ .stSelectbox > div > div > select,
202
+ .stNumberInput > div > div > input {
203
+ background: rgba(255, 255, 255, 0.08) !important;
204
+ border: 1px solid rgba(255, 255, 255, 0.15) !important;
205
+ border-radius: 10px !important;
206
+ color: white !important;
207
+ padding: 0.75rem !important;
208
+ }
209
+
210
+ .stTextInput > div > div > input:focus,
211
+ .stTextArea > div > div > textarea:focus,
212
+ .stSelectbox > div > div > select:focus,
213
+ .stNumberInput > div > div > input:focus {
214
+ border-color: #3b82f6 !important;
215
+ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15) !important;
216
+ }
217
+
218
+ /* Labels - Better visibility */
219
+ label, .stTextInput > label, .stTextArea > label, .stSelectbox > label, .stSlider > label, .stNumberInput > label {
220
+ color: rgba(255, 255, 255, 0.85) !important;
221
+ font-weight: 500 !important;
222
+ font-size: 0.95rem !important;
223
+ }
224
+
225
+ /* Metric cards - Modern design */
226
+ [data-testid="stMetricValue"] {
227
+ font-size: 2.2rem;
228
+ font-weight: 700;
229
+ color: white;
230
+ }
231
+
232
+ [data-testid="stMetricLabel"] {
233
+ color: rgba(255, 255, 255, 0.7);
234
+ font-size: 0.95rem;
235
+ font-weight: 500;
236
+ }
237
+
238
+ /* Tabs styling - Modern */
239
+ .stTabs [data-baseweb="tab-list"] {
240
+ gap: 8px;
241
+ background: rgba(255, 255, 255, 0.03);
242
+ padding: 0.5rem;
243
+ border-radius: 12px;
244
+ }
245
+
246
+ .stTabs [data-baseweb="tab"] {
247
+ background: transparent;
248
+ border-radius: 8px;
249
+ color: rgba(255, 255, 255, 0.7);
250
+ font-weight: 500;
251
+ padding: 0.75rem 1.5rem;
252
+ border: 1px solid transparent;
253
+ }
254
+
255
+ .stTabs [aria-selected="true"] {
256
+ background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
257
+ color: white !important;
258
+ border-color: #3b82f6;
259
+ }
260
+
261
+ /* Messages - Modern alerts */
262
+ .stSuccess, .element-container:has(.stSuccess) {
263
+ background: rgba(16, 185, 129, 0.15) !important;
264
+ border: 1px solid rgba(16, 185, 129, 0.3) !important;
265
+ border-radius: 12px !important;
266
+ padding: 1rem !important;
267
+ }
268
+
269
+ .stSuccess [data-testid="stMarkdownContainer"] p {
270
+ color: #10b981 !important;
271
+ }
272
+
273
+ .stError {
274
+ background: rgba(239, 68, 68, 0.15) !important;
275
+ border: 1px solid rgba(239, 68, 68, 0.3) !important;
276
+ border-radius: 12px !important;
277
+ }
278
+
279
+ .stWarning {
280
+ background: rgba(245, 158, 11, 0.15) !important;
281
+ border: 1px solid rgba(245, 158, 11, 0.3) !important;
282
+ border-radius: 12px !important;
283
+ }
284
+
285
+ .stInfo {
286
+ background: rgba(59, 130, 246, 0.15) !important;
287
+ border: 1px solid rgba(59, 130, 246, 0.3) !important;
288
+ border-radius: 12px !important;
289
+ }
290
+
291
+ /* Sidebar - Better styling */
292
+ [data-testid="stSidebar"] h1, [data-testid="stSidebar"] h2, [data-testid="stSidebar"] h3 {
293
+ color: white !important;
294
+ }
295
+
296
+ [data-testid="stSidebar"] p, [data-testid="stSidebar"] span {
297
+ color: rgba(255, 255, 255, 0.85) !important;
298
+ }
299
+
300
+ /* Expander - Modern style */
301
+ .streamlit-expanderHeader {
302
+ background: rgba(255, 255, 255, 0.05) !important;
303
+ border-radius: 10px !important;
304
+ border: 1px solid rgba(255, 255, 255, 0.1) !important;
305
+ color: white !important;
306
+ font-weight: 500 !important;
307
+ }
308
+
309
+ .streamlit-expanderContent {
310
+ background: rgba(255, 255, 255, 0.02) !important;
311
+ border: 1px solid rgba(255, 255, 255, 0.08) !important;
312
+ border-radius: 0 0 10px 10px !important;
313
+ }
314
+
315
+ /* Checkbox and Radio - Better visibility */
316
+ .stCheckbox label, .stRadio label {
317
+ color: rgba(255, 255, 255, 0.85) !important;
318
+ }
319
+
320
+ /* Slider - Modern */
321
+ .stSlider [data-baseweb="slider"] {
322
+ background: rgba(59, 130, 246, 0.2);
323
+ }
324
+
325
+ /* Headings - Better hierarchy */
326
+ h1, h2, h3, h4, h5, h6 {
327
+ color: white !important;
328
+ }
329
+
330
+ h1 {
331
+ font-weight: 800 !important;
332
+ letter-spacing: -0.5px;
333
+ }
334
+
335
+ h2 {
336
+ font-weight: 700 !important;
337
+ letter-spacing: -0.3px;
338
+ }
339
+
340
+ h3 {
341
+ font-weight: 600 !important;
342
+ }
343
+
344
+ /* Markdown text - Better readability */
345
+ p, span, div {
346
+ color: rgba(255, 255, 255, 0.9);
347
+ }
348
+
349
+ /* Code blocks - Dark theme */
350
+ code {
351
+ background: rgba(255, 255, 255, 0.08) !important;
352
+ color: #60a5fa !important;
353
+ padding: 0.2rem 0.4rem !important;
354
+ border-radius: 4px !important;
355
+ }
356
+
357
+ /* Dataframe - Dark theme */
358
+ [data-testid="stDataFrame"] {
359
+ background: rgba(255, 255, 255, 0.05);
360
+ border-radius: 12px;
361
+ border: 1px solid rgba(255, 255, 255, 0.1);
362
+ }
363
+
364
+ /* Hide Streamlit branding */
365
+ #MainMenu {visibility: hidden;}
366
+ footer {visibility: hidden;}
367
+
368
+ /* Progress bar */
369
+ .stProgress > div > div {
370
+ background: linear-gradient(90deg, #3b82f6 0%, #8b5cf6 100%);
371
+ border-radius: 8px;
372
+ }
373
+ </style>
374
+ """, unsafe_allow_html=True)
375
+
376
+ # ============================================================
377
+ # SESSION STATE INITIALIZATION
378
+ # ============================================================
379
+
380
+ def init_session_state():
381
+ """Initialize all session state variables."""
382
+
383
+ # Application state
384
+ if 'current_step' not in st.session_state:
385
+ st.session_state.current_step = 1
386
+
387
+ # Accident information
388
+ if 'accident_info' not in st.session_state:
389
+ st.session_state.accident_info = {
390
+ 'location': CASE_STUDY_LOCATION.copy(),
391
+ 'datetime': None,
392
+ 'road_type': 'roundabout',
393
+ 'weather': 'clear',
394
+ 'road_condition': 'dry',
395
+ 'visibility': 1.0,
396
+ 'lighting': 'daylight',
397
+ 'notes': ''
398
+ }
399
+
400
+ # Vehicle 1 data (for analysis)
401
+ if 'vehicle_1' not in st.session_state:
402
+ st.session_state.vehicle_1 = {
403
+ 'type': 'sedan',
404
+ 'speed': 50,
405
+ 'direction': 'north',
406
+ 'action': 'entering_roundabout',
407
+ 'braking': False,
408
+ 'signaling': False,
409
+ 'lights_on': True,
410
+ 'horn_used': False,
411
+ 'path': [],
412
+ 'description': ''
413
+ }
414
+
415
+ # Vehicle 2 data (for analysis)
416
+ if 'vehicle_2' not in st.session_state:
417
+ st.session_state.vehicle_2 = {
418
+ 'type': 'sedan',
419
+ 'speed': 50,
420
+ 'direction': 'east',
421
+ 'action': 'going_straight',
422
+ 'braking': False,
423
+ 'signaling': False,
424
+ 'lights_on': True,
425
+ 'horn_used': False,
426
+ 'path': [],
427
+ 'description': ''
428
+ }
429
+
430
+ # Party 1 data (driver details)
431
+ if 'party_1' not in st.session_state:
432
+ st.session_state.party_1 = {
433
+ 'full_name': '',
434
+ 'id_iqama': '',
435
+ 'phone': '',
436
+ 'role': 'Driver',
437
+ 'vehicle_make_model': '',
438
+ 'plate_number': '',
439
+ 'insurance': '',
440
+ 'damage_notes': '',
441
+ 'statement': ''
442
+ }
443
+
444
+ # Party 2 data (driver details)
445
+ if 'party_2' not in st.session_state:
446
+ st.session_state.party_2 = {
447
+ 'full_name': '',
448
+ 'id_iqama': '',
449
+ 'phone': '',
450
+ 'role': 'Driver',
451
+ 'vehicle_make_model': '',
452
+ 'plate_number': '',
453
+ 'insurance': '',
454
+ 'damage_notes': '',
455
+ 'statement': ''
456
+ }
457
+
458
+ # Evidence photos
459
+ if 'evidence_photos' not in st.session_state:
460
+ st.session_state.evidence_photos = []
461
+
462
+ # Analysis results
463
+ if 'analysis_results' not in st.session_state:
464
+ st.session_state.analysis_results = None
465
+
466
+ # Generated scenarios
467
+ if 'scenarios' not in st.session_state:
468
+ st.session_state.scenarios = []
469
+
470
+ # Map data
471
+ if 'map_data' not in st.session_state:
472
+ st.session_state.map_data = None
473
+
474
+ # Analysis ready flag
475
+ if 'analysis_ready' not in st.session_state:
476
+ st.session_state.analysis_ready = False
477
+
478
+ # ============================================================
479
+ # MAIN APPLICATION
480
+ # ============================================================
481
+
482
+ def main():
483
+ """Main application entry point."""
484
+
485
+ # Initialize session state
486
+ init_session_state()
487
+
488
+ # Render modern header - clean without logo
489
+ col1, col2, col3 = st.columns([1, 6, 1])
490
+ with col1:
491
+ st.markdown("## 🚗")
492
+ with col2:
493
+ st.markdown("""
494
+ <div style='padding-top: 0.5rem;'>
495
+ <h1 style='margin: 0; font-size: 2.5rem; font-weight: 800; background: linear-gradient(135deg, #60a5fa 0%, #3b82f6 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent;'>CrashLens AI</h1>
496
+ <p style='margin: 0.3rem 0 0 0; color: rgba(255, 255, 255, 0.7); font-size: 1rem;'>Traffic Accident Scenario Analysis • MindSpore-ready • PDF report</p>
497
+ </div>
498
+ """, unsafe_allow_html=True)
499
+ with col3:
500
+ st.write("")
501
+
502
+ st.markdown("<br>", unsafe_allow_html=True)
503
+
504
+ # Render sidebar
505
+ render_sidebar()
506
+
507
+ # Modern step indicator with pills - Using columns to avoid HTML rendering issues
508
+ steps = ["📍 Location", "🚙 Vehicle 1", "🚗 Vehicle 2", "👥 Parties", "📄 Evidence", "🔍 Analysis", "📊 Results"]
509
+ cols = st.columns(len(steps))
510
+
511
+ for i, (col, step_name) in enumerate(zip(cols, steps), 1):
512
+ with col:
513
+ if i < st.session_state.current_step:
514
+ # Completed step - Green
515
+ st.markdown(f"""
516
+ <div style='text-align: center; padding: 0.75rem 0.25rem;
517
+ background: linear-gradient(135deg, #10b981 0%, #059669 100%);
518
+ color: white; border-radius: 10px; font-size: 0.8rem; font-weight: 600;
519
+ box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3);'>
520
+ ✓ {step_name.split()[-1]}
521
+ </div>
522
+ """, unsafe_allow_html=True)
523
+ elif i == st.session_state.current_step:
524
+ # Active step - Blue gradient
525
+ st.markdown(f"""
526
+ <div style='text-align: center; padding: 0.75rem 0.25rem;
527
+ background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
528
+ color: white; border-radius: 10px; font-size: 0.8rem; font-weight: 600;
529
+ box-shadow: 0 6px 16px rgba(59, 130, 246, 0.4);
530
+ border: 2px solid rgba(96, 165, 250, 0.5);'>
531
+ {step_name}
532
+ </div>
533
+ """, unsafe_allow_html=True)
534
+ else:
535
+ # Future step - Dark gray
536
+ st.markdown(f"""
537
+ <div style='text-align: center; padding: 0.75rem 0.25rem;
538
+ background: rgba(255, 255, 255, 0.05);
539
+ color: rgba(255, 255, 255, 0.4); border-radius: 10px; font-size: 0.8rem;
540
+ border: 1px solid rgba(255, 255, 255, 0.1);'>
541
+ {step_name.split()[-1]}
542
+ </div>
543
+ """, unsafe_allow_html=True)
544
+
545
+ st.markdown("<div style='margin: 1.5rem 0; height: 1px; background: linear-gradient(90deg, transparent, rgba(255,255,255,0.1), transparent);'></div>", unsafe_allow_html=True)
546
+
547
+ # Main content based on current step
548
+ if st.session_state.current_step == 1:
549
+ render_step_1_location()
550
+ elif st.session_state.current_step == 2:
551
+ render_step_2_vehicle1()
552
+ elif st.session_state.current_step == 3:
553
+ render_step_3_vehicle2()
554
+ elif st.session_state.current_step == 4:
555
+ render_step_4_parties()
556
+ elif st.session_state.current_step == 5:
557
+ render_step_5_evidence()
558
+ elif st.session_state.current_step == 6:
559
+ render_step_6_analysis()
560
+ elif st.session_state.current_step == 7:
561
+ render_step_7_results()
562
+
563
+ # Render footer
564
+ render_footer()
565
+
566
+
567
+ # ============================================================
568
+ # STEP 1: LOCATION SELECTION
569
+ # ============================================================
570
+
571
+ def render_step_1_location():
572
+ """Render the location selection step."""
573
+
574
+ st.header("📍 Step 1: Accident Location")
575
+
576
+ st.markdown("""
577
+ <div class="info-card">
578
+ <h4>Accident Details</h4>
579
+ <p>Enter the location and conditions where the accident occurred.</p>
580
+ </div>
581
+ """, unsafe_allow_html=True)
582
+
583
+ col1, col2 = st.columns([2, 1])
584
+
585
+ with col1:
586
+ # Map display
587
+ render_map_section()
588
+
589
+ with col2:
590
+ st.subheader("Location Details")
591
+
592
+ # Location name
593
+ location_name = st.text_input(
594
+ "Location Name",
595
+ value=st.session_state.accident_info['location']['name']
596
+ )
597
+
598
+ # Coordinates
599
+ lat = st.number_input(
600
+ "Latitude",
601
+ value=st.session_state.accident_info['location']['latitude'],
602
+ format="%.6f",
603
+ step=0.0001
604
+ )
605
+
606
+ lon = st.number_input(
607
+ "Longitude",
608
+ value=st.session_state.accident_info['location']['longitude'],
609
+ format="%.6f",
610
+ step=0.0001
611
+ )
612
+
613
+ # Road type - expanded options
614
+ road_types = [
615
+ 'roundabout', 'crossroad', 't_junction', 'highway_merge',
616
+ 'parking', 'highway', 'urban_road', 'other'
617
+ ]
618
+ road_type_labels = {
619
+ 'roundabout': 'Roundabout (دوار)',
620
+ 'crossroad': 'Crossroad (تقاطع)',
621
+ 't_junction': 'T-Junction',
622
+ 'highway_merge': 'Highway Merge',
623
+ 'parking': 'Parking / Low Speed',
624
+ 'highway': 'Highway',
625
+ 'urban_road': 'Urban Road',
626
+ 'other': 'Other'
627
+ }
628
+
629
+ road_type = st.selectbox(
630
+ "Road Type",
631
+ options=road_types,
632
+ format_func=lambda x: road_type_labels.get(x, x.title()),
633
+ index=road_types.index(st.session_state.accident_info.get('road_type', 'roundabout'))
634
+ )
635
+
636
+ # Date and time
637
+ accident_date = st.date_input("Accident Date")
638
+ accident_time = st.time_input("Accident Time")
639
+
640
+ # Weather conditions
641
+ weather = st.selectbox(
642
+ "Weather Conditions",
643
+ options=['clear', 'cloudy', 'rainy', 'foggy', 'sandstorm'],
644
+ format_func=lambda x: x.title()
645
+ )
646
+
647
+ # Road condition
648
+ road_condition = st.selectbox(
649
+ "Road Condition",
650
+ options=['dry', 'wet', 'sandy', 'oily'],
651
+ format_func=lambda x: x.title()
652
+ )
653
+
654
+ # Notes
655
+ notes = st.text_area(
656
+ "Notes (optional)",
657
+ value=st.session_state.accident_info.get('notes', ''),
658
+ placeholder="Any extra context about the accident...",
659
+ height=80
660
+ )
661
+
662
+ # Update session state
663
+ st.session_state.accident_info.update({
664
+ 'location': {
665
+ 'name': location_name,
666
+ 'latitude': lat,
667
+ 'longitude': lon,
668
+ 'radius_meters': 200
669
+ },
670
+ 'road_type': road_type,
671
+ 'datetime': f"{accident_date} {accident_time}",
672
+ 'weather': weather,
673
+ 'road_condition': road_condition,
674
+ 'notes': notes
675
+ })
676
+
677
+ # Navigation buttons
678
+ col1, col2, col3 = st.columns([1, 2, 1])
679
+ with col3:
680
+ if st.button("Next: Vehicle 1 →", type="primary"):
681
+ st.session_state.current_step = 2
682
+ st.rerun()
683
+
684
+
685
+ # ============================================================
686
+ # STEP 2: VEHICLE 1 INPUT
687
+ # ============================================================
688
+
689
+ def render_step_2_vehicle1():
690
+ """Render Vehicle 1 input step."""
691
+
692
+ st.header("🚙 Step 2: First Vehicle Information")
693
+
694
  st.markdown("""
695
+ <div class="info-card" style="border-color: #FF4B4B;">
696
+ <h4>Vehicle 1 (Red)</h4>
697
+ <p>Enter the details of the first vehicle involved in the accident.</p>
698
+ </div>
699
+ """, unsafe_allow_html=True)
700
+
701
+ col1, col2 = st.columns([2, 1])
702
+
703
+ with col1:
704
+ # Map with path drawing
705
+ render_map_section(vehicle_id=1)
706
+
707
+ with col2:
708
+ render_vehicle_input(1)
709
+
710
+ # Navigation buttons
711
+ col1, col2, col3 = st.columns([1, 2, 1])
712
+ with col1:
713
+ if st.button("← Back"):
714
+ st.session_state.current_step = 1
715
+ st.rerun()
716
+ with col3:
717
+ if st.button("Next: Vehicle 2 →", type="primary"):
718
+ st.session_state.current_step = 3
719
+ st.rerun()
720
+
721
 
722
+ # ============================================================
723
+ # STEP 3: VEHICLE 2 INPUT
724
+ # ============================================================
725
 
726
+ def render_step_3_vehicle2():
727
+ """Render Vehicle 2 input step."""
728
+
729
+ st.header("🚗 Step 3: Second Vehicle Information")
730
+
731
+ st.markdown("""
732
+ <div class="info-card" style="border-color: #4B7BFF;">
733
+ <h4>Vehicle 2 (Blue)</h4>
734
+ <p>Enter the details of the second vehicle involved in the accident.</p>
735
+ </div>
736
+ """, unsafe_allow_html=True)
737
+
738
+ col1, col2 = st.columns([2, 1])
739
 
 
740
  with col1:
741
+ # Map with path drawing (showing both vehicles)
742
+ render_map_section(vehicle_id=2)
 
743
 
744
  with col2:
745
+ render_vehicle_input(2)
 
 
746
 
747
+ # Navigation buttons
748
+ col1, col2, col3 = st.columns([1, 2, 1])
749
+ with col1:
750
+ if st.button("← Back"):
751
+ st.session_state.current_step = 2
752
+ st.rerun()
753
+ with col3:
754
+ if st.button("Analyze Accident →", type="primary"):
755
+ st.session_state.current_step = 4
756
+ st.rerun()
757
 
758
+
759
+ # ============================================================
760
+ # STEP 4: PARTIES INFORMATION
761
+ # ============================================================
762
+
763
+ def render_step_4_parties():
764
+ """Render the parties (driver details) input step."""
765
+
766
+ st.header("👥 Step 4: Parties Information")
767
 
768
+ st.markdown("""
769
+ <div class="info-card">
770
+ <h4>Driver & Vehicle Details</h4>
771
+ <p>Enter information about the parties involved in the accident. This information will be included in the official report.</p>
772
+ </div>
773
+ """, unsafe_allow_html=True)
774
+
775
+ # Two columns for both parties
776
  col1, col2 = st.columns(2)
777
 
778
  with col1:
779
+ render_party_input(1)
 
 
 
 
780
 
781
  with col2:
782
+ render_party_input(2)
 
 
 
 
783
 
784
+ # Navigation buttons
785
+ st.markdown("---")
786
+ col1, col2, col3 = st.columns([1, 2, 1])
787
+ with col1:
788
+ if st.button("← Back to Vehicle 2"):
789
+ st.session_state.current_step = 3
790
+ st.rerun()
791
+ with col3:
792
+ if st.button("Next: Evidence →", type="primary"):
793
+ st.session_state.current_step = 5
794
+ st.rerun()
795
+
796
+
797
+ # ============================================================
798
+ # STEP 5: EVIDENCE UPLOAD
799
+ # ============================================================
800
 
801
+ def render_step_5_evidence():
802
+ """Render the evidence upload step."""
803
 
804
+ st.header("📷 Step 5: Evidence (Optional)")
805
+
806
+ st.markdown("""
807
+ <div class="info-card">
808
+ <h4>Upload Evidence Photos</h4>
809
+ <p>Upload photos of the accident scene, vehicle damage, or any other relevant evidence. This is optional but recommended.</p>
810
+ </div>
811
+ """, unsafe_allow_html=True)
812
+
813
+ # Evidence upload
814
+ render_evidence_upload()
815
+
816
+ # Quick summary of data entered so far
817
+ st.markdown("---")
818
+ st.subheader("📋 Data Summary")
819
+
820
+ col1, col2, col3 = st.columns(3)
821
+
822
+ with col1:
823
+ st.markdown("**📍 Location**")
824
+ st.write(f"{st.session_state.accident_info['location'].get('name', 'Not set')}")
825
+ st.write(f"Type: {st.session_state.accident_info.get('road_type', 'N/A').replace('_', ' ').title()}")
826
+
827
+ with col2:
828
+ st.markdown("**🚙 Vehicle 1**")
829
+ # Defensive check - ensure vehicle_1 is a dict
830
+ v1 = st.session_state.vehicle_1
831
+ if isinstance(v1, dict):
832
+ st.write(f"{v1.get('type', 'sedan').title()} @ {v1.get('speed', 50)} km/h")
833
+ else:
834
+ st.write("Vehicle 1 data")
835
+ party1_name = st.session_state.party_1.get('full_name', '') if isinstance(st.session_state.party_1, dict) else ''
836
+ if party1_name:
837
+ st.write(f"Driver: {party1_name}")
838
+
839
+ with col3:
840
+ st.markdown("**🚗 Vehicle 2**")
841
+ # Defensive check - ensure vehicle_2 is a dict
842
+ v2 = st.session_state.vehicle_2
843
+ if isinstance(v2, dict):
844
+ st.write(f"{v2.get('type', 'sedan').title()} @ {v2.get('speed', 50)} km/h")
845
+ else:
846
+ st.write("Vehicle 2 data")
847
+ party2_name = st.session_state.party_2.get('full_name', '') if isinstance(st.session_state.party_2, dict) else ''
848
+ if party2_name:
849
+ st.write(f"Driver: {party2_name}")
850
+
851
+ # Navigation buttons
852
+ st.markdown("---")
853
+ col1, col2, col3 = st.columns([1, 2, 1])
854
+ with col1:
855
+ if st.button("← Back to Parties"):
856
+ st.session_state.current_step = 4
857
+ st.rerun()
858
+ with col3:
859
+ if st.button("Run Analysis →", type="primary"):
860
+ st.session_state.current_step = 6
861
+ st.rerun()
862
+
863
+
864
+ # ============================================================
865
+ # STEP 6: AI ANALYSIS
866
+ # ============================================================
867
+
868
+ def render_step_6_analysis():
869
+ """Render the AI analysis step."""
870
+
871
+ st.header("🤖 Step 4: AI Analysis")
872
+
873
+ st.markdown("""
874
+ <div class="info-card">
875
+ <h4>MindSpore AI Analysis</h4>
876
+ <p>Review the accident data below and click "Run AI Analysis" to generate possible scenarios.</p>
877
+ </div>
878
+ """, unsafe_allow_html=True)
879
+
880
+ # Data validation
881
+ validation_passed = True
882
+ validation_messages = []
883
+
884
+ # Check paths
885
+ if not st.session_state.vehicle_1.get('path'):
886
+ validation_messages.append("⚠️ Vehicle 1 path not defined - will use direction-based estimation")
887
+ if not st.session_state.vehicle_2.get('path'):
888
+ validation_messages.append("⚠️ Vehicle 2 path not defined - will use direction-based estimation")
889
+
890
+ # Summary of inputs in cards
891
+ st.subheader("📋 Input Data Summary")
892
+
893
+ col1, col2, col3 = st.columns(3)
894
+
895
+ with col1:
896
+ st.markdown("""
897
+ <div style='background: rgba(59, 130, 246, 0.15); padding: 1rem; border-radius: 12px; border-left: 4px solid #3b82f6; box-shadow: 0 4px 12px rgba(0,0,0,0.2);'>
898
+ <h4 style='margin:0; color:#60a5fa; font-weight: 700;'>📍 Location</h4>
899
+ </div>
900
+ """, unsafe_allow_html=True)
901
+ st.write(f"**{st.session_state.accident_info['location'].get('name', 'Unknown')}**")
902
+ st.write(f"🛣️ Type: `{st.session_state.accident_info['road_type']}`")
903
+ st.write(f"🌤️ Weather: `{st.session_state.accident_info['weather']}`")
904
+ st.write(f"🛤️ Road: `{st.session_state.accident_info['road_condition']}`")
905
+ st.write(f"📅 {st.session_state.accident_info.get('datetime', 'Not specified')}")
906
+
907
+ with col2:
908
+ st.markdown("""
909
+ <div style='background: rgba(255, 75, 75, 0.15); padding: 1rem; border-radius: 12px; border-left: 4px solid #FF4B4B; box-shadow: 0 4px 12px rgba(0,0,0,0.2);'>
910
+ <h4 style='margin:0; color:#ff6b6b; font-weight: 700;'>🚙 Vehicle 1 (Red)</h4>
911
+ </div>
912
+ """, unsafe_allow_html=True)
913
+ v1 = st.session_state.vehicle_1 if isinstance(st.session_state.vehicle_1, dict) else {'type': 'sedan', 'speed': 50, 'direction': 'north', 'action': 'going_straight', 'path': []}
914
+ st.write(f"**Type:** {v1.get('type', 'sedan').title()}")
915
+ st.write(f"**Speed:** {v1.get('speed', 50)} km/h")
916
+ st.write(f"**Direction:** {v1.get('direction', 'north').title()}")
917
+ st.write(f"**Action:** {v1.get('action', 'going_straight').replace('_', ' ').title()}")
918
+ path_status = "✅ Defined" if v1.get('path') else "⚠️ Not set"
919
+ st.write(f"**Path:** {path_status}")
920
+
921
+ with col3:
922
+ st.markdown("""
923
+ <div style='background: rgba(75, 123, 255, 0.15); padding: 1rem; border-radius: 12px; border-left: 4px solid #4B7BFF; box-shadow: 0 4px 12px rgba(0,0,0,0.2);'>
924
+ <h4 style='margin:0; color:#6b9bff; font-weight: 700;'>🚗 Vehicle 2 (Blue)</h4>
925
+ </div>
926
+ """, unsafe_allow_html=True)
927
+ v2 = st.session_state.vehicle_2 if isinstance(st.session_state.vehicle_2, dict) else {'type': 'sedan', 'speed': 50, 'direction': 'east', 'action': 'going_straight', 'path': []}
928
+ st.write(f"**Type:** {v2.get('type', 'sedan').title()}")
929
+ st.write(f"**Speed:** {v2.get('speed', 50)} km/h")
930
+ st.write(f"**Direction:** {v2.get('direction', 'east').title()}")
931
+ st.write(f"**Action:** {v2.get('action', 'going_straight').replace('_', ' ').title()}")
932
+ path_status = "✅ Defined" if v2.get('path') else "⚠️ Not set"
933
+ st.write(f"**Path:** {path_status}")
934
+
935
+ # Show validation messages
936
+ if validation_messages:
937
+ st.markdown("---")
938
+ for msg in validation_messages:
939
+ st.warning(msg)
940
+
941
+ st.markdown("---")
942
+
943
+ # Analysis section
944
+ st.subheader("🚀 Run Analysis")
945
+
946
+ col1, col2, col3 = st.columns([1, 2, 1])
947
+
948
+ with col2:
949
+ if st.button("🧠 Run MindSpore AI Analysis", type="primary", use_container_width=True):
950
+
951
+ # Progress bar
952
+ progress_bar = st.progress(0)
953
+ status_text = st.empty()
954
+
955
  try:
956
+ # Step 1: Validate data
957
+ status_text.text("Step 1/4: Validating input data...")
958
+ progress_bar.progress(10)
959
+ import time
960
+ time.sleep(0.5)
 
 
 
 
961
 
962
+ # Step 2: Extract features
963
+ status_text.text("Step 2/4: Extracting features...")
964
+ progress_bar.progress(30)
965
+ time.sleep(0.5)
 
 
 
 
966
 
967
+ # Step 3: Run AI model
968
+ status_text.text("Step 3/4: Running MindSpore AI model...")
969
+ progress_bar.progress(50)
 
 
 
 
 
970
 
971
+ # Import and run analysis
972
  from analysis.scenario_analyzer import analyze_accident
973
 
974
  results = analyze_accident(
975
+ accident_info=st.session_state.accident_info,
976
+ vehicle_1=st.session_state.vehicle_1,
977
+ vehicle_2=st.session_state.vehicle_2
978
  )
979
 
980
+ progress_bar.progress(80)
981
+ time.sleep(0.3)
982
+
983
+ # Step 4: Generate scenarios
984
+ status_text.text("Step 4/4: Generating scenarios...")
985
+ progress_bar.progress(100)
986
+
987
+ # Store results
988
+ st.session_state.analysis_results = results
989
+ st.session_state.scenarios = results.get('scenarios', [])
990
+ st.session_state.analysis_ready = True
991
+
992
+ status_text.empty()
993
+ progress_bar.empty()
994
+
995
+ # Success message
996
+ st.success(f"""
997
+ ✅ **Analysis Complete!**
998
+
999
+ - Generated **{len(st.session_state.scenarios)}** possible scenarios
1000
+ - Most likely: **{results.get('most_likely_scenario', {}).get('type', 'Unknown').replace('_', ' ').title()}**
1001
+ - Confidence: **{results.get('overall_collision_probability', 0)*100:.1f}%**
1002
+ """)
1003
 
1004
  except Exception as e:
1005
+ st.error(f"Analysis failed: {str(e)}")
1006
+ progress_bar.empty()
1007
+ status_text.empty()
 
1008
 
1009
+ # Show results preview if available
1010
+ if st.session_state.analysis_results:
1011
+ st.markdown("---")
1012
+ st.subheader("📊 Quick Results Preview")
1013
+
1014
+ results = st.session_state.analysis_results
1015
+ scenarios = st.session_state.scenarios
1016
+
1017
+ # Quick metrics
1018
+ metric_cols = st.columns(4)
1019
+
1020
+ with metric_cols[0]:
1021
+ st.metric(
1022
+ "Scenarios Generated",
1023
+ len(scenarios)
1024
+ )
1025
 
1026
+ with metric_cols[1]:
1027
+ most_likely = results.get('most_likely_scenario', {})
1028
+ st.metric(
1029
+ "Most Likely",
1030
+ f"#{most_likely.get('id', 1)}"
1031
+ )
1032
 
1033
+ with metric_cols[2]:
1034
+ prob = results.get('overall_collision_probability', 0) * 100
1035
+ st.metric(
1036
+ "Collision Certainty",
1037
+ f"{prob:.1f}%"
1038
+ )
1039
 
1040
+ with metric_cols[3]:
1041
+ fault = results.get('preliminary_fault_assessment', {})
1042
+ st.metric(
1043
+ "Primary Factor",
1044
+ fault.get('primary_factor', 'Unknown').replace('_', ' ').title()[:15]
1045
+ )
1046
 
1047
+ # Top scenarios
1048
+ st.write("**Top Scenarios:**")
1049
  for i, scenario in enumerate(scenarios[:3], 1):
1050
+ prob_pct = scenario['probability'] * 100
1051
+ st.write(f"{i}. **{scenario['accident_type'].replace('_', ' ').title()}** - {prob_pct:.1f}%")
1052
+
1053
+ # Navigation
1054
+ st.markdown("---")
1055
+ col1, col2, col3 = st.columns([1, 2, 1])
1056
+ with col1:
1057
+ if st.button(" Back to Evidence"):
1058
+ st.session_state.current_step = 5
1059
+ st.rerun()
1060
+
1061
+ with col3:
1062
+ if st.session_state.analysis_results:
1063
+ if st.button("View Full Results →", type="primary"):
1064
+ st.session_state.current_step = 7
1065
+ st.rerun()
1066
+
1067
+
1068
+ # ============================================================
1069
+ # STEP 7: RESULTS
1070
+ # ============================================================
1071
+
1072
+ def render_step_7_results():
1073
+ """Render the results step."""
1074
+
1075
+ st.header("📊 Step 7: Analysis Results")
1076
+
1077
+ if not st.session_state.analysis_results:
1078
+ st.warning("No analysis results available. Please run the analysis first.")
1079
+ if st.button("← Go to Analysis"):
1080
+ st.session_state.current_step = 6
1081
+ st.rerun()
1082
+ return
1083
+
1084
+ # Render full results
1085
+ render_results()
1086
+
1087
+ # Navigation
1088
+ col1, col2, col3 = st.columns([1, 2, 1])
1089
+ with col1:
1090
+ if st.button("← Back to Analysis"):
1091
+ st.session_state.current_step = 6
1092
+ st.rerun()
1093
+
1094
+ with col3:
1095
+ if st.button("🔄 Start New Analysis"):
1096
+ # Reset session state
1097
+ for key in list(st.session_state.keys()):
1098
+ del st.session_state[key]
1099
+ st.rerun()
1100
+
1101
+
1102
+ # ============================================================
1103
+ # RUN APPLICATION
1104
+ # ============================================================
1105
+
1106
+ if __name__ == "__main__":
1107
+ main()
app_FIXED.py DELETED
@@ -1,169 +0,0 @@
1
- """
2
- CrashLens - Fixed Version (No Rerun Loops)
3
- """
4
- import streamlit as st
5
- import sys
6
- from pathlib import Path
7
-
8
- sys.path.insert(0, str(Path(__file__).parent))
9
-
10
- st.set_page_config(
11
- page_title="CrashLens AI",
12
- page_icon="🚗",
13
- layout="wide"
14
- )
15
-
16
- # Initialize session state
17
- if 'analysis_done' not in st.session_state:
18
- st.session_state.analysis_done = False
19
- if 'results' not in st.session_state:
20
- st.session_state.results = None
21
-
22
- # Header
23
- st.title("🚗 CrashLens - AI Traffic Accident Analyzer")
24
- st.markdown("**Huawei AI Innovation Challenge 2026** | Jubail Industrial College")
25
- st.markdown("---")
26
-
27
- # Sidebar
28
- with st.sidebar:
29
- st.image("assets/mindspore_logo.png", width=200)
30
- st.markdown("### 🧠 Powered by MindSpore AI")
31
- st.markdown("---")
32
- st.markdown("### 📋 How to Use")
33
- st.markdown("""
34
- 1. Enter accident location
35
- 2. Add vehicle details
36
- 3. Click Analyze
37
- 4. View AI predictions
38
- """)
39
-
40
- # Main content - Use tabs instead of steps
41
- tab1, tab2, tab3 = st.tabs(["📍 Location", "🚗 Vehicles", "🔬 Analysis"])
42
-
43
- with tab1:
44
- st.subheader("Accident Location")
45
-
46
- col1, col2 = st.columns(2)
47
- with col1:
48
- location_name = st.text_input("Location Name", "Seef District Roundabout")
49
- latitude = st.number_input("Latitude", value=26.2397, format="%.4f")
50
- longitude = st.number_input("Longitude", value=50.5369, format="%.4f")
51
-
52
- with col2:
53
- weather = st.selectbox("Weather", ["clear", "cloudy", "rainy", "foggy"])
54
- road_condition = st.selectbox("Road Condition", ["dry", "wet", "sandy", "oily"])
55
- road_type = st.selectbox("Road Type", ["roundabout", "crossroad", "highway", "urban_road"])
56
-
57
- st.success("✅ Location configured")
58
-
59
- with tab2:
60
- st.subheader("Vehicle Information")
61
-
62
- col1, col2 = st.columns(2)
63
-
64
- with col1:
65
- st.markdown("**🚗 Vehicle 1 (Red)**")
66
- v1_type = st.selectbox("Type", ["sedan", "suv", "truck"], key="v1_type")
67
- v1_speed = st.slider("Speed (km/h)", 0, 120, 50, key="v1_speed")
68
- v1_direction = st.selectbox("Direction", ["north", "south", "east", "west"], key="v1_dir")
69
- v1_action = st.selectbox("Action", ["going_straight", "turning_left", "turning_right"], key="v1_action")
70
-
71
- with col2:
72
- st.markdown("**🚙 Vehicle 2 (Blue)**")
73
- v2_type = st.selectbox("Type", ["sedan", "suv", "truck"], key="v2_type")
74
- v2_speed = st.slider("Speed (km/h)", 0, 120, 50, key="v2_speed")
75
- v2_direction = st.selectbox("Direction", ["north", "south", "east", "west"], key="v2_dir")
76
- v2_action = st.selectbox("Action", ["going_straight", "turning_left", "turning_right"], key="v2_action")
77
-
78
- st.success("✅ Vehicles configured")
79
-
80
- with tab3:
81
- st.subheader("AI Analysis")
82
-
83
- if st.button("🧠 Run AI Analysis", type="primary", use_container_width=True):
84
-
85
- with st.spinner("Analyzing accident scenario..."):
86
- try:
87
- # Prepare data
88
- accident_info = {
89
- 'location': {'name': location_name, 'lat': latitude, 'lng': longitude},
90
- 'weather': weather,
91
- 'road_condition': road_condition,
92
- 'road_type': road_type,
93
- 'visibility': 1.0,
94
- 'lighting': 'daylight'
95
- }
96
-
97
- vehicle_1 = {
98
- 'type': v1_type,
99
- 'speed': v1_speed,
100
- 'direction': v1_direction,
101
- 'action': v1_action,
102
- 'braking': False,
103
- 'signaling': False
104
- }
105
-
106
- vehicle_2 = {
107
- 'type': v2_type,
108
- 'speed': v2_speed,
109
- 'direction': v2_direction,
110
- 'action': v2_action,
111
- 'braking': False,
112
- 'signaling': False
113
- }
114
-
115
- # Run analysis
116
- from analysis.scenario_analyzer import analyze_accident
117
-
118
- results = analyze_accident(
119
- accident_info=accident_info,
120
- vehicle_1=vehicle_1,
121
- vehicle_2=vehicle_2
122
- )
123
-
124
- st.session_state.results = results
125
- st.session_state.analysis_done = True
126
-
127
- except Exception as e:
128
- st.error(f"Analysis error: {e}")
129
- import traceback
130
- with st.expander("Error Details"):
131
- st.code(traceback.format_exc())
132
-
133
- # Display results
134
- if st.session_state.analysis_done and st.session_state.results:
135
- st.success("✅ Analysis Complete!")
136
-
137
- results = st.session_state.results
138
-
139
- # Show collision type
140
- st.markdown("### 🎯 Predicted Collision Type")
141
- collision_type = results.get('collision_type', 'Unknown')
142
- st.info(f"**{collision_type.replace('_', ' ').title()}**")
143
-
144
- # Show scenarios
145
- st.markdown("### 📊 Scenarios")
146
- scenarios = results.get('scenarios', [])
147
-
148
- for i, scenario in enumerate(scenarios[:3], 1):
149
- with st.expander(f"Scenario {i}: {scenario.get('type', 'Unknown').replace('_', ' ').title()}"):
150
- col1, col2 = st.columns(2)
151
-
152
- with col1:
153
- st.metric("Probability", f"{scenario.get('probability', 0)*100:.1f}%")
154
- st.write("**Fault Distribution:**")
155
- st.write(f"- Vehicle 1: {scenario.get('fault_percentage_1', 50)}%")
156
- st.write(f"- Vehicle 2: {scenario.get('fault_percentage_2', 50)}%")
157
-
158
- with col2:
159
- # Show animation if exists
160
- gif_path = f"output/visualizations/{scenario.get('type', 'head_on_collision')}.gif"
161
- if Path(gif_path).exists():
162
- st.image(gif_path, caption="Simulation")
163
-
164
- # Download report
165
- if st.button("📄 Download Report"):
166
- st.info("Report generation coming soon!")
167
-
168
- st.markdown("---")
169
- st.markdown("© 2026 Jubail Industrial College | Huawei AI Innovation Challenge")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
models/__init__.py CHANGED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Models Package
3
+ ==============
4
+ MindSpore neural network models for accident prediction.
5
+ """
6
+
7
+ from .mindspore_model import (
8
+ AccidentModelTrainer,
9
+ NumpyAccidentModel,
10
+ get_accident_type_name,
11
+ prepare_features,
12
+ MINDSPORE_AVAILABLE
13
+ )
14
+
15
+ __all__ = [
16
+ 'AccidentModelTrainer',
17
+ 'NumpyAccidentModel',
18
+ 'get_accident_type_name',
19
+ 'prepare_features',
20
+ 'MINDSPORE_AVAILABLE'
21
+ ]
models/mindspore_loader.py ADDED
@@ -0,0 +1,467 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ MindSpore Model Loader
3
+ ======================
4
+ Loads the trained MindSpore model for accident prediction.
5
+ Supports both MindSpore (.ckpt) and NumPy fallback (.npz).
6
+ """
7
+
8
+ import numpy as np
9
+ import json
10
+ from pathlib import Path
11
+ from typing import Dict, Tuple, Optional
12
+
13
+ # Try to import MindSpore
14
+ MINDSPORE_AVAILABLE = False
15
+ try:
16
+ import mindspore as ms
17
+ import mindspore.nn as nn
18
+ from mindspore import Tensor, context, load_checkpoint, load_param_into_net
19
+ import mindspore.ops as ops
20
+ MINDSPORE_AVAILABLE = True
21
+ print(f"✅ MindSpore {ms.__version__} available")
22
+ except ImportError:
23
+ print("⚠️ MindSpore not available, using NumPy fallback")
24
+
25
+
26
+ # ============================================================
27
+ # ENCODING MAPS (must match training)
28
+ # ============================================================
29
+
30
+ DIRECTION_MAP = {
31
+ 'north': 0, 'northeast': 1, 'east': 2, 'southeast': 3,
32
+ 'south': 4, 'southwest': 5, 'west': 6, 'northwest': 7
33
+ }
34
+
35
+ ACTION_MAP = {
36
+ 'going_straight': 0, 'turning_left': 1, 'turning_right': 2,
37
+ 'entering_roundabout': 3, 'exiting_roundabout': 4,
38
+ 'changing_lane_left': 5, 'changing_lane_right': 6,
39
+ 'slowing_down': 7, 'accelerating': 8, 'stopped': 9
40
+ }
41
+
42
+ VEHICLE_MAP = {'sedan': 0, 'suv': 1, 'truck': 2, 'motorcycle': 3, 'bus': 4}
43
+ WEATHER_MAP = {'clear': 0, 'cloudy': 1, 'rainy': 2, 'foggy': 3, 'sandstorm': 4}
44
+ ROAD_COND_MAP = {'dry': 0, 'wet': 1, 'sandy': 2, 'oily': 3}
45
+ ROAD_TYPE_MAP = {
46
+ 'roundabout': 0, 'crossroad': 1, 't_junction': 2, 'highway_merge': 3,
47
+ 'parking': 4, 'highway': 5, 'urban_road': 6, 'other': 7
48
+ }
49
+ LIGHTING_MAP = {'daylight': 0, 'dusk': 1, 'dawn': 2, 'night_lit': 3, 'night_dark': 4}
50
+
51
+ ACCIDENT_MAP = {
52
+ 'rear_end_collision': 0,
53
+ 'side_impact': 1,
54
+ 'head_on_collision': 2,
55
+ 'sideswipe': 3,
56
+ 'roundabout_entry_collision': 4,
57
+ 'lane_change_collision': 5,
58
+ 'intersection_collision': 6
59
+ }
60
+
61
+ ACCIDENT_NAMES = {v: k for k, v in ACCIDENT_MAP.items()}
62
+
63
+
64
+ # ============================================================
65
+ # MINDSPORE MODEL DEFINITION
66
+ # ============================================================
67
+
68
+ if MINDSPORE_AVAILABLE:
69
+ class AccidentClassifier(nn.Cell):
70
+ """
71
+ MindSpore Neural Network for Traffic Accident Classification.
72
+ Architecture: 31 → 128 → 64 → 32 → 7
73
+ """
74
+
75
+ def __init__(self, input_dim=31, num_classes=7):
76
+ super(AccidentClassifier, self).__init__()
77
+
78
+ # Layer 1: Input → 128
79
+ self.fc1 = nn.Dense(input_dim, 128)
80
+ self.bn1 = nn.BatchNorm1d(128)
81
+ self.relu1 = nn.ReLU()
82
+ self.dropout1 = nn.Dropout(p=0.3)
83
+
84
+ # Layer 2: 128 → 64
85
+ self.fc2 = nn.Dense(128, 64)
86
+ self.bn2 = nn.BatchNorm1d(64)
87
+ self.relu2 = nn.ReLU()
88
+ self.dropout2 = nn.Dropout(p=0.3)
89
+
90
+ # Layer 3: 64 → 32
91
+ self.fc3 = nn.Dense(64, 32)
92
+ self.bn3 = nn.BatchNorm1d(32)
93
+ self.relu3 = nn.ReLU()
94
+ self.dropout3 = nn.Dropout(p=0.2)
95
+
96
+ # Output: 32 → 7
97
+ self.fc4 = nn.Dense(32, num_classes)
98
+
99
+ def construct(self, x):
100
+ x = self.fc1(x)
101
+ x = self.bn1(x)
102
+ x = self.relu1(x)
103
+ x = self.dropout1(x)
104
+
105
+ x = self.fc2(x)
106
+ x = self.bn2(x)
107
+ x = self.relu2(x)
108
+ x = self.dropout2(x)
109
+
110
+ x = self.fc3(x)
111
+ x = self.bn3(x)
112
+ x = self.relu3(x)
113
+ x = self.dropout3(x)
114
+
115
+ x = self.fc4(x)
116
+ return x
117
+
118
+
119
+ # ============================================================
120
+ # NUMPY FALLBACK MODEL
121
+ # ============================================================
122
+
123
+ class NumpyAccidentModel:
124
+ """NumPy fallback when MindSpore is not available."""
125
+
126
+ def __init__(self, input_dim=31, num_classes=7):
127
+ self.input_dim = input_dim
128
+ self.num_classes = num_classes
129
+ self.trained = False
130
+
131
+ # Initialize weights
132
+ np.random.seed(42)
133
+ self.W1 = np.random.randn(input_dim, 128) * np.sqrt(2.0 / input_dim)
134
+ self.b1 = np.zeros(128)
135
+ self.W2 = np.random.randn(128, 64) * np.sqrt(2.0 / 128)
136
+ self.b2 = np.zeros(64)
137
+ self.W3 = np.random.randn(64, 32) * np.sqrt(2.0 / 64)
138
+ self.b3 = np.zeros(32)
139
+ self.W4 = np.random.randn(32, num_classes) * np.sqrt(2.0 / 32)
140
+ self.b4 = np.zeros(num_classes)
141
+
142
+ def relu(self, x):
143
+ return np.maximum(0, x)
144
+
145
+ def softmax(self, x):
146
+ exp_x = np.exp(x - np.max(x, axis=-1, keepdims=True))
147
+ return exp_x / np.sum(exp_x, axis=-1, keepdims=True)
148
+
149
+ def forward(self, x):
150
+ x = self.relu(x @ self.W1 + self.b1)
151
+ x = self.relu(x @ self.W2 + self.b2)
152
+ x = self.relu(x @ self.W3 + self.b3)
153
+ x = x @ self.W4 + self.b4
154
+ return self.softmax(x)
155
+
156
+ def predict(self, x):
157
+ if x.ndim == 1:
158
+ x = x.reshape(1, -1)
159
+ probs = self.forward(x)
160
+ classes = np.argmax(probs, axis=1)
161
+ return classes, probs
162
+
163
+ def load(self, filepath):
164
+ data = np.load(filepath)
165
+ self.W1 = data['W1']
166
+ self.b1 = data['b1']
167
+ self.W2 = data['W2']
168
+ self.b2 = data['b2']
169
+ self.W3 = data['W3']
170
+ self.b3 = data['b3']
171
+ self.W4 = data.get('W4', data.get('weight_4', np.random.randn(32, 7) * 0.1))
172
+ self.b4 = data.get('b4', data.get('bias_4', np.zeros(7)))
173
+ self.trained = True
174
+ print(f"✅ NumPy model loaded from {filepath}")
175
+
176
+
177
+ # ============================================================
178
+ # FEATURE EXTRACTION
179
+ # ============================================================
180
+
181
+ def extract_features(accident_info: Dict, vehicle_1: Dict, vehicle_2: Dict) -> np.ndarray:
182
+ """
183
+ Extract 31 features from accident data.
184
+ Must match the training feature extraction exactly!
185
+ """
186
+
187
+ # Vehicle 1 features (7)
188
+ v1_type = VEHICLE_MAP.get(vehicle_1.get('type', 'sedan'), 0) / 5
189
+ v1_speed = vehicle_1.get('speed', 50) / 200
190
+ v1_dir = DIRECTION_MAP.get(vehicle_1.get('direction', 'north'), 0) / 8
191
+ v1_angle = v1_dir * 45 / 360
192
+ v1_action = ACTION_MAP.get(vehicle_1.get('action', 'going_straight'), 0) / 10
193
+ v1_braking = 1.0 if vehicle_1.get('braking', False) else 0.0
194
+ v1_signaling = 1.0 if vehicle_1.get('signaling', False) else 0.0
195
+
196
+ # Vehicle 2 features (7)
197
+ v2_type = VEHICLE_MAP.get(vehicle_2.get('type', 'sedan'), 0) / 5
198
+ v2_speed = vehicle_2.get('speed', 50) / 200
199
+ v2_dir = DIRECTION_MAP.get(vehicle_2.get('direction', 'east'), 2) / 8
200
+ v2_angle = v2_dir * 45 / 360
201
+ v2_action = ACTION_MAP.get(vehicle_2.get('action', 'going_straight'), 0) / 10
202
+ v2_braking = 1.0 if vehicle_2.get('braking', False) else 0.0
203
+ v2_signaling = 1.0 if vehicle_2.get('signaling', False) else 0.0
204
+
205
+ # Environment features (5)
206
+ weather = WEATHER_MAP.get(accident_info.get('weather', 'clear'), 0) / 5
207
+ road_cond = ROAD_COND_MAP.get(accident_info.get('road_condition', 'dry'), 0) / 4
208
+ visibility = accident_info.get('visibility', 1.0)
209
+ lighting = LIGHTING_MAP.get(accident_info.get('lighting', 'daylight'), 0) / 5
210
+ road_type = ROAD_TYPE_MAP.get(accident_info.get('road_type', 'roundabout'), 0) / 8
211
+
212
+ # Derived features (12)
213
+ angle1 = DIRECTION_MAP.get(vehicle_1.get('direction', 'north'), 0) * 45
214
+ angle2 = DIRECTION_MAP.get(vehicle_2.get('direction', 'east'), 2) * 45
215
+ collision_angle = abs(angle1 - angle2)
216
+ if collision_angle > 180:
217
+ collision_angle = 360 - collision_angle
218
+ collision_angle_norm = collision_angle / 180
219
+
220
+ speed1 = vehicle_1.get('speed', 50)
221
+ speed2 = vehicle_2.get('speed', 50)
222
+ speed_diff = abs(speed1 - speed2) / 200
223
+ combined_speed = (speed1 + speed2) / 400
224
+ same_direction = 1.0 if vehicle_1.get('direction') == vehicle_2.get('direction') else 0.0
225
+ speed_product = (speed1 * speed2) / 40000
226
+
227
+ weather_risk = [0.1, 0.2, 0.5, 0.7, 0.8][WEATHER_MAP.get(accident_info.get('weather', 'clear'), 0)]
228
+ road_risk = [0.1, 0.5, 0.6, 0.8][ROAD_COND_MAP.get(accident_info.get('road_condition', 'dry'), 0)]
229
+ base_risk = (weather_risk + road_risk) / 2
230
+
231
+ action_risk = {
232
+ 'going_straight': 0.3, 'turning_left': 0.5, 'turning_right': 0.4,
233
+ 'entering_roundabout': 0.6, 'exiting_roundabout': 0.5,
234
+ 'changing_lane_left': 0.7, 'changing_lane_right': 0.7,
235
+ 'slowing_down': 0.4, 'accelerating': 0.6, 'stopped': 0.2
236
+ }
237
+ v1_action_risk = action_risk.get(vehicle_1.get('action', 'going_straight'), 0.5)
238
+ v2_action_risk = action_risk.get(vehicle_2.get('action', 'going_straight'), 0.5)
239
+
240
+ relative_speed = (speed1 + speed2) / 400 if collision_angle > 90 else abs(speed1 - speed2) / 200
241
+ approach_rate = min(relative_speed * (1 + base_risk), 1.0)
242
+ time_factor = 0.5 # Default noon
243
+
244
+ # Build feature vector (31 features)
245
+ features = np.array([
246
+ # Vehicle 1 (7)
247
+ v1_type, v1_speed, v1_dir, v1_angle, v1_action, v1_braking, v1_signaling,
248
+ # Vehicle 2 (7)
249
+ v2_type, v2_speed, v2_dir, v2_angle, v2_action, v2_braking, v2_signaling,
250
+ # Environment (5)
251
+ weather, road_cond, visibility, lighting, road_type,
252
+ # Derived (12)
253
+ collision_angle_norm, speed_diff, combined_speed, same_direction,
254
+ speed_product, collision_angle_norm, time_factor, base_risk,
255
+ v1_action_risk, v2_action_risk, relative_speed, approach_rate
256
+ ], dtype=np.float32)
257
+
258
+ return features
259
+
260
+
261
+ # ============================================================
262
+ # MODEL MANAGER CLASS
263
+ # ============================================================
264
+
265
+ class AccidentModelManager:
266
+ """
267
+ Manages loading and inference for accident prediction model.
268
+ Automatically uses MindSpore if available, otherwise NumPy fallback.
269
+ """
270
+
271
+ def __init__(self, model_dir: str = None):
272
+ self.model_dir = Path(model_dir) if model_dir else Path(__file__).parent / "trained"
273
+ self.model = None
274
+ self.metadata = None
275
+ self.backend = None
276
+ self._loaded = False
277
+
278
+ def load(self, ckpt_path: str = None, npz_path: str = None):
279
+ """Load the model from checkpoint."""
280
+
281
+ # Try MindSpore first
282
+ if MINDSPORE_AVAILABLE and ckpt_path:
283
+ ckpt_file = Path(ckpt_path)
284
+ if ckpt_file.exists():
285
+ try:
286
+ context.set_context(mode=context.GRAPH_MODE, device_target="CPU")
287
+ self.model = AccidentClassifier(input_dim=31, num_classes=7)
288
+ param_dict = load_checkpoint(str(ckpt_file))
289
+ load_param_into_net(self.model, param_dict)
290
+ self.model.set_train(False)
291
+ self.backend = "MindSpore"
292
+ self._loaded = True
293
+ print(f"✅ MindSpore model loaded from {ckpt_file}")
294
+ return True
295
+ except Exception as e:
296
+ print(f"⚠️ Failed to load MindSpore model: {e}")
297
+
298
+ # Fallback to NumPy
299
+ if npz_path:
300
+ npz_file = Path(npz_path)
301
+ if npz_file.exists():
302
+ try:
303
+ self.model = NumpyAccidentModel(input_dim=31, num_classes=7)
304
+ self.model.load(str(npz_file))
305
+ self.backend = "NumPy"
306
+ self._loaded = True
307
+ return True
308
+ except Exception as e:
309
+ print(f"⚠️ Failed to load NumPy model: {e}")
310
+
311
+ # Try default paths
312
+ default_ckpt = self.model_dir / "best_accident_model.ckpt"
313
+ default_npz = self.model_dir / "accident_model.npz"
314
+
315
+ if MINDSPORE_AVAILABLE and default_ckpt.exists():
316
+ return self.load(ckpt_path=str(default_ckpt))
317
+ elif default_npz.exists():
318
+ return self.load(npz_path=str(default_npz))
319
+
320
+ print("⚠️ No model found. Using untrained model.")
321
+ self.model = NumpyAccidentModel(input_dim=31, num_classes=7)
322
+ self.backend = "NumPy (untrained)"
323
+ self._loaded = True
324
+ return False
325
+
326
+ def load_metadata(self, metadata_path: str = None):
327
+ """Load model metadata."""
328
+ if metadata_path:
329
+ meta_file = Path(metadata_path)
330
+ else:
331
+ meta_file = self.model_dir / "model_metadata.json"
332
+
333
+ if meta_file.exists():
334
+ with open(meta_file, 'r') as f:
335
+ self.metadata = json.load(f)
336
+ print(f"✅ Metadata loaded from {meta_file}")
337
+ return self.metadata
338
+ return None
339
+
340
+ def predict(self, accident_info: Dict, vehicle_1: Dict, vehicle_2: Dict) -> Dict:
341
+ """
342
+ Predict accident type from input data.
343
+
344
+ Returns:
345
+ Dict with predicted_class, class_name, probabilities, confidence
346
+ """
347
+ if not self._loaded:
348
+ self.load()
349
+
350
+ # Extract features
351
+ features = extract_features(accident_info, vehicle_1, vehicle_2)
352
+
353
+ # Get prediction
354
+ if self.backend == "MindSpore" and MINDSPORE_AVAILABLE:
355
+ self.model.set_train(False)
356
+ x = Tensor(features.reshape(1, -1), ms.float32)
357
+ logits = self.model(x)
358
+ softmax = ops.Softmax(axis=1)
359
+ probs = softmax(logits)[0].asnumpy()
360
+ pred_class = int(np.argmax(probs))
361
+ else:
362
+ pred_class, probs = self.model.predict(features)
363
+ pred_class = int(pred_class[0]) if isinstance(pred_class, np.ndarray) else int(pred_class)
364
+ probs = probs[0] if probs.ndim > 1 else probs
365
+
366
+ # Build result
367
+ result = {
368
+ 'predicted_class': pred_class,
369
+ 'class_name': ACCIDENT_NAMES[pred_class],
370
+ 'confidence': float(probs[pred_class]),
371
+ 'probabilities': {
372
+ ACCIDENT_NAMES[i]: float(probs[i])
373
+ for i in range(len(probs))
374
+ },
375
+ 'backend': self.backend
376
+ }
377
+
378
+ return result
379
+
380
+ def get_all_predictions(self, accident_info: Dict, vehicle_1: Dict, vehicle_2: Dict) -> list:
381
+ """Get all accident types sorted by probability."""
382
+ result = self.predict(accident_info, vehicle_1, vehicle_2)
383
+
384
+ sorted_predictions = sorted(
385
+ result['probabilities'].items(),
386
+ key=lambda x: x[1],
387
+ reverse=True
388
+ )
389
+
390
+ return [
391
+ {'type': name, 'probability': prob}
392
+ for name, prob in sorted_predictions
393
+ ]
394
+
395
+
396
+ # ============================================================
397
+ # GLOBAL MODEL INSTANCE
398
+ # ============================================================
399
+
400
+ _model_manager = None
401
+
402
+ def get_model_manager(model_dir: str = None) -> AccidentModelManager:
403
+ """Get or create global model manager instance."""
404
+ global _model_manager
405
+ if _model_manager is None:
406
+ _model_manager = AccidentModelManager(model_dir)
407
+ return _model_manager
408
+
409
+
410
+ def predict_accident(accident_info: Dict, vehicle_1: Dict, vehicle_2: Dict) -> Dict:
411
+ """Convenience function for prediction."""
412
+ manager = get_model_manager()
413
+ if not manager._loaded:
414
+ manager.load()
415
+ return manager.predict(accident_info, vehicle_1, vehicle_2)
416
+
417
+
418
+ # ============================================================
419
+ # TEST
420
+ # ============================================================
421
+
422
+ if __name__ == "__main__":
423
+ print("\n" + "="*60)
424
+ print("🧪 Testing MindSpore Model Loader")
425
+ print("="*60)
426
+
427
+ # Create manager
428
+ manager = AccidentModelManager()
429
+ manager.load()
430
+
431
+ # Test prediction
432
+ test_accident = {
433
+ 'weather': 'clear',
434
+ 'road_condition': 'dry',
435
+ 'visibility': 1.0,
436
+ 'lighting': 'daylight',
437
+ 'road_type': 'roundabout'
438
+ }
439
+
440
+ test_v1 = {
441
+ 'type': 'sedan',
442
+ 'speed': 45,
443
+ 'direction': 'north',
444
+ 'action': 'going_straight',
445
+ 'braking': False,
446
+ 'signaling': False
447
+ }
448
+
449
+ test_v2 = {
450
+ 'type': 'suv',
451
+ 'speed': 55,
452
+ 'direction': 'east',
453
+ 'action': 'entering_roundabout',
454
+ 'braking': False,
455
+ 'signaling': True
456
+ }
457
+
458
+ result = manager.predict(test_accident, test_v1, test_v2)
459
+
460
+ print(f"\n🔮 Prediction Result:")
461
+ print(f" Backend: {result['backend']}")
462
+ print(f" Predicted: {result['class_name']}")
463
+ print(f" Confidence: {result['confidence']*100:.1f}%")
464
+ print(f"\n📊 All Probabilities:")
465
+ for name, prob in sorted(result['probabilities'].items(), key=lambda x: -x[1]):
466
+ bar = '█' * int(prob * 20)
467
+ print(f" {name:30}: {prob*100:5.1f}% {bar}")
models/mindspore_model.py ADDED
@@ -0,0 +1,737 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ MindSpore Accident Prediction Model
3
+ ====================================
4
+ Neural network model for predicting accident types and probabilities
5
+ using Huawei MindSpore framework.
6
+
7
+ Architecture:
8
+ - Input: 31 features (vehicle data, environment, derived features)
9
+ - Hidden: [256, 128, 64, 32] with BatchNorm and Dropout
10
+ - Output: 7 classes (accident types) + 1 probability
11
+
12
+ Huawei AI Innovation Challenge 2026
13
+ """
14
+
15
+ import numpy as np
16
+ from pathlib import Path
17
+ from typing import Tuple, Dict, Any, Optional
18
+ import json
19
+
20
+ # Try to import MindSpore (may not be available in all environments)
21
+ try:
22
+ import mindspore as ms
23
+ import mindspore.nn as nn
24
+ import mindspore.ops as ops
25
+ from mindspore import Tensor, Parameter
26
+ from mindspore.train import Model
27
+ from mindspore.train.callback import LossMonitor, ModelCheckpoint, CheckpointConfig
28
+ from mindspore.nn import SoftmaxCrossEntropyWithLogits, Adam, WithLossCell, TrainOneStepCell
29
+ from mindspore import context, save_checkpoint, load_checkpoint, load_param_into_net
30
+ MINDSPORE_AVAILABLE = True
31
+ except ImportError:
32
+ MINDSPORE_AVAILABLE = False
33
+ print("Warning: MindSpore not available. Using NumPy fallback.")
34
+
35
+ import sys
36
+ sys.path.insert(0, str(Path(__file__).parent.parent))
37
+
38
+ from config import MINDSPORE_CONFIG, ACCIDENT_TYPES
39
+
40
+ # Define MODEL_DIR locally if not in config
41
+ MODEL_DIR = Path(__file__).parent
42
+
43
+
44
+ # ============================================================
45
+ # MODEL ARCHITECTURE
46
+ # ============================================================
47
+
48
+ if MINDSPORE_AVAILABLE:
49
+
50
+ class AccidentClassifier(nn.Cell):
51
+ """
52
+ MindSpore Neural Network for Accident Type Classification.
53
+
54
+ Architecture:
55
+ - Input Layer: 31 features
56
+ - Hidden Layers: [256, 128, 64, 32] with BatchNorm, ReLU, Dropout
57
+ - Output Layer: 7 classes (accident types)
58
+ """
59
+
60
+ def __init__(self,
61
+ input_dim: int = 31,
62
+ hidden_dims: list = [256, 128, 64, 32],
63
+ num_classes: int = 7,
64
+ dropout_rate: float = 0.3):
65
+ super(AccidentClassifier, self).__init__()
66
+
67
+ self.input_dim = input_dim
68
+ self.num_classes = num_classes
69
+
70
+ # Build layers
71
+ layers = []
72
+ prev_dim = input_dim
73
+
74
+ for hidden_dim in hidden_dims:
75
+ layers.extend([
76
+ nn.Dense(prev_dim, hidden_dim),
77
+ nn.BatchNorm1d(hidden_dim),
78
+ nn.ReLU(),
79
+ nn.Dropout(p=dropout_rate)
80
+ ])
81
+ prev_dim = hidden_dim
82
+
83
+ # Output layer
84
+ layers.append(nn.Dense(prev_dim, num_classes))
85
+
86
+ self.network = nn.SequentialCell(layers)
87
+
88
+ def construct(self, x):
89
+ """Forward pass."""
90
+ return self.network(x)
91
+
92
+
93
+ class AccidentProbabilityRegressor(nn.Cell):
94
+ """
95
+ MindSpore Neural Network for Accident Probability Regression.
96
+
97
+ Predicts collision probability (0-1) based on input features.
98
+ """
99
+
100
+ def __init__(self,
101
+ input_dim: int = 31,
102
+ hidden_dims: list = [128, 64, 32],
103
+ dropout_rate: float = 0.2):
104
+ super(AccidentProbabilityRegressor, self).__init__()
105
+
106
+ layers = []
107
+ prev_dim = input_dim
108
+
109
+ for hidden_dim in hidden_dims:
110
+ layers.extend([
111
+ nn.Dense(prev_dim, hidden_dim),
112
+ nn.BatchNorm1d(hidden_dim),
113
+ nn.ReLU(),
114
+ nn.Dropout(p=dropout_rate)
115
+ ])
116
+ prev_dim = hidden_dim
117
+
118
+ # Output layer (sigmoid for probability)
119
+ layers.append(nn.Dense(prev_dim, 1))
120
+ layers.append(nn.Sigmoid())
121
+
122
+ self.network = nn.SequentialCell(layers)
123
+
124
+ def construct(self, x):
125
+ """Forward pass."""
126
+ return self.network(x)
127
+
128
+
129
+ class CombinedAccidentModel(nn.Cell):
130
+ """
131
+ Combined model for both classification and probability regression.
132
+
133
+ Two-head architecture:
134
+ - Shared backbone for feature extraction
135
+ - Classification head for accident type
136
+ - Regression head for collision probability
137
+ """
138
+
139
+ def __init__(self,
140
+ input_dim: int = 31,
141
+ backbone_dims: list = [256, 128],
142
+ classifier_dims: list = [64, 32],
143
+ regressor_dims: list = [32],
144
+ num_classes: int = 7,
145
+ dropout_rate: float = 0.3):
146
+ super(CombinedAccidentModel, self).__init__()
147
+
148
+ # Shared backbone
149
+ backbone_layers = []
150
+ prev_dim = input_dim
151
+
152
+ for hidden_dim in backbone_dims:
153
+ backbone_layers.extend([
154
+ nn.Dense(prev_dim, hidden_dim),
155
+ nn.BatchNorm1d(hidden_dim),
156
+ nn.ReLU(),
157
+ nn.Dropout(p=dropout_rate)
158
+ ])
159
+ prev_dim = hidden_dim
160
+
161
+ self.backbone = nn.SequentialCell(backbone_layers)
162
+ backbone_output_dim = prev_dim
163
+
164
+ # Classification head
165
+ classifier_layers = []
166
+ prev_dim = backbone_output_dim
167
+
168
+ for hidden_dim in classifier_dims:
169
+ classifier_layers.extend([
170
+ nn.Dense(prev_dim, hidden_dim),
171
+ nn.ReLU(),
172
+ nn.Dropout(p=dropout_rate * 0.5)
173
+ ])
174
+ prev_dim = hidden_dim
175
+
176
+ classifier_layers.append(nn.Dense(prev_dim, num_classes))
177
+ self.classifier = nn.SequentialCell(classifier_layers)
178
+
179
+ # Regression head
180
+ regressor_layers = []
181
+ prev_dim = backbone_output_dim
182
+
183
+ for hidden_dim in regressor_dims:
184
+ regressor_layers.extend([
185
+ nn.Dense(prev_dim, hidden_dim),
186
+ nn.ReLU()
187
+ ])
188
+ prev_dim = hidden_dim
189
+
190
+ regressor_layers.append(nn.Dense(prev_dim, 1))
191
+ regressor_layers.append(nn.Sigmoid())
192
+ self.regressor = nn.SequentialCell(regressor_layers)
193
+
194
+ def construct(self, x):
195
+ """Forward pass returning both outputs."""
196
+ features = self.backbone(x)
197
+ class_logits = self.classifier(features)
198
+ probability = self.regressor(features)
199
+ return class_logits, probability
200
+
201
+
202
+ # ============================================================
203
+ # NUMPY FALLBACK MODEL (when MindSpore is not available)
204
+ # ============================================================
205
+
206
+ class NumpyAccidentModel:
207
+ """
208
+ NumPy-based neural network model for accident classification.
209
+ Uses proper mini-batch gradient descent with Adam optimizer approximation.
210
+ """
211
+
212
+ def __init__(self, input_dim: int = 31, num_classes: int = 7):
213
+ self.input_dim = input_dim
214
+ self.num_classes = num_classes
215
+ self.trained = False
216
+
217
+ # Network architecture: input -> 64 -> 32 -> output
218
+ np.random.seed(42)
219
+
220
+ # He initialization for ReLU
221
+ self.W1 = np.random.randn(input_dim, 64) * np.sqrt(2.0 / input_dim)
222
+ self.b1 = np.zeros(64)
223
+
224
+ self.W2 = np.random.randn(64, 32) * np.sqrt(2.0 / 64)
225
+ self.b2 = np.zeros(32)
226
+
227
+ self.W3 = np.random.randn(32, num_classes) * np.sqrt(2.0 / 32)
228
+ self.b3 = np.zeros(num_classes)
229
+
230
+ # Adam optimizer state
231
+ self.m = {}
232
+ self.v = {}
233
+ self.t = 0
234
+
235
+ def relu(self, x):
236
+ return np.maximum(0, x)
237
+
238
+ def relu_derivative(self, x):
239
+ return (x > 0).astype(float)
240
+
241
+ def softmax(self, x):
242
+ exp_x = np.exp(x - np.max(x, axis=-1, keepdims=True))
243
+ return exp_x / np.sum(exp_x, axis=-1, keepdims=True)
244
+
245
+ def forward(self, X):
246
+ """Forward pass."""
247
+ self.z1 = X @ self.W1 + self.b1
248
+ self.a1 = self.relu(self.z1)
249
+
250
+ self.z2 = self.a1 @ self.W2 + self.b2
251
+ self.a2 = self.relu(self.z2)
252
+
253
+ self.z3 = self.a2 @ self.W3 + self.b3
254
+ self.a3 = self.softmax(self.z3)
255
+
256
+ return self.a3
257
+
258
+ def compute_loss(self, y_pred, y_true):
259
+ """Cross-entropy loss."""
260
+ n = len(y_true)
261
+ y_onehot = np.zeros((n, self.num_classes))
262
+ y_onehot[np.arange(n), y_true] = 1
263
+ return -np.mean(np.sum(y_onehot * np.log(y_pred + 1e-10), axis=1))
264
+
265
+ def backward(self, X, y_true, learning_rate=0.001):
266
+ """Backward pass with gradient descent."""
267
+ n = len(y_true)
268
+
269
+ # One-hot encode
270
+ y_onehot = np.zeros((n, self.num_classes))
271
+ y_onehot[np.arange(n), y_true] = 1
272
+
273
+ # Output layer gradient
274
+ dz3 = self.a3 - y_onehot
275
+ dW3 = self.a2.T @ dz3 / n
276
+ db3 = np.mean(dz3, axis=0)
277
+
278
+ # Hidden layer 2
279
+ da2 = dz3 @ self.W3.T
280
+ dz2 = da2 * self.relu_derivative(self.z2)
281
+ dW2 = self.a1.T @ dz2 / n
282
+ db2 = np.mean(dz2, axis=0)
283
+
284
+ # Hidden layer 1
285
+ da1 = dz2 @ self.W2.T
286
+ dz1 = da1 * self.relu_derivative(self.z1)
287
+ dW1 = X.T @ dz1 / n
288
+ db1 = np.mean(dz1, axis=0)
289
+
290
+ # Gradient clipping
291
+ max_grad = 5.0
292
+ dW1 = np.clip(dW1, -max_grad, max_grad)
293
+ dW2 = np.clip(dW2, -max_grad, max_grad)
294
+ dW3 = np.clip(dW3, -max_grad, max_grad)
295
+
296
+ # Update weights
297
+ self.W1 -= learning_rate * dW1
298
+ self.b1 -= learning_rate * db1
299
+ self.W2 -= learning_rate * dW2
300
+ self.b2 -= learning_rate * db2
301
+ self.W3 -= learning_rate * dW3
302
+ self.b3 -= learning_rate * db3
303
+
304
+ def predict(self, X):
305
+ """Predict classes and probabilities."""
306
+ if X.ndim == 1:
307
+ X = X.reshape(1, -1)
308
+ probs = self.forward(X)
309
+ classes = np.argmax(probs, axis=1)
310
+ return classes, probs
311
+
312
+ def train(self, X, y, epochs=100, lr=0.01, batch_size=32):
313
+ """Train the model."""
314
+ n_samples = X.shape[0]
315
+
316
+ best_loss = float('inf')
317
+ patience = 20
318
+ patience_counter = 0
319
+
320
+ for epoch in range(epochs):
321
+ # Shuffle
322
+ indices = np.random.permutation(n_samples)
323
+ X_shuffled = X[indices]
324
+ y_shuffled = y[indices]
325
+
326
+ total_loss = 0
327
+ n_batches = 0
328
+
329
+ # Learning rate schedule
330
+ current_lr = lr * (0.98 ** (epoch // 10))
331
+
332
+ for i in range(0, n_samples, batch_size):
333
+ X_batch = X_shuffled[i:i+batch_size]
334
+ y_batch = y_shuffled[i:i+batch_size]
335
+
336
+ # Forward
337
+ probs = self.forward(X_batch)
338
+ loss = self.compute_loss(probs, y_batch)
339
+ total_loss += loss
340
+ n_batches += 1
341
+
342
+ # Backward
343
+ self.backward(X_batch, y_batch, current_lr)
344
+
345
+ avg_loss = total_loss / n_batches
346
+
347
+ # Calculate accuracy
348
+ preds, _ = self.predict(X)
349
+ accuracy = np.mean(preds == y)
350
+
351
+ # Early stopping
352
+ if avg_loss < best_loss:
353
+ best_loss = avg_loss
354
+ patience_counter = 0
355
+ else:
356
+ patience_counter += 1
357
+
358
+ if (epoch + 1) % 10 == 0:
359
+ print(f"Epoch {epoch+1}/{epochs}, Loss: {avg_loss:.4f}, Accuracy: {accuracy*100:.1f}%")
360
+
361
+ if patience_counter >= patience:
362
+ print(f"Early stopping at epoch {epoch+1}")
363
+ break
364
+
365
+ self.trained = True
366
+
367
+ def save(self, filepath: str):
368
+ """Save model weights."""
369
+ np.savez(filepath,
370
+ W1=self.W1, b1=self.b1,
371
+ W2=self.W2, b2=self.b2,
372
+ W3=self.W3, b3=self.b3,
373
+ trained=self.trained)
374
+ print(f"Model saved to {filepath}")
375
+
376
+ def load(self, filepath: str):
377
+ """Load model weights."""
378
+ data = np.load(filepath)
379
+ self.W1 = data['W1']
380
+ self.b1 = data['b1']
381
+ self.W2 = data['W2']
382
+ self.b2 = data['b2']
383
+ self.W3 = data['W3']
384
+ self.b3 = data['b3']
385
+ self.trained = bool(data['trained'])
386
+ print(f"Model loaded from {filepath}")
387
+
388
+
389
+ # ============================================================
390
+ # MODEL TRAINER
391
+ # ============================================================
392
+
393
+ class AccidentModelTrainer:
394
+ """
395
+ Trainer class for accident prediction models.
396
+ Handles both MindSpore and NumPy fallback.
397
+ """
398
+
399
+ def __init__(self,
400
+ input_dim: int = 31,
401
+ num_classes: int = 7,
402
+ use_mindspore: bool = True):
403
+
404
+ self.input_dim = input_dim
405
+ self.num_classes = num_classes
406
+ self.use_mindspore = use_mindspore and MINDSPORE_AVAILABLE
407
+
408
+ if self.use_mindspore:
409
+ # Set MindSpore context
410
+ context.set_context(
411
+ mode=context.GRAPH_MODE,
412
+ device_target="CPU"
413
+ )
414
+ self.model = AccidentClassifier(
415
+ input_dim=input_dim,
416
+ num_classes=num_classes
417
+ )
418
+ print("Using MindSpore model")
419
+ else:
420
+ self.model = NumpyAccidentModel(
421
+ input_dim=input_dim,
422
+ num_classes=num_classes
423
+ )
424
+ print("Using NumPy fallback model")
425
+
426
+ self.trained = False
427
+ self.history = {'loss': [], 'accuracy': []}
428
+
429
+ def train(self,
430
+ X_train: np.ndarray,
431
+ y_train: np.ndarray,
432
+ X_val: np.ndarray = None,
433
+ y_val: np.ndarray = None,
434
+ epochs: int = 100,
435
+ batch_size: int = 64,
436
+ learning_rate: float = 0.001):
437
+ """
438
+ Train the model.
439
+
440
+ Args:
441
+ X_train: Training features
442
+ y_train: Training labels
443
+ X_val: Validation features (optional)
444
+ y_val: Validation labels (optional)
445
+ epochs: Number of training epochs
446
+ batch_size: Batch size
447
+ learning_rate: Learning rate
448
+ """
449
+
450
+ print(f"\n{'='*60}")
451
+ print("TRAINING ACCIDENT PREDICTION MODEL")
452
+ print(f"{'='*60}")
453
+ print(f"Training samples: {X_train.shape[0]}")
454
+ print(f"Features: {X_train.shape[1]}")
455
+ print(f"Classes: {self.num_classes}")
456
+ print(f"Epochs: {epochs}")
457
+ print(f"Batch size: {batch_size}")
458
+ print(f"Learning rate: {learning_rate}")
459
+ print(f"{'='*60}\n")
460
+
461
+ if self.use_mindspore:
462
+ self._train_mindspore(X_train, y_train, X_val, y_val,
463
+ epochs, batch_size, learning_rate)
464
+ else:
465
+ self._train_numpy(X_train, y_train, epochs, learning_rate, batch_size)
466
+
467
+ self.trained = True
468
+ print("\n✅ Training complete!")
469
+
470
+ def _train_mindspore(self, X_train, y_train, X_val, y_val,
471
+ epochs, batch_size, learning_rate):
472
+ """Train using MindSpore."""
473
+
474
+ # Create dataset
475
+ dataset = ms.dataset.NumpySlicesDataset(
476
+ {"data": X_train, "label": y_train},
477
+ shuffle=True
478
+ )
479
+ dataset = dataset.batch(batch_size)
480
+
481
+ # Loss and optimizer
482
+ loss_fn = nn.SoftmaxCrossEntropyWithLogits(sparse=True, reduction='mean')
483
+ optimizer = nn.Adam(self.model.trainable_params(), learning_rate=learning_rate)
484
+
485
+ # Training loop
486
+ self.model.set_train()
487
+
488
+ for epoch in range(epochs):
489
+ total_loss = 0
490
+ num_batches = 0
491
+
492
+ for batch in dataset.create_dict_iterator():
493
+ data = batch["data"]
494
+ label = batch["label"]
495
+
496
+ # Forward pass
497
+ logits = self.model(data)
498
+ loss = loss_fn(logits, label)
499
+
500
+ # Backward pass
501
+ grads = ops.GradOperation(get_all=True)(loss_fn)(logits, label)
502
+ optimizer(grads)
503
+
504
+ total_loss += loss.asnumpy()
505
+ num_batches += 1
506
+
507
+ avg_loss = total_loss / num_batches
508
+ self.history['loss'].append(avg_loss)
509
+
510
+ if (epoch + 1) % 10 == 0:
511
+ # Calculate accuracy on validation set
512
+ if X_val is not None:
513
+ acc = self._evaluate_mindspore(X_val, y_val)
514
+ self.history['accuracy'].append(acc)
515
+ print(f"Epoch {epoch+1}/{epochs} - Loss: {avg_loss:.4f} - Val Acc: {acc:.4f}")
516
+ else:
517
+ print(f"Epoch {epoch+1}/{epochs} - Loss: {avg_loss:.4f}")
518
+
519
+ def _train_numpy(self, X_train, y_train, epochs, learning_rate, batch_size):
520
+ """Train using NumPy fallback."""
521
+ self.model.train(X_train, y_train, epochs=epochs,
522
+ lr=learning_rate, batch_size=batch_size)
523
+
524
+ def _evaluate_mindspore(self, X, y):
525
+ """Evaluate accuracy using MindSpore."""
526
+ self.model.set_train(False)
527
+
528
+ logits = self.model(Tensor(X, ms.float32))
529
+ predictions = logits.asnumpy().argmax(axis=1)
530
+ accuracy = (predictions == y).mean()
531
+
532
+ self.model.set_train(True)
533
+ return accuracy
534
+
535
+ def predict(self, X: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
536
+ """
537
+ Predict accident type and probabilities.
538
+
539
+ Returns:
540
+ class_ids: Predicted class indices
541
+ probabilities: Probability for each class
542
+ """
543
+ if not self.trained:
544
+ print("Warning: Model not trained, using random initialization")
545
+
546
+ if self.use_mindspore:
547
+ self.model.set_train(False)
548
+ logits = self.model(Tensor(X, ms.float32))
549
+ probs = ops.Softmax(axis=1)(logits).asnumpy()
550
+ class_ids = probs.argmax(axis=1)
551
+ else:
552
+ class_ids, probs = self.model.predict(X)
553
+
554
+ return class_ids, probs
555
+
556
+ def save(self, filepath: str):
557
+ """Save the trained model."""
558
+ model_dir = Path(filepath).parent
559
+ model_dir.mkdir(parents=True, exist_ok=True)
560
+
561
+ if self.use_mindspore:
562
+ save_checkpoint(self.model, filepath + ".ckpt")
563
+ print(f"MindSpore model saved to {filepath}.ckpt")
564
+ else:
565
+ self.model.save(filepath + ".npz")
566
+
567
+ # Save metadata
568
+ metadata = {
569
+ 'input_dim': self.input_dim,
570
+ 'num_classes': self.num_classes,
571
+ 'use_mindspore': self.use_mindspore,
572
+ 'trained': self.trained,
573
+ 'history': self.history
574
+ }
575
+ with open(filepath + "_metadata.json", 'w') as f:
576
+ json.dump(metadata, f, indent=2)
577
+
578
+ def load(self, filepath: str):
579
+ """Load a trained model."""
580
+ if self.use_mindspore:
581
+ param_dict = load_checkpoint(filepath + ".ckpt")
582
+ load_param_into_net(self.model, param_dict)
583
+ print(f"MindSpore model loaded from {filepath}.ckpt")
584
+ else:
585
+ self.model.load(filepath + ".npz")
586
+
587
+ # Load metadata
588
+ metadata_path = filepath + "_metadata.json"
589
+ if Path(metadata_path).exists():
590
+ with open(metadata_path, 'r') as f:
591
+ metadata = json.load(f)
592
+ self.trained = metadata.get('trained', True)
593
+ self.history = metadata.get('history', {})
594
+ else:
595
+ self.trained = True
596
+
597
+
598
+ # ============================================================
599
+ # HELPER FUNCTIONS
600
+ # ============================================================
601
+
602
+ def get_accident_type_name(class_id: int) -> str:
603
+ """Convert class ID to accident type name."""
604
+ if 0 <= class_id < len(ACCIDENT_TYPES):
605
+ return ACCIDENT_TYPES[class_id]
606
+ return "unknown"
607
+
608
+
609
+ def prepare_features(accident_data: Dict) -> np.ndarray:
610
+ """
611
+ Prepare feature vector from accident data dictionary.
612
+
613
+ Args:
614
+ accident_data: Dictionary containing accident information
615
+
616
+ Returns:
617
+ Feature vector (31 dimensions)
618
+ """
619
+ # Encodings
620
+ direction_encoding = {
621
+ 'north': 0, 'northeast': 1, 'east': 2, 'southeast': 3,
622
+ 'south': 4, 'southwest': 5, 'west': 6, 'northwest': 7
623
+ }
624
+ action_encoding = {
625
+ 'going_straight': 0, 'turning_left': 1, 'turning_right': 2,
626
+ 'entering_roundabout': 3, 'exiting_roundabout': 4,
627
+ 'changing_lane_left': 5, 'changing_lane_right': 6,
628
+ 'slowing_down': 7, 'accelerating': 8, 'stopped': 9
629
+ }
630
+ vehicle_encoding = {'sedan': 0, 'suv': 1, 'truck': 2, 'motorcycle': 3, 'bus': 4}
631
+ weather_encoding = {'clear': 0, 'cloudy': 1, 'rainy': 2, 'foggy': 3, 'sandstorm': 4}
632
+ road_encoding = {'dry': 0, 'wet': 1, 'sandy': 2, 'oily': 3}
633
+ road_type_encoding = {
634
+ 'roundabout': 0, 'crossroad': 1, 't_junction': 2, 'highway_merge': 3,
635
+ 'parking': 4, 'highway': 5, 'urban_road': 6, 'other': 7
636
+ }
637
+ lighting_encoding = {'daylight': 0, 'dusk': 1, 'dawn': 2, 'night_lit': 3, 'night_dark': 4}
638
+
639
+ # Extract data
640
+ v1 = accident_data.get('vehicle_1', {})
641
+ v2 = accident_data.get('vehicle_2', {})
642
+ conditions = accident_data.get('accident_info', {})
643
+
644
+ # Build feature vector
645
+ v1_speed = v1.get('speed', 50)
646
+ v2_speed = v2.get('speed', 50)
647
+ v1_dir = direction_encoding.get(v1.get('direction', 'north'), 0)
648
+ v2_dir = direction_encoding.get(v2.get('direction', 'east'), 2)
649
+ v1_angle = v1_dir * 45
650
+ v2_angle = v2_dir * 45
651
+
652
+ collision_angle = abs(v1_angle - v2_angle)
653
+ if collision_angle > 180:
654
+ collision_angle = 360 - collision_angle
655
+
656
+ weather = weather_encoding.get(conditions.get('weather', 'clear'), 0)
657
+ road_cond = road_encoding.get(conditions.get('road_condition', 'dry'), 0)
658
+ visibility = conditions.get('visibility', 1.0)
659
+ lighting = lighting_encoding.get(conditions.get('lighting', 'daylight'), 0)
660
+ road_type = road_type_encoding.get(conditions.get('road_type', 'roundabout'), 0)
661
+
662
+ features = [
663
+ # Vehicle 1 (7)
664
+ vehicle_encoding.get(v1.get('type', 'sedan'), 0) / 5,
665
+ v1_speed / 200,
666
+ v1_dir / 8,
667
+ v1_angle / 360,
668
+ action_encoding.get(v1.get('action', 'going_straight'), 0) / 10,
669
+ 1 if v1.get('braking', False) else 0,
670
+ 1 if v1.get('signaling', False) else 0,
671
+
672
+ # Vehicle 2 (7)
673
+ vehicle_encoding.get(v2.get('type', 'sedan'), 0) / 5,
674
+ v2_speed / 200,
675
+ v2_dir / 8,
676
+ v2_angle / 360,
677
+ action_encoding.get(v2.get('action', 'going_straight'), 0) / 10,
678
+ 1 if v2.get('braking', False) else 0,
679
+ 1 if v2.get('signaling', False) else 0,
680
+
681
+ # Environment (5)
682
+ weather / 5,
683
+ road_cond / 4,
684
+ visibility,
685
+ lighting / 5,
686
+ road_type / 8,
687
+
688
+ # Derived (12)
689
+ collision_angle / 180,
690
+ abs(v1_speed - v2_speed) / 200,
691
+ (v1_speed + v2_speed) / 400,
692
+ 1 if v1.get('direction') == v2.get('direction') else 0,
693
+ (v1_speed * v2_speed) / 40000,
694
+ collision_angle / 180,
695
+ 12 / 24, # Default noon
696
+ 0.3, # Default risk
697
+ 0.5, # V1 action risk
698
+ 0.5, # V2 action risk
699
+ (v1_speed + v2_speed) / 400, # Relative speed
700
+ 0.3 # Approach rate
701
+ ]
702
+
703
+ return np.array(features, dtype=np.float32)
704
+
705
+
706
+ # ============================================================
707
+ # TEST
708
+ # ============================================================
709
+
710
+ if __name__ == "__main__":
711
+ print("Testing Accident Prediction Model")
712
+ print("="*60)
713
+
714
+ # Create dummy data
715
+ np.random.seed(42)
716
+ X_test = np.random.randn(100, 31).astype(np.float32)
717
+ y_test = np.random.randint(0, 7, 100).astype(np.int32)
718
+
719
+ # Create and test model
720
+ trainer = AccidentModelTrainer(
721
+ input_dim=31,
722
+ num_classes=7,
723
+ use_mindspore=MINDSPORE_AVAILABLE
724
+ )
725
+
726
+ # Train
727
+ trainer.train(X_test, y_test, epochs=20, batch_size=16)
728
+
729
+ # Predict
730
+ class_ids, probs = trainer.predict(X_test[:5])
731
+
732
+ print("\nPredictions:")
733
+ for i in range(5):
734
+ print(f" Sample {i+1}: {get_accident_type_name(class_ids[i])} "
735
+ f"(prob: {probs[i].max():.3f})")
736
+
737
+ print("\n✅ Model test complete!")
models/trained/best_accident_model.ckpt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2f579d411b52fcf754113dd5b81e1c9aba5f319dd493910c4210e95b03974976
3
+ size 62916
output/reports/accident_report_20251230_084121.html ADDED
@@ -0,0 +1,688 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ <!DOCTYPE html>
3
+ <html lang="en">
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Traffic Accident Analysis Report</title>
8
+ <style>
9
+ * {
10
+ margin: 0;
11
+ padding: 0;
12
+ box-sizing: border-box;
13
+ }
14
+
15
+ body {
16
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
17
+ line-height: 1.6;
18
+ color: #333;
19
+ background: #f5f5f5;
20
+ }
21
+
22
+ .container {
23
+ max-width: 1000px;
24
+ margin: 0 auto;
25
+ background: white;
26
+ box-shadow: 0 0 20px rgba(0,0,0,0.1);
27
+ }
28
+
29
+ .header {
30
+ background: linear-gradient(135deg, #1e3a5f 0%, #2d5a87 100%);
31
+ color: white;
32
+ padding: 40px;
33
+ text-align: center;
34
+ }
35
+
36
+ .header h1 {
37
+ font-size: 2.5rem;
38
+ margin-bottom: 10px;
39
+ }
40
+
41
+ .header .subtitle {
42
+ opacity: 0.9;
43
+ font-size: 1.1rem;
44
+ }
45
+
46
+ .section {
47
+ padding: 30px 40px;
48
+ border-bottom: 1px solid #eee;
49
+ }
50
+
51
+ .section h2 {
52
+ color: #1e3a5f;
53
+ margin-bottom: 20px;
54
+ padding-bottom: 10px;
55
+ border-bottom: 2px solid #2d5a87;
56
+ }
57
+
58
+ .summary-grid {
59
+ display: grid;
60
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
61
+ gap: 20px;
62
+ margin: 20px 0;
63
+ }
64
+
65
+ .summary-card {
66
+ background: #f8f9fa;
67
+ padding: 20px;
68
+ border-radius: 10px;
69
+ text-align: center;
70
+ border-top: 4px solid #2d5a87;
71
+ }
72
+
73
+ .summary-card .label {
74
+ font-size: 0.9rem;
75
+ color: #666;
76
+ margin-bottom: 5px;
77
+ }
78
+
79
+ .summary-card .value {
80
+ font-size: 1.8rem;
81
+ font-weight: bold;
82
+ color: #1e3a5f;
83
+ }
84
+
85
+ .summary-card .delta {
86
+ font-size: 0.85rem;
87
+ margin-top: 5px;
88
+ }
89
+
90
+ .delta.high { color: #28a745; }
91
+ .delta.medium { color: #ffc107; }
92
+ .delta.low { color: #dc3545; }
93
+
94
+ .info-grid {
95
+ display: grid;
96
+ grid-template-columns: repeat(2, 1fr);
97
+ gap: 30px;
98
+ }
99
+
100
+ .info-box {
101
+ background: #f8f9fa;
102
+ padding: 20px;
103
+ border-radius: 10px;
104
+ }
105
+
106
+ .info-box h3 {
107
+ color: #1e3a5f;
108
+ margin-bottom: 15px;
109
+ }
110
+
111
+ .info-row {
112
+ display: flex;
113
+ justify-content: space-between;
114
+ padding: 8px 0;
115
+ border-bottom: 1px solid #eee;
116
+ }
117
+
118
+ .info-row:last-child {
119
+ border-bottom: none;
120
+ }
121
+
122
+ .vehicle-card {
123
+ background: white;
124
+ border-radius: 10px;
125
+ padding: 20px;
126
+ margin: 15px 0;
127
+ }
128
+
129
+ .vehicle-card.v1 {
130
+ border-left: 4px solid #FF4B4B;
131
+ }
132
+
133
+ .vehicle-card.v2 {
134
+ border-left: 4px solid #4B7BFF;
135
+ }
136
+
137
+ .scenario-card {
138
+ background: #f8f9fa;
139
+ border-radius: 10px;
140
+ padding: 20px;
141
+ margin: 15px 0;
142
+ border-left: 4px solid #2d5a87;
143
+ }
144
+
145
+ .scenario-header {
146
+ display: flex;
147
+ justify-content: space-between;
148
+ align-items: center;
149
+ margin-bottom: 15px;
150
+ }
151
+
152
+ .probability {
153
+ font-size: 1.5rem;
154
+ font-weight: bold;
155
+ }
156
+
157
+ .probability.high { color: #28a745; }
158
+ .probability.medium { color: #ffc107; }
159
+ .probability.low { color: #dc3545; }
160
+
161
+ .progress-bar {
162
+ background: #e9ecef;
163
+ border-radius: 5px;
164
+ height: 10px;
165
+ margin: 10px 0;
166
+ overflow: hidden;
167
+ }
168
+
169
+ .progress-fill {
170
+ background: #2d5a87;
171
+ height: 100%;
172
+ border-radius: 5px;
173
+ }
174
+
175
+ .factors-list {
176
+ list-style: none;
177
+ padding: 0;
178
+ }
179
+
180
+ .factors-list li {
181
+ padding: 5px 0;
182
+ padding-left: 20px;
183
+ position: relative;
184
+ }
185
+
186
+ .factors-list li::before {
187
+ content: "•";
188
+ color: #2d5a87;
189
+ position: absolute;
190
+ left: 0;
191
+ }
192
+
193
+ .timeline {
194
+ margin: 20px 0;
195
+ }
196
+
197
+ .timeline-item {
198
+ display: flex;
199
+ margin: 10px 0;
200
+ }
201
+
202
+ .timeline-time {
203
+ min-width: 80px;
204
+ padding: 8px 15px;
205
+ background: #ffc107;
206
+ color: white;
207
+ font-weight: bold;
208
+ text-align: center;
209
+ border-radius: 5px;
210
+ }
211
+
212
+ .timeline-time.impact {
213
+ background: #dc3545;
214
+ }
215
+
216
+ .timeline-time.after {
217
+ background: #28a745;
218
+ }
219
+
220
+ .timeline-event {
221
+ flex: 1;
222
+ padding: 8px 15px;
223
+ background: #f8f9fa;
224
+ margin-left: 10px;
225
+ border-radius: 5px;
226
+ }
227
+
228
+ .fault-assessment {
229
+ background: #fff3cd;
230
+ padding: 20px;
231
+ border-radius: 10px;
232
+ border-left: 4px solid #ffc107;
233
+ margin: 20px 0;
234
+ }
235
+
236
+ .footer {
237
+ background: #1e3a5f;
238
+ color: white;
239
+ padding: 30px 40px;
240
+ text-align: center;
241
+ }
242
+
243
+ .footer p {
244
+ opacity: 0.8;
245
+ font-size: 0.9rem;
246
+ }
247
+
248
+ @media print {
249
+ .container {
250
+ box-shadow: none;
251
+ }
252
+
253
+ .section {
254
+ page-break-inside: avoid;
255
+ }
256
+ }
257
+ </style>
258
+ </head>
259
+ <body>
260
+ <div class="container">
261
+ <!-- Header -->
262
+ <div class="header">
263
+ <h1>🚗 Traffic Accident Analysis Report</h1>
264
+ <p class="subtitle">AI-Powered Analysis using Huawei MindSpore</p>
265
+ <p style="margin-top: 15px; opacity: 0.7;">Generated: December 30, 2025 at 08:41</p>
266
+ </div>
267
+
268
+ <!-- Executive Summary -->
269
+ <div class="section">
270
+ <h2>📊 Executive Summary</h2>
271
+
272
+ <div class="summary-grid">
273
+ <div class="summary-card">
274
+ <div class="label">Most Likely Scenario</div>
275
+ <div class="value">#1</div>
276
+ <div class="delta high">27.9% probability</div>
277
+ </div>
278
+
279
+ <div class="summary-card">
280
+ <div class="label">Scenarios Generated</div>
281
+ <div class="value">5</div>
282
+ <div class="delta">AI-generated</div>
283
+ </div>
284
+
285
+ <div class="summary-card">
286
+ <div class="label">Collision Certainty</div>
287
+ <div class="value">86.0%</div>
288
+ <div class="delta high">
289
+ High
290
+ </div>
291
+ </div>
292
+
293
+ <div class="summary-card">
294
+ <div class="label">Primary Factor</div>
295
+ <div class="value" style="font-size: 1.2rem;">Failure To Yield</div>
296
+ <div class="delta">Vehicle 2</div>
297
+ </div>
298
+ </div>
299
+ </div>
300
+
301
+ <!-- Accident Details -->
302
+ <div class="section">
303
+ <h2>📍 Accident Details</h2>
304
+
305
+ <div class="info-grid">
306
+ <div class="info-box">
307
+ <h3>Location Information</h3>
308
+ <div class="info-row">
309
+ <span>Location:</span>
310
+ <strong>دوار السيف - Seef District Roundabout</strong>
311
+ </div>
312
+ <div class="info-row">
313
+ <span>Coordinates:</span>
314
+ <strong>26.2397, 50.5369</strong>
315
+ </div>
316
+ <div class="info-row">
317
+ <span>Road Type:</span>
318
+ <strong>Roundabout</strong>
319
+ </div>
320
+ </div>
321
+
322
+ <div class="info-box">
323
+ <h3>Conditions</h3>
324
+ <div class="info-row">
325
+ <span>Date/Time:</span>
326
+ <strong>2025-12-30 10:30</strong>
327
+ </div>
328
+ <div class="info-row">
329
+ <span>Weather:</span>
330
+ <strong>Clear</strong>
331
+ </div>
332
+ <div class="info-row">
333
+ <span>Road Condition:</span>
334
+ <strong>Dry</strong>
335
+ </div>
336
+ </div>
337
+ </div>
338
+ </div>
339
+
340
+ <!-- Vehicle Information -->
341
+ <div class="section">
342
+ <h2>🚙 Vehicle Information</h2>
343
+
344
+ <div class="info-grid">
345
+ <div class="vehicle-card v1">
346
+ <h3 style="color: #FF4B4B;">Vehicle 1 (Red)</h3>
347
+ <div class="info-row">
348
+ <span>Type:</span>
349
+ <strong>Sedan</strong>
350
+ </div>
351
+ <div class="info-row">
352
+ <span>Speed:</span>
353
+ <strong>45 km/h</strong>
354
+ </div>
355
+ <div class="info-row">
356
+ <span>Direction:</span>
357
+ <strong>North</strong>
358
+ </div>
359
+ <div class="info-row">
360
+ <span>Action:</span>
361
+ <strong>Entering Roundabout</strong>
362
+ </div>
363
+ <div class="info-row">
364
+ <span>Braking:</span>
365
+ <strong>No</strong>
366
+ </div>
367
+ <div class="info-row">
368
+ <span>Signaling:</span>
369
+ <strong>Yes</strong>
370
+ </div>
371
+ </div>
372
+
373
+ <div class="vehicle-card v2">
374
+ <h3 style="color: #4B7BFF;">Vehicle 2 (Blue)</h3>
375
+ <div class="info-row">
376
+ <span>Type:</span>
377
+ <strong>SUV</strong>
378
+ </div>
379
+ <div class="info-row">
380
+ <span>Speed:</span>
381
+ <strong>55 km/h</strong>
382
+ </div>
383
+ <div class="info-row">
384
+ <span>Direction:</span>
385
+ <strong>East</strong>
386
+ </div>
387
+ <div class="info-row">
388
+ <span>Action:</span>
389
+ <strong>Going Straight</strong>
390
+ </div>
391
+ <div class="info-row">
392
+ <span>Braking:</span>
393
+ <strong>Yes</strong>
394
+ </div>
395
+ <div class="info-row">
396
+ <span>Signaling:</span>
397
+ <strong>No</strong>
398
+ </div>
399
+ </div>
400
+ </div>
401
+ </div>
402
+
403
+ <!-- Generated Scenarios -->
404
+ <div class="section">
405
+ <h2>🎯 AI-Generated Scenarios</h2>
406
+
407
+
408
+ <div class="scenario-card">
409
+ <div class="scenario-header">
410
+ <div>
411
+ <h3>Scenario 1: Side Impact</h3>
412
+ </div>
413
+ <div class="probability medium">
414
+ 27.9%
415
+ </div>
416
+ </div>
417
+
418
+ <p>A Sedan was struck on the side by a SUV at an intersection. The impact angle was approximately 90 degrees.</p>
419
+
420
+ <div style="margin-top: 15px;">
421
+ <strong>Analysis Metrics:</strong>
422
+ <div style="margin-top: 10px;">
423
+ <span>Collision Probability: 86.0%</span>
424
+ <div class="progress-bar">
425
+ <div class="progress-fill" style="width: 86.0440679248718%"></div>
426
+ </div>
427
+ </div>
428
+ <div>
429
+ <span>Path Overlap: 87.6%</span>
430
+ <div class="progress-bar">
431
+ <div class="progress-fill" style="width: 87.64689308290596%"></div>
432
+ </div>
433
+ </div>
434
+ <p style="margin-top: 10px;">Speed Differential: 10.0 km/h | Time to Collision: 1.16s</p>
435
+ </div>
436
+
437
+ <div style="margin-top: 15px;">
438
+ <strong>Contributing Factors:</strong>
439
+ <ul class="factors-list">
440
+ <li>Failure To Yield</li><li>Running Red Light</li>
441
+ </ul>
442
+ </div>
443
+ </div>
444
+
445
+ <div class="scenario-card">
446
+ <div class="scenario-header">
447
+ <div>
448
+ <h3>Scenario 2: Roundabout Entry Collision</h3>
449
+ </div>
450
+ <div class="probability medium">
451
+ 23.0%
452
+ </div>
453
+ </div>
454
+
455
+ <p>A Sedan entering the roundabout collided with a SUV already circulating within the roundabout.</p>
456
+
457
+ <div style="margin-top: 15px;">
458
+ <strong>Analysis Metrics:</strong>
459
+ <div style="margin-top: 10px;">
460
+ <span>Collision Probability: 86.0%</span>
461
+ <div class="progress-bar">
462
+ <div class="progress-fill" style="width: 86.0440679248718%"></div>
463
+ </div>
464
+ </div>
465
+ <div>
466
+ <span>Path Overlap: 87.6%</span>
467
+ <div class="progress-bar">
468
+ <div class="progress-fill" style="width: 87.64689308290596%"></div>
469
+ </div>
470
+ </div>
471
+ <p style="margin-top: 10px;">Speed Differential: 10.0 km/h | Time to Collision: 1.16s</p>
472
+ </div>
473
+
474
+ <div style="margin-top: 15px;">
475
+ <strong>Contributing Factors:</strong>
476
+ <ul class="factors-list">
477
+ <li>Failure To Yield</li><li>Running Red Light</li>
478
+ </ul>
479
+ </div>
480
+ </div>
481
+
482
+ <div class="scenario-card">
483
+ <div class="scenario-header">
484
+ <div>
485
+ <h3>Scenario 3: Intersection Collision</h3>
486
+ </div>
487
+ <div class="probability medium">
488
+ 23.0%
489
+ </div>
490
+ </div>
491
+
492
+ <p>Both vehicles entered the intersection simultaneously, resulting in a collision at the crossing point.</p>
493
+
494
+ <div style="margin-top: 15px;">
495
+ <strong>Analysis Metrics:</strong>
496
+ <div style="margin-top: 10px;">
497
+ <span>Collision Probability: 86.0%</span>
498
+ <div class="progress-bar">
499
+ <div class="progress-fill" style="width: 86.0440679248718%"></div>
500
+ </div>
501
+ </div>
502
+ <div>
503
+ <span>Path Overlap: 87.6%</span>
504
+ <div class="progress-bar">
505
+ <div class="progress-fill" style="width: 87.64689308290596%"></div>
506
+ </div>
507
+ </div>
508
+ <p style="margin-top: 10px;">Speed Differential: 10.0 km/h | Time to Collision: 1.16s</p>
509
+ </div>
510
+
511
+ <div style="margin-top: 15px;">
512
+ <strong>Contributing Factors:</strong>
513
+ <ul class="factors-list">
514
+ <li>Failure To Yield</li>
515
+ </ul>
516
+ </div>
517
+ </div>
518
+
519
+ <div class="scenario-card">
520
+ <div class="scenario-header">
521
+ <div>
522
+ <h3>Scenario 4: Lane Change Collision</h3>
523
+ </div>
524
+ <div class="probability low">
525
+ 19.7%
526
+ </div>
527
+ </div>
528
+
529
+ <p>A collision occurred when one vehicle changed lanes without properly checking for the other vehicle.</p>
530
+
531
+ <div style="margin-top: 15px;">
532
+ <strong>Analysis Metrics:</strong>
533
+ <div style="margin-top: 10px;">
534
+ <span>Collision Probability: 86.0%</span>
535
+ <div class="progress-bar">
536
+ <div class="progress-fill" style="width: 86.0440679248718%"></div>
537
+ </div>
538
+ </div>
539
+ <div>
540
+ <span>Path Overlap: 87.6%</span>
541
+ <div class="progress-bar">
542
+ <div class="progress-fill" style="width: 87.64689308290596%"></div>
543
+ </div>
544
+ </div>
545
+ <p style="margin-top: 10px;">Speed Differential: 10.0 km/h | Time to Collision: 1.16s</p>
546
+ </div>
547
+
548
+ <div style="margin-top: 15px;">
549
+ <strong>Contributing Factors:</strong>
550
+ <ul class="factors-list">
551
+ <li>Improper Lane Change</li>
552
+ </ul>
553
+ </div>
554
+ </div>
555
+
556
+ <div class="scenario-card">
557
+ <div class="scenario-header">
558
+ <div>
559
+ <h3>Scenario 5: Sideswipe</h3>
560
+ </div>
561
+ <div class="probability low">
562
+ 6.6%
563
+ </div>
564
+ </div>
565
+
566
+ <p>Both vehicles were traveling in similar directions when a Sedan sideswiped a SUV during a lane change or merge.</p>
567
+
568
+ <div style="margin-top: 15px;">
569
+ <strong>Analysis Metrics:</strong>
570
+ <div style="margin-top: 10px;">
571
+ <span>Collision Probability: 86.0%</span>
572
+ <div class="progress-bar">
573
+ <div class="progress-fill" style="width: 86.0440679248718%"></div>
574
+ </div>
575
+ </div>
576
+ <div>
577
+ <span>Path Overlap: 87.6%</span>
578
+ <div class="progress-bar">
579
+ <div class="progress-fill" style="width: 87.64689308290596%"></div>
580
+ </div>
581
+ </div>
582
+ <p style="margin-top: 10px;">Speed Differential: 10.0 km/h | Time to Collision: 1.16s</p>
583
+ </div>
584
+
585
+ <div style="margin-top: 15px;">
586
+ <strong>Contributing Factors:</strong>
587
+ <ul class="factors-list">
588
+ <li>Improper Lane Change</li><li>Running Red Light</li>
589
+ </ul>
590
+ </div>
591
+ </div>
592
+
593
+ </div>
594
+
595
+ <!-- Fault Assessment -->
596
+ <div class="section">
597
+ <h2>⚖️ Preliminary Fault Assessment</h2>
598
+
599
+ <div class="fault-assessment">
600
+ <h3>⚠️ Disclaimer</h3>
601
+ <p>This is a preliminary AI-generated assessment for reference purposes only. Final fault determination should be made by qualified traffic authorities based on comprehensive investigation.</p>
602
+ </div>
603
+
604
+ <div class="info-grid" style="margin-top: 20px;">
605
+ <div class="info-box">
606
+ <h3>Contribution Analysis</h3>
607
+ <div style="margin: 15px 0;">
608
+ <span style="color: #FF4B4B;">Vehicle 1: 36.4%</span>
609
+ <div class="progress-bar">
610
+ <div class="progress-fill" style="width: 36.44859813084113%; background: #FF4B4B;"></div>
611
+ </div>
612
+ </div>
613
+ <div>
614
+ <span style="color: #4B7BFF;">Vehicle 2: 63.6%</span>
615
+ <div class="progress-bar">
616
+ <div class="progress-fill" style="width: 63.55140186915889%; background: #4B7BFF;"></div>
617
+ </div>
618
+ </div>
619
+ </div>
620
+
621
+ <div class="info-box">
622
+ <h3>Assessment Summary</h3>
623
+ <div class="info-row">
624
+ <span>Higher Contribution:</span>
625
+ <strong>Vehicle 2</strong>
626
+ </div>
627
+ <div class="info-row">
628
+ <span>Primary Factor:</span>
629
+ <strong>Failure To Yield</strong>
630
+ </div>
631
+ <div class="info-row">
632
+ <span>Assessment Confidence:</span>
633
+ <strong>63.6%</strong>
634
+ </div>
635
+ </div>
636
+ </div>
637
+ </div>
638
+
639
+ <!-- Timeline -->
640
+ <div class="section">
641
+ <h2>⏱️ Estimated Accident Timeline</h2>
642
+
643
+ <div class="timeline">
644
+
645
+ <div class="timeline-item">
646
+ <div class="timeline-time ">-1.2s</div>
647
+ <div class="timeline-event">Vehicles approaching conflict zone</div>
648
+ </div>
649
+
650
+ <div class="timeline-item">
651
+ <div class="timeline-time ">-0.7s</div>
652
+ <div class="timeline-event">Vehicle paths begin to converge</div>
653
+ </div>
654
+
655
+ <div class="timeline-item">
656
+ <div class="timeline-time ">-0.3s</div>
657
+ <div class="timeline-event">Collision becomes imminent</div>
658
+ </div>
659
+
660
+ <div class="timeline-item">
661
+ <div class="timeline-time ">-0.1s</div>
662
+ <div class="timeline-event">Point of no return - evasive action no longer possible</div>
663
+ </div>
664
+
665
+ <div class="timeline-item">
666
+ <div class="timeline-time impact">+0.0s</div>
667
+ <div class="timeline-event">Impact - Collision occurs</div>
668
+ </div>
669
+
670
+ <div class="timeline-item">
671
+ <div class="timeline-time after">+0.5s</div>
672
+ <div class="timeline-event">Vehicles come to rest after impact</div>
673
+ </div>
674
+
675
+ </div>
676
+ </div>
677
+
678
+ <!-- Footer -->
679
+ <div class="footer">
680
+ <p><strong>Traffic Accident Reconstruction System</strong></p>
681
+ <p>Huawei AI Innovation Challenge 2026 | Jubail Industrial College</p>
682
+ <p style="margin-top: 10px;">Powered by Huawei MindSpore AI Framework</p>
683
+ <p style="margin-top: 15px; font-size: 0.8rem;">Report ID: 20251230_084121</p>
684
+ </div>
685
+ </div>
686
+ </body>
687
+ </html>
688
+
output/reports/accident_report_20251230_091851.html ADDED
@@ -0,0 +1,688 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ <!DOCTYPE html>
3
+ <html lang="en">
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Traffic Accident Analysis Report</title>
8
+ <style>
9
+ * {
10
+ margin: 0;
11
+ padding: 0;
12
+ box-sizing: border-box;
13
+ }
14
+
15
+ body {
16
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
17
+ line-height: 1.6;
18
+ color: #333;
19
+ background: #f5f5f5;
20
+ }
21
+
22
+ .container {
23
+ max-width: 1000px;
24
+ margin: 0 auto;
25
+ background: white;
26
+ box-shadow: 0 0 20px rgba(0,0,0,0.1);
27
+ }
28
+
29
+ .header {
30
+ background: linear-gradient(135deg, #1e3a5f 0%, #2d5a87 100%);
31
+ color: white;
32
+ padding: 40px;
33
+ text-align: center;
34
+ }
35
+
36
+ .header h1 {
37
+ font-size: 2.5rem;
38
+ margin-bottom: 10px;
39
+ }
40
+
41
+ .header .subtitle {
42
+ opacity: 0.9;
43
+ font-size: 1.1rem;
44
+ }
45
+
46
+ .section {
47
+ padding: 30px 40px;
48
+ border-bottom: 1px solid #eee;
49
+ }
50
+
51
+ .section h2 {
52
+ color: #1e3a5f;
53
+ margin-bottom: 20px;
54
+ padding-bottom: 10px;
55
+ border-bottom: 2px solid #2d5a87;
56
+ }
57
+
58
+ .summary-grid {
59
+ display: grid;
60
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
61
+ gap: 20px;
62
+ margin: 20px 0;
63
+ }
64
+
65
+ .summary-card {
66
+ background: #f8f9fa;
67
+ padding: 20px;
68
+ border-radius: 10px;
69
+ text-align: center;
70
+ border-top: 4px solid #2d5a87;
71
+ }
72
+
73
+ .summary-card .label {
74
+ font-size: 0.9rem;
75
+ color: #666;
76
+ margin-bottom: 5px;
77
+ }
78
+
79
+ .summary-card .value {
80
+ font-size: 1.8rem;
81
+ font-weight: bold;
82
+ color: #1e3a5f;
83
+ }
84
+
85
+ .summary-card .delta {
86
+ font-size: 0.85rem;
87
+ margin-top: 5px;
88
+ }
89
+
90
+ .delta.high { color: #28a745; }
91
+ .delta.medium { color: #ffc107; }
92
+ .delta.low { color: #dc3545; }
93
+
94
+ .info-grid {
95
+ display: grid;
96
+ grid-template-columns: repeat(2, 1fr);
97
+ gap: 30px;
98
+ }
99
+
100
+ .info-box {
101
+ background: #f8f9fa;
102
+ padding: 20px;
103
+ border-radius: 10px;
104
+ }
105
+
106
+ .info-box h3 {
107
+ color: #1e3a5f;
108
+ margin-bottom: 15px;
109
+ }
110
+
111
+ .info-row {
112
+ display: flex;
113
+ justify-content: space-between;
114
+ padding: 8px 0;
115
+ border-bottom: 1px solid #eee;
116
+ }
117
+
118
+ .info-row:last-child {
119
+ border-bottom: none;
120
+ }
121
+
122
+ .vehicle-card {
123
+ background: white;
124
+ border-radius: 10px;
125
+ padding: 20px;
126
+ margin: 15px 0;
127
+ }
128
+
129
+ .vehicle-card.v1 {
130
+ border-left: 4px solid #FF4B4B;
131
+ }
132
+
133
+ .vehicle-card.v2 {
134
+ border-left: 4px solid #4B7BFF;
135
+ }
136
+
137
+ .scenario-card {
138
+ background: #f8f9fa;
139
+ border-radius: 10px;
140
+ padding: 20px;
141
+ margin: 15px 0;
142
+ border-left: 4px solid #2d5a87;
143
+ }
144
+
145
+ .scenario-header {
146
+ display: flex;
147
+ justify-content: space-between;
148
+ align-items: center;
149
+ margin-bottom: 15px;
150
+ }
151
+
152
+ .probability {
153
+ font-size: 1.5rem;
154
+ font-weight: bold;
155
+ }
156
+
157
+ .probability.high { color: #28a745; }
158
+ .probability.medium { color: #ffc107; }
159
+ .probability.low { color: #dc3545; }
160
+
161
+ .progress-bar {
162
+ background: #e9ecef;
163
+ border-radius: 5px;
164
+ height: 10px;
165
+ margin: 10px 0;
166
+ overflow: hidden;
167
+ }
168
+
169
+ .progress-fill {
170
+ background: #2d5a87;
171
+ height: 100%;
172
+ border-radius: 5px;
173
+ }
174
+
175
+ .factors-list {
176
+ list-style: none;
177
+ padding: 0;
178
+ }
179
+
180
+ .factors-list li {
181
+ padding: 5px 0;
182
+ padding-left: 20px;
183
+ position: relative;
184
+ }
185
+
186
+ .factors-list li::before {
187
+ content: "•";
188
+ color: #2d5a87;
189
+ position: absolute;
190
+ left: 0;
191
+ }
192
+
193
+ .timeline {
194
+ margin: 20px 0;
195
+ }
196
+
197
+ .timeline-item {
198
+ display: flex;
199
+ margin: 10px 0;
200
+ }
201
+
202
+ .timeline-time {
203
+ min-width: 80px;
204
+ padding: 8px 15px;
205
+ background: #ffc107;
206
+ color: white;
207
+ font-weight: bold;
208
+ text-align: center;
209
+ border-radius: 5px;
210
+ }
211
+
212
+ .timeline-time.impact {
213
+ background: #dc3545;
214
+ }
215
+
216
+ .timeline-time.after {
217
+ background: #28a745;
218
+ }
219
+
220
+ .timeline-event {
221
+ flex: 1;
222
+ padding: 8px 15px;
223
+ background: #f8f9fa;
224
+ margin-left: 10px;
225
+ border-radius: 5px;
226
+ }
227
+
228
+ .fault-assessment {
229
+ background: #fff3cd;
230
+ padding: 20px;
231
+ border-radius: 10px;
232
+ border-left: 4px solid #ffc107;
233
+ margin: 20px 0;
234
+ }
235
+
236
+ .footer {
237
+ background: #1e3a5f;
238
+ color: white;
239
+ padding: 30px 40px;
240
+ text-align: center;
241
+ }
242
+
243
+ .footer p {
244
+ opacity: 0.8;
245
+ font-size: 0.9rem;
246
+ }
247
+
248
+ @media print {
249
+ .container {
250
+ box-shadow: none;
251
+ }
252
+
253
+ .section {
254
+ page-break-inside: avoid;
255
+ }
256
+ }
257
+ </style>
258
+ </head>
259
+ <body>
260
+ <div class="container">
261
+ <!-- Header -->
262
+ <div class="header">
263
+ <h1>🚗 Traffic Accident Analysis Report</h1>
264
+ <p class="subtitle">AI-Powered Analysis using Huawei MindSpore</p>
265
+ <p style="margin-top: 15px; opacity: 0.7;">Generated: December 30, 2025 at 09:18</p>
266
+ </div>
267
+
268
+ <!-- Executive Summary -->
269
+ <div class="section">
270
+ <h2>📊 Executive Summary</h2>
271
+
272
+ <div class="summary-grid">
273
+ <div class="summary-card">
274
+ <div class="label">Most Likely Scenario</div>
275
+ <div class="value">#1</div>
276
+ <div class="delta high">95.3% probability</div>
277
+ </div>
278
+
279
+ <div class="summary-card">
280
+ <div class="label">Scenarios Generated</div>
281
+ <div class="value">5</div>
282
+ <div class="delta">AI-generated</div>
283
+ </div>
284
+
285
+ <div class="summary-card">
286
+ <div class="label">Collision Certainty</div>
287
+ <div class="value">86.0%</div>
288
+ <div class="delta high">
289
+ High
290
+ </div>
291
+ </div>
292
+
293
+ <div class="summary-card">
294
+ <div class="label">Primary Factor</div>
295
+ <div class="value" style="font-size: 1.2rem;">Failure To Yield</div>
296
+ <div class="delta">Vehicle 2</div>
297
+ </div>
298
+ </div>
299
+ </div>
300
+
301
+ <!-- Accident Details -->
302
+ <div class="section">
303
+ <h2>📍 Accident Details</h2>
304
+
305
+ <div class="info-grid">
306
+ <div class="info-box">
307
+ <h3>Location Information</h3>
308
+ <div class="info-row">
309
+ <span>Location:</span>
310
+ <strong>دوار السيف - Seef District Roundabout</strong>
311
+ </div>
312
+ <div class="info-row">
313
+ <span>Coordinates:</span>
314
+ <strong>26.2397, 50.5369</strong>
315
+ </div>
316
+ <div class="info-row">
317
+ <span>Road Type:</span>
318
+ <strong>Roundabout</strong>
319
+ </div>
320
+ </div>
321
+
322
+ <div class="info-box">
323
+ <h3>Conditions</h3>
324
+ <div class="info-row">
325
+ <span>Date/Time:</span>
326
+ <strong>2025-12-30 10:30</strong>
327
+ </div>
328
+ <div class="info-row">
329
+ <span>Weather:</span>
330
+ <strong>Clear</strong>
331
+ </div>
332
+ <div class="info-row">
333
+ <span>Road Condition:</span>
334
+ <strong>Dry</strong>
335
+ </div>
336
+ </div>
337
+ </div>
338
+ </div>
339
+
340
+ <!-- Vehicle Information -->
341
+ <div class="section">
342
+ <h2>🚙 Vehicle Information</h2>
343
+
344
+ <div class="info-grid">
345
+ <div class="vehicle-card v1">
346
+ <h3 style="color: #FF4B4B;">Vehicle 1 (Red)</h3>
347
+ <div class="info-row">
348
+ <span>Type:</span>
349
+ <strong>Sedan</strong>
350
+ </div>
351
+ <div class="info-row">
352
+ <span>Speed:</span>
353
+ <strong>45 km/h</strong>
354
+ </div>
355
+ <div class="info-row">
356
+ <span>Direction:</span>
357
+ <strong>North</strong>
358
+ </div>
359
+ <div class="info-row">
360
+ <span>Action:</span>
361
+ <strong>Entering Roundabout</strong>
362
+ </div>
363
+ <div class="info-row">
364
+ <span>Braking:</span>
365
+ <strong>No</strong>
366
+ </div>
367
+ <div class="info-row">
368
+ <span>Signaling:</span>
369
+ <strong>Yes</strong>
370
+ </div>
371
+ </div>
372
+
373
+ <div class="vehicle-card v2">
374
+ <h3 style="color: #4B7BFF;">Vehicle 2 (Blue)</h3>
375
+ <div class="info-row">
376
+ <span>Type:</span>
377
+ <strong>SUV</strong>
378
+ </div>
379
+ <div class="info-row">
380
+ <span>Speed:</span>
381
+ <strong>55 km/h</strong>
382
+ </div>
383
+ <div class="info-row">
384
+ <span>Direction:</span>
385
+ <strong>East</strong>
386
+ </div>
387
+ <div class="info-row">
388
+ <span>Action:</span>
389
+ <strong>Going Straight</strong>
390
+ </div>
391
+ <div class="info-row">
392
+ <span>Braking:</span>
393
+ <strong>Yes</strong>
394
+ </div>
395
+ <div class="info-row">
396
+ <span>Signaling:</span>
397
+ <strong>No</strong>
398
+ </div>
399
+ </div>
400
+ </div>
401
+ </div>
402
+
403
+ <!-- Generated Scenarios -->
404
+ <div class="section">
405
+ <h2>🎯 AI-Generated Scenarios</h2>
406
+
407
+
408
+ <div class="scenario-card">
409
+ <div class="scenario-header">
410
+ <div>
411
+ <h3>Scenario 1: Side Impact</h3>
412
+ </div>
413
+ <div class="probability high">
414
+ 95.3%
415
+ </div>
416
+ </div>
417
+
418
+ <p>A Sedan was struck on the side by a SUV at an intersection. The impact angle was approximately 90 degrees.</p>
419
+
420
+ <div style="margin-top: 15px;">
421
+ <strong>Analysis Metrics:</strong>
422
+ <div style="margin-top: 10px;">
423
+ <span>Collision Probability: 86.0%</span>
424
+ <div class="progress-bar">
425
+ <div class="progress-fill" style="width: 86.0440679248718%"></div>
426
+ </div>
427
+ </div>
428
+ <div>
429
+ <span>Path Overlap: 87.6%</span>
430
+ <div class="progress-bar">
431
+ <div class="progress-fill" style="width: 87.64689308290596%"></div>
432
+ </div>
433
+ </div>
434
+ <p style="margin-top: 10px;">Speed Differential: 10.0 km/h | Time to Collision: 1.16s</p>
435
+ </div>
436
+
437
+ <div style="margin-top: 15px;">
438
+ <strong>Contributing Factors:</strong>
439
+ <ul class="factors-list">
440
+ <li>Failure To Yield</li><li>Distracted Driving</li>
441
+ </ul>
442
+ </div>
443
+ </div>
444
+
445
+ <div class="scenario-card">
446
+ <div class="scenario-header">
447
+ <div>
448
+ <h3>Scenario 2: Roundabout Entry Collision</h3>
449
+ </div>
450
+ <div class="probability low">
451
+ 1.8%
452
+ </div>
453
+ </div>
454
+
455
+ <p>A Sedan entering the roundabout collided with a SUV already circulating within the roundabout.</p>
456
+
457
+ <div style="margin-top: 15px;">
458
+ <strong>Analysis Metrics:</strong>
459
+ <div style="margin-top: 10px;">
460
+ <span>Collision Probability: 86.0%</span>
461
+ <div class="progress-bar">
462
+ <div class="progress-fill" style="width: 86.0440679248718%"></div>
463
+ </div>
464
+ </div>
465
+ <div>
466
+ <span>Path Overlap: 87.6%</span>
467
+ <div class="progress-bar">
468
+ <div class="progress-fill" style="width: 87.64689308290596%"></div>
469
+ </div>
470
+ </div>
471
+ <p style="margin-top: 10px;">Speed Differential: 10.0 km/h | Time to Collision: 1.16s</p>
472
+ </div>
473
+
474
+ <div style="margin-top: 15px;">
475
+ <strong>Contributing Factors:</strong>
476
+ <ul class="factors-list">
477
+ <li>Failure To Yield</li><li>Running Red Light</li>
478
+ </ul>
479
+ </div>
480
+ </div>
481
+
482
+ <div class="scenario-card">
483
+ <div class="scenario-header">
484
+ <div>
485
+ <h3>Scenario 3: Intersection Collision</h3>
486
+ </div>
487
+ <div class="probability low">
488
+ 1.0%
489
+ </div>
490
+ </div>
491
+
492
+ <p>Both vehicles entered the intersection simultaneously, resulting in a collision at the crossing point.</p>
493
+
494
+ <div style="margin-top: 15px;">
495
+ <strong>Analysis Metrics:</strong>
496
+ <div style="margin-top: 10px;">
497
+ <span>Collision Probability: 86.0%</span>
498
+ <div class="progress-bar">
499
+ <div class="progress-fill" style="width: 86.0440679248718%"></div>
500
+ </div>
501
+ </div>
502
+ <div>
503
+ <span>Path Overlap: 87.6%</span>
504
+ <div class="progress-bar">
505
+ <div class="progress-fill" style="width: 87.64689308290596%"></div>
506
+ </div>
507
+ </div>
508
+ <p style="margin-top: 10px;">Speed Differential: 10.0 km/h | Time to Collision: 1.16s</p>
509
+ </div>
510
+
511
+ <div style="margin-top: 15px;">
512
+ <strong>Contributing Factors:</strong>
513
+ <ul class="factors-list">
514
+ <li>Failure To Yield</li><li>Running Red Light</li>
515
+ </ul>
516
+ </div>
517
+ </div>
518
+
519
+ <div class="scenario-card">
520
+ <div class="scenario-header">
521
+ <div>
522
+ <h3>Scenario 4: Lane Change Collision</h3>
523
+ </div>
524
+ <div class="probability low">
525
+ 1.0%
526
+ </div>
527
+ </div>
528
+
529
+ <p>A collision occurred when one vehicle changed lanes without properly checking for the other vehicle.</p>
530
+
531
+ <div style="margin-top: 15px;">
532
+ <strong>Analysis Metrics:</strong>
533
+ <div style="margin-top: 10px;">
534
+ <span>Collision Probability: 86.0%</span>
535
+ <div class="progress-bar">
536
+ <div class="progress-fill" style="width: 86.0440679248718%"></div>
537
+ </div>
538
+ </div>
539
+ <div>
540
+ <span>Path Overlap: 87.6%</span>
541
+ <div class="progress-bar">
542
+ <div class="progress-fill" style="width: 87.64689308290596%"></div>
543
+ </div>
544
+ </div>
545
+ <p style="margin-top: 10px;">Speed Differential: 10.0 km/h | Time to Collision: 1.16s</p>
546
+ </div>
547
+
548
+ <div style="margin-top: 15px;">
549
+ <strong>Contributing Factors:</strong>
550
+ <ul class="factors-list">
551
+ <li>Improper Lane Change</li><li>Improper Turn</li>
552
+ </ul>
553
+ </div>
554
+ </div>
555
+
556
+ <div class="scenario-card">
557
+ <div class="scenario-header">
558
+ <div>
559
+ <h3>Scenario 5: Sideswipe</h3>
560
+ </div>
561
+ <div class="probability low">
562
+ 1.0%
563
+ </div>
564
+ </div>
565
+
566
+ <p>Both vehicles were traveling in similar directions when a Sedan sideswiped a SUV during a lane change or merge.</p>
567
+
568
+ <div style="margin-top: 15px;">
569
+ <strong>Analysis Metrics:</strong>
570
+ <div style="margin-top: 10px;">
571
+ <span>Collision Probability: 86.0%</span>
572
+ <div class="progress-bar">
573
+ <div class="progress-fill" style="width: 86.0440679248718%"></div>
574
+ </div>
575
+ </div>
576
+ <div>
577
+ <span>Path Overlap: 87.6%</span>
578
+ <div class="progress-bar">
579
+ <div class="progress-fill" style="width: 87.64689308290596%"></div>
580
+ </div>
581
+ </div>
582
+ <p style="margin-top: 10px;">Speed Differential: 10.0 km/h | Time to Collision: 1.16s</p>
583
+ </div>
584
+
585
+ <div style="margin-top: 15px;">
586
+ <strong>Contributing Factors:</strong>
587
+ <ul class="factors-list">
588
+ <li>Improper Lane Change</li>
589
+ </ul>
590
+ </div>
591
+ </div>
592
+
593
+ </div>
594
+
595
+ <!-- Fault Assessment -->
596
+ <div class="section">
597
+ <h2>⚖️ Preliminary Fault Assessment</h2>
598
+
599
+ <div class="fault-assessment">
600
+ <h3>⚠️ Disclaimer</h3>
601
+ <p>This is a preliminary AI-generated assessment for reference purposes only. Final fault determination should be made by qualified traffic authorities based on comprehensive investigation.</p>
602
+ </div>
603
+
604
+ <div class="info-grid" style="margin-top: 20px;">
605
+ <div class="info-box">
606
+ <h3>Contribution Analysis</h3>
607
+ <div style="margin: 15px 0;">
608
+ <span style="color: #FF4B4B;">Vehicle 1: 36.4%</span>
609
+ <div class="progress-bar">
610
+ <div class="progress-fill" style="width: 36.44859813084113%; background: #FF4B4B;"></div>
611
+ </div>
612
+ </div>
613
+ <div>
614
+ <span style="color: #4B7BFF;">Vehicle 2: 63.6%</span>
615
+ <div class="progress-bar">
616
+ <div class="progress-fill" style="width: 63.55140186915889%; background: #4B7BFF;"></div>
617
+ </div>
618
+ </div>
619
+ </div>
620
+
621
+ <div class="info-box">
622
+ <h3>Assessment Summary</h3>
623
+ <div class="info-row">
624
+ <span>Higher Contribution:</span>
625
+ <strong>Vehicle 2</strong>
626
+ </div>
627
+ <div class="info-row">
628
+ <span>Primary Factor:</span>
629
+ <strong>Failure To Yield</strong>
630
+ </div>
631
+ <div class="info-row">
632
+ <span>Assessment Confidence:</span>
633
+ <strong>63.6%</strong>
634
+ </div>
635
+ </div>
636
+ </div>
637
+ </div>
638
+
639
+ <!-- Timeline -->
640
+ <div class="section">
641
+ <h2>⏱️ Estimated Accident Timeline</h2>
642
+
643
+ <div class="timeline">
644
+
645
+ <div class="timeline-item">
646
+ <div class="timeline-time ">-1.2s</div>
647
+ <div class="timeline-event">Vehicles approaching conflict zone</div>
648
+ </div>
649
+
650
+ <div class="timeline-item">
651
+ <div class="timeline-time ">-0.7s</div>
652
+ <div class="timeline-event">Vehicle paths begin to converge</div>
653
+ </div>
654
+
655
+ <div class="timeline-item">
656
+ <div class="timeline-time ">-0.3s</div>
657
+ <div class="timeline-event">Collision becomes imminent</div>
658
+ </div>
659
+
660
+ <div class="timeline-item">
661
+ <div class="timeline-time ">-0.1s</div>
662
+ <div class="timeline-event">Point of no return - evasive action no longer possible</div>
663
+ </div>
664
+
665
+ <div class="timeline-item">
666
+ <div class="timeline-time impact">+0.0s</div>
667
+ <div class="timeline-event">Impact - Collision occurs</div>
668
+ </div>
669
+
670
+ <div class="timeline-item">
671
+ <div class="timeline-time after">+0.5s</div>
672
+ <div class="timeline-event">Vehicles come to rest after impact</div>
673
+ </div>
674
+
675
+ </div>
676
+ </div>
677
+
678
+ <!-- Footer -->
679
+ <div class="footer">
680
+ <p><strong>Traffic Accident Reconstruction System</strong></p>
681
+ <p>Huawei AI Innovation Challenge 2026 | Jubail Industrial College</p>
682
+ <p style="margin-top: 10px;">Powered by Huawei MindSpore AI Framework</p>
683
+ <p style="margin-top: 15px; font-size: 0.8rem;">Report ID: 20251230_091851</p>
684
+ </div>
685
+ </div>
686
+ </body>
687
+ </html>
688
+
output/reports/accident_report_20251230_124847.html ADDED
@@ -0,0 +1,688 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ <!DOCTYPE html>
3
+ <html lang="en">
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Traffic Accident Analysis Report</title>
8
+ <style>
9
+ * {
10
+ margin: 0;
11
+ padding: 0;
12
+ box-sizing: border-box;
13
+ }
14
+
15
+ body {
16
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
17
+ line-height: 1.6;
18
+ color: #333;
19
+ background: #f5f5f5;
20
+ }
21
+
22
+ .container {
23
+ max-width: 1000px;
24
+ margin: 0 auto;
25
+ background: white;
26
+ box-shadow: 0 0 20px rgba(0,0,0,0.1);
27
+ }
28
+
29
+ .header {
30
+ background: linear-gradient(135deg, #1e3a5f 0%, #2d5a87 100%);
31
+ color: white;
32
+ padding: 40px;
33
+ text-align: center;
34
+ }
35
+
36
+ .header h1 {
37
+ font-size: 2.5rem;
38
+ margin-bottom: 10px;
39
+ }
40
+
41
+ .header .subtitle {
42
+ opacity: 0.9;
43
+ font-size: 1.1rem;
44
+ }
45
+
46
+ .section {
47
+ padding: 30px 40px;
48
+ border-bottom: 1px solid #eee;
49
+ }
50
+
51
+ .section h2 {
52
+ color: #1e3a5f;
53
+ margin-bottom: 20px;
54
+ padding-bottom: 10px;
55
+ border-bottom: 2px solid #2d5a87;
56
+ }
57
+
58
+ .summary-grid {
59
+ display: grid;
60
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
61
+ gap: 20px;
62
+ margin: 20px 0;
63
+ }
64
+
65
+ .summary-card {
66
+ background: #f8f9fa;
67
+ padding: 20px;
68
+ border-radius: 10px;
69
+ text-align: center;
70
+ border-top: 4px solid #2d5a87;
71
+ }
72
+
73
+ .summary-card .label {
74
+ font-size: 0.9rem;
75
+ color: #666;
76
+ margin-bottom: 5px;
77
+ }
78
+
79
+ .summary-card .value {
80
+ font-size: 1.8rem;
81
+ font-weight: bold;
82
+ color: #1e3a5f;
83
+ }
84
+
85
+ .summary-card .delta {
86
+ font-size: 0.85rem;
87
+ margin-top: 5px;
88
+ }
89
+
90
+ .delta.high { color: #28a745; }
91
+ .delta.medium { color: #ffc107; }
92
+ .delta.low { color: #dc3545; }
93
+
94
+ .info-grid {
95
+ display: grid;
96
+ grid-template-columns: repeat(2, 1fr);
97
+ gap: 30px;
98
+ }
99
+
100
+ .info-box {
101
+ background: #f8f9fa;
102
+ padding: 20px;
103
+ border-radius: 10px;
104
+ }
105
+
106
+ .info-box h3 {
107
+ color: #1e3a5f;
108
+ margin-bottom: 15px;
109
+ }
110
+
111
+ .info-row {
112
+ display: flex;
113
+ justify-content: space-between;
114
+ padding: 8px 0;
115
+ border-bottom: 1px solid #eee;
116
+ }
117
+
118
+ .info-row:last-child {
119
+ border-bottom: none;
120
+ }
121
+
122
+ .vehicle-card {
123
+ background: white;
124
+ border-radius: 10px;
125
+ padding: 20px;
126
+ margin: 15px 0;
127
+ }
128
+
129
+ .vehicle-card.v1 {
130
+ border-left: 4px solid #FF4B4B;
131
+ }
132
+
133
+ .vehicle-card.v2 {
134
+ border-left: 4px solid #4B7BFF;
135
+ }
136
+
137
+ .scenario-card {
138
+ background: #f8f9fa;
139
+ border-radius: 10px;
140
+ padding: 20px;
141
+ margin: 15px 0;
142
+ border-left: 4px solid #2d5a87;
143
+ }
144
+
145
+ .scenario-header {
146
+ display: flex;
147
+ justify-content: space-between;
148
+ align-items: center;
149
+ margin-bottom: 15px;
150
+ }
151
+
152
+ .probability {
153
+ font-size: 1.5rem;
154
+ font-weight: bold;
155
+ }
156
+
157
+ .probability.high { color: #28a745; }
158
+ .probability.medium { color: #ffc107; }
159
+ .probability.low { color: #dc3545; }
160
+
161
+ .progress-bar {
162
+ background: #e9ecef;
163
+ border-radius: 5px;
164
+ height: 10px;
165
+ margin: 10px 0;
166
+ overflow: hidden;
167
+ }
168
+
169
+ .progress-fill {
170
+ background: #2d5a87;
171
+ height: 100%;
172
+ border-radius: 5px;
173
+ }
174
+
175
+ .factors-list {
176
+ list-style: none;
177
+ padding: 0;
178
+ }
179
+
180
+ .factors-list li {
181
+ padding: 5px 0;
182
+ padding-left: 20px;
183
+ position: relative;
184
+ }
185
+
186
+ .factors-list li::before {
187
+ content: "•";
188
+ color: #2d5a87;
189
+ position: absolute;
190
+ left: 0;
191
+ }
192
+
193
+ .timeline {
194
+ margin: 20px 0;
195
+ }
196
+
197
+ .timeline-item {
198
+ display: flex;
199
+ margin: 10px 0;
200
+ }
201
+
202
+ .timeline-time {
203
+ min-width: 80px;
204
+ padding: 8px 15px;
205
+ background: #ffc107;
206
+ color: white;
207
+ font-weight: bold;
208
+ text-align: center;
209
+ border-radius: 5px;
210
+ }
211
+
212
+ .timeline-time.impact {
213
+ background: #dc3545;
214
+ }
215
+
216
+ .timeline-time.after {
217
+ background: #28a745;
218
+ }
219
+
220
+ .timeline-event {
221
+ flex: 1;
222
+ padding: 8px 15px;
223
+ background: #f8f9fa;
224
+ margin-left: 10px;
225
+ border-radius: 5px;
226
+ }
227
+
228
+ .fault-assessment {
229
+ background: #fff3cd;
230
+ padding: 20px;
231
+ border-radius: 10px;
232
+ border-left: 4px solid #ffc107;
233
+ margin: 20px 0;
234
+ }
235
+
236
+ .footer {
237
+ background: #1e3a5f;
238
+ color: white;
239
+ padding: 30px 40px;
240
+ text-align: center;
241
+ }
242
+
243
+ .footer p {
244
+ opacity: 0.8;
245
+ font-size: 0.9rem;
246
+ }
247
+
248
+ @media print {
249
+ .container {
250
+ box-shadow: none;
251
+ }
252
+
253
+ .section {
254
+ page-break-inside: avoid;
255
+ }
256
+ }
257
+ </style>
258
+ </head>
259
+ <body>
260
+ <div class="container">
261
+ <!-- Header -->
262
+ <div class="header">
263
+ <h1>🚗 Traffic Accident Analysis Report</h1>
264
+ <p class="subtitle">AI-Powered Analysis using Huawei MindSpore</p>
265
+ <p style="margin-top: 15px; opacity: 0.7;">Generated: December 30, 2025 at 12:48</p>
266
+ </div>
267
+
268
+ <!-- Executive Summary -->
269
+ <div class="section">
270
+ <h2>📊 Executive Summary</h2>
271
+
272
+ <div class="summary-grid">
273
+ <div class="summary-card">
274
+ <div class="label">Most Likely Scenario</div>
275
+ <div class="value">#1</div>
276
+ <div class="delta high">27.9% probability</div>
277
+ </div>
278
+
279
+ <div class="summary-card">
280
+ <div class="label">Scenarios Generated</div>
281
+ <div class="value">5</div>
282
+ <div class="delta">AI-generated</div>
283
+ </div>
284
+
285
+ <div class="summary-card">
286
+ <div class="label">Collision Certainty</div>
287
+ <div class="value">86.0%</div>
288
+ <div class="delta high">
289
+ High
290
+ </div>
291
+ </div>
292
+
293
+ <div class="summary-card">
294
+ <div class="label">Primary Factor</div>
295
+ <div class="value" style="font-size: 1.2rem;">Failure To Yield</div>
296
+ <div class="delta">Vehicle 2</div>
297
+ </div>
298
+ </div>
299
+ </div>
300
+
301
+ <!-- Accident Details -->
302
+ <div class="section">
303
+ <h2>📍 Accident Details</h2>
304
+
305
+ <div class="info-grid">
306
+ <div class="info-box">
307
+ <h3>Location Information</h3>
308
+ <div class="info-row">
309
+ <span>Location:</span>
310
+ <strong>دوار السيف - Seef District Roundabout</strong>
311
+ </div>
312
+ <div class="info-row">
313
+ <span>Coordinates:</span>
314
+ <strong>26.2397, 50.5369</strong>
315
+ </div>
316
+ <div class="info-row">
317
+ <span>Road Type:</span>
318
+ <strong>Roundabout</strong>
319
+ </div>
320
+ </div>
321
+
322
+ <div class="info-box">
323
+ <h3>Conditions</h3>
324
+ <div class="info-row">
325
+ <span>Date/Time:</span>
326
+ <strong>2025-12-30 10:30</strong>
327
+ </div>
328
+ <div class="info-row">
329
+ <span>Weather:</span>
330
+ <strong>Clear</strong>
331
+ </div>
332
+ <div class="info-row">
333
+ <span>Road Condition:</span>
334
+ <strong>Dry</strong>
335
+ </div>
336
+ </div>
337
+ </div>
338
+ </div>
339
+
340
+ <!-- Vehicle Information -->
341
+ <div class="section">
342
+ <h2>🚙 Vehicle Information</h2>
343
+
344
+ <div class="info-grid">
345
+ <div class="vehicle-card v1">
346
+ <h3 style="color: #FF4B4B;">Vehicle 1 (Red)</h3>
347
+ <div class="info-row">
348
+ <span>Type:</span>
349
+ <strong>Sedan</strong>
350
+ </div>
351
+ <div class="info-row">
352
+ <span>Speed:</span>
353
+ <strong>45 km/h</strong>
354
+ </div>
355
+ <div class="info-row">
356
+ <span>Direction:</span>
357
+ <strong>North</strong>
358
+ </div>
359
+ <div class="info-row">
360
+ <span>Action:</span>
361
+ <strong>Entering Roundabout</strong>
362
+ </div>
363
+ <div class="info-row">
364
+ <span>Braking:</span>
365
+ <strong>No</strong>
366
+ </div>
367
+ <div class="info-row">
368
+ <span>Signaling:</span>
369
+ <strong>Yes</strong>
370
+ </div>
371
+ </div>
372
+
373
+ <div class="vehicle-card v2">
374
+ <h3 style="color: #4B7BFF;">Vehicle 2 (Blue)</h3>
375
+ <div class="info-row">
376
+ <span>Type:</span>
377
+ <strong>SUV</strong>
378
+ </div>
379
+ <div class="info-row">
380
+ <span>Speed:</span>
381
+ <strong>55 km/h</strong>
382
+ </div>
383
+ <div class="info-row">
384
+ <span>Direction:</span>
385
+ <strong>East</strong>
386
+ </div>
387
+ <div class="info-row">
388
+ <span>Action:</span>
389
+ <strong>Going Straight</strong>
390
+ </div>
391
+ <div class="info-row">
392
+ <span>Braking:</span>
393
+ <strong>Yes</strong>
394
+ </div>
395
+ <div class="info-row">
396
+ <span>Signaling:</span>
397
+ <strong>No</strong>
398
+ </div>
399
+ </div>
400
+ </div>
401
+ </div>
402
+
403
+ <!-- Generated Scenarios -->
404
+ <div class="section">
405
+ <h2>🎯 AI-Generated Scenarios</h2>
406
+
407
+
408
+ <div class="scenario-card">
409
+ <div class="scenario-header">
410
+ <div>
411
+ <h3>Scenario 1: Side Impact</h3>
412
+ </div>
413
+ <div class="probability medium">
414
+ 27.9%
415
+ </div>
416
+ </div>
417
+
418
+ <p>A Sedan was struck on the side by a SUV at an intersection. The impact angle was approximately 90 degrees.</p>
419
+
420
+ <div style="margin-top: 15px;">
421
+ <strong>Analysis Metrics:</strong>
422
+ <div style="margin-top: 10px;">
423
+ <span>Collision Probability: 86.0%</span>
424
+ <div class="progress-bar">
425
+ <div class="progress-fill" style="width: 86.0440679248718%"></div>
426
+ </div>
427
+ </div>
428
+ <div>
429
+ <span>Path Overlap: 87.6%</span>
430
+ <div class="progress-bar">
431
+ <div class="progress-fill" style="width: 87.64689308290596%"></div>
432
+ </div>
433
+ </div>
434
+ <p style="margin-top: 10px;">Speed Differential: 10.0 km/h | Time to Collision: 1.16s</p>
435
+ </div>
436
+
437
+ <div style="margin-top: 15px;">
438
+ <strong>Contributing Factors:</strong>
439
+ <ul class="factors-list">
440
+ <li>Failure To Yield</li>
441
+ </ul>
442
+ </div>
443
+ </div>
444
+
445
+ <div class="scenario-card">
446
+ <div class="scenario-header">
447
+ <div>
448
+ <h3>Scenario 2: Roundabout Entry Collision</h3>
449
+ </div>
450
+ <div class="probability medium">
451
+ 23.0%
452
+ </div>
453
+ </div>
454
+
455
+ <p>A Sedan entering the roundabout collided with a SUV already circulating within the roundabout.</p>
456
+
457
+ <div style="margin-top: 15px;">
458
+ <strong>Analysis Metrics:</strong>
459
+ <div style="margin-top: 10px;">
460
+ <span>Collision Probability: 86.0%</span>
461
+ <div class="progress-bar">
462
+ <div class="progress-fill" style="width: 86.0440679248718%"></div>
463
+ </div>
464
+ </div>
465
+ <div>
466
+ <span>Path Overlap: 87.6%</span>
467
+ <div class="progress-bar">
468
+ <div class="progress-fill" style="width: 87.64689308290596%"></div>
469
+ </div>
470
+ </div>
471
+ <p style="margin-top: 10px;">Speed Differential: 10.0 km/h | Time to Collision: 1.16s</p>
472
+ </div>
473
+
474
+ <div style="margin-top: 15px;">
475
+ <strong>Contributing Factors:</strong>
476
+ <ul class="factors-list">
477
+ <li>Failure To Yield</li><li>Improper Turn</li>
478
+ </ul>
479
+ </div>
480
+ </div>
481
+
482
+ <div class="scenario-card">
483
+ <div class="scenario-header">
484
+ <div>
485
+ <h3>Scenario 3: Intersection Collision</h3>
486
+ </div>
487
+ <div class="probability medium">
488
+ 23.0%
489
+ </div>
490
+ </div>
491
+
492
+ <p>Both vehicles entered the intersection simultaneously, resulting in a collision at the crossing point.</p>
493
+
494
+ <div style="margin-top: 15px;">
495
+ <strong>Analysis Metrics:</strong>
496
+ <div style="margin-top: 10px;">
497
+ <span>Collision Probability: 86.0%</span>
498
+ <div class="progress-bar">
499
+ <div class="progress-fill" style="width: 86.0440679248718%"></div>
500
+ </div>
501
+ </div>
502
+ <div>
503
+ <span>Path Overlap: 87.6%</span>
504
+ <div class="progress-bar">
505
+ <div class="progress-fill" style="width: 87.64689308290596%"></div>
506
+ </div>
507
+ </div>
508
+ <p style="margin-top: 10px;">Speed Differential: 10.0 km/h | Time to Collision: 1.16s</p>
509
+ </div>
510
+
511
+ <div style="margin-top: 15px;">
512
+ <strong>Contributing Factors:</strong>
513
+ <ul class="factors-list">
514
+ <li>Failure To Yield</li>
515
+ </ul>
516
+ </div>
517
+ </div>
518
+
519
+ <div class="scenario-card">
520
+ <div class="scenario-header">
521
+ <div>
522
+ <h3>Scenario 4: Lane Change Collision</h3>
523
+ </div>
524
+ <div class="probability low">
525
+ 19.7%
526
+ </div>
527
+ </div>
528
+
529
+ <p>A collision occurred when one vehicle changed lanes without properly checking for the other vehicle.</p>
530
+
531
+ <div style="margin-top: 15px;">
532
+ <strong>Analysis Metrics:</strong>
533
+ <div style="margin-top: 10px;">
534
+ <span>Collision Probability: 86.0%</span>
535
+ <div class="progress-bar">
536
+ <div class="progress-fill" style="width: 86.0440679248718%"></div>
537
+ </div>
538
+ </div>
539
+ <div>
540
+ <span>Path Overlap: 87.6%</span>
541
+ <div class="progress-bar">
542
+ <div class="progress-fill" style="width: 87.64689308290596%"></div>
543
+ </div>
544
+ </div>
545
+ <p style="margin-top: 10px;">Speed Differential: 10.0 km/h | Time to Collision: 1.16s</p>
546
+ </div>
547
+
548
+ <div style="margin-top: 15px;">
549
+ <strong>Contributing Factors:</strong>
550
+ <ul class="factors-list">
551
+ <li>Improper Lane Change</li>
552
+ </ul>
553
+ </div>
554
+ </div>
555
+
556
+ <div class="scenario-card">
557
+ <div class="scenario-header">
558
+ <div>
559
+ <h3>Scenario 5: Sideswipe</h3>
560
+ </div>
561
+ <div class="probability low">
562
+ 6.6%
563
+ </div>
564
+ </div>
565
+
566
+ <p>Both vehicles were traveling in similar directions when a Sedan sideswiped a SUV during a lane change or merge.</p>
567
+
568
+ <div style="margin-top: 15px;">
569
+ <strong>Analysis Metrics:</strong>
570
+ <div style="margin-top: 10px;">
571
+ <span>Collision Probability: 86.0%</span>
572
+ <div class="progress-bar">
573
+ <div class="progress-fill" style="width: 86.0440679248718%"></div>
574
+ </div>
575
+ </div>
576
+ <div>
577
+ <span>Path Overlap: 87.6%</span>
578
+ <div class="progress-bar">
579
+ <div class="progress-fill" style="width: 87.64689308290596%"></div>
580
+ </div>
581
+ </div>
582
+ <p style="margin-top: 10px;">Speed Differential: 10.0 km/h | Time to Collision: 1.16s</p>
583
+ </div>
584
+
585
+ <div style="margin-top: 15px;">
586
+ <strong>Contributing Factors:</strong>
587
+ <ul class="factors-list">
588
+ <li>Improper Lane Change</li><li>Distracted Driving</li>
589
+ </ul>
590
+ </div>
591
+ </div>
592
+
593
+ </div>
594
+
595
+ <!-- Fault Assessment -->
596
+ <div class="section">
597
+ <h2>⚖️ Preliminary Fault Assessment</h2>
598
+
599
+ <div class="fault-assessment">
600
+ <h3>⚠️ Disclaimer</h3>
601
+ <p>This is a preliminary AI-generated assessment for reference purposes only. Final fault determination should be made by qualified traffic authorities based on comprehensive investigation.</p>
602
+ </div>
603
+
604
+ <div class="info-grid" style="margin-top: 20px;">
605
+ <div class="info-box">
606
+ <h3>Contribution Analysis</h3>
607
+ <div style="margin: 15px 0;">
608
+ <span style="color: #FF4B4B;">Vehicle 1: 36.4%</span>
609
+ <div class="progress-bar">
610
+ <div class="progress-fill" style="width: 36.44859813084113%; background: #FF4B4B;"></div>
611
+ </div>
612
+ </div>
613
+ <div>
614
+ <span style="color: #4B7BFF;">Vehicle 2: 63.6%</span>
615
+ <div class="progress-bar">
616
+ <div class="progress-fill" style="width: 63.55140186915889%; background: #4B7BFF;"></div>
617
+ </div>
618
+ </div>
619
+ </div>
620
+
621
+ <div class="info-box">
622
+ <h3>Assessment Summary</h3>
623
+ <div class="info-row">
624
+ <span>Higher Contribution:</span>
625
+ <strong>Vehicle 2</strong>
626
+ </div>
627
+ <div class="info-row">
628
+ <span>Primary Factor:</span>
629
+ <strong>Failure To Yield</strong>
630
+ </div>
631
+ <div class="info-row">
632
+ <span>Assessment Confidence:</span>
633
+ <strong>63.6%</strong>
634
+ </div>
635
+ </div>
636
+ </div>
637
+ </div>
638
+
639
+ <!-- Timeline -->
640
+ <div class="section">
641
+ <h2>⏱️ Estimated Accident Timeline</h2>
642
+
643
+ <div class="timeline">
644
+
645
+ <div class="timeline-item">
646
+ <div class="timeline-time ">-1.2s</div>
647
+ <div class="timeline-event">Vehicles approaching conflict zone</div>
648
+ </div>
649
+
650
+ <div class="timeline-item">
651
+ <div class="timeline-time ">-0.7s</div>
652
+ <div class="timeline-event">Vehicle paths begin to converge</div>
653
+ </div>
654
+
655
+ <div class="timeline-item">
656
+ <div class="timeline-time ">-0.3s</div>
657
+ <div class="timeline-event">Collision becomes imminent</div>
658
+ </div>
659
+
660
+ <div class="timeline-item">
661
+ <div class="timeline-time ">-0.1s</div>
662
+ <div class="timeline-event">Point of no return - evasive action no longer possible</div>
663
+ </div>
664
+
665
+ <div class="timeline-item">
666
+ <div class="timeline-time impact">+0.0s</div>
667
+ <div class="timeline-event">Impact - Collision occurs</div>
668
+ </div>
669
+
670
+ <div class="timeline-item">
671
+ <div class="timeline-time after">+0.5s</div>
672
+ <div class="timeline-event">Vehicles come to rest after impact</div>
673
+ </div>
674
+
675
+ </div>
676
+ </div>
677
+
678
+ <!-- Footer -->
679
+ <div class="footer">
680
+ <p><strong>Traffic Accident Reconstruction System</strong></p>
681
+ <p>Huawei AI Innovation Challenge 2026 | Jubail Industrial College</p>
682
+ <p style="margin-top: 10px;">Powered by Huawei MindSpore AI Framework</p>
683
+ <p style="margin-top: 15px; font-size: 0.8rem;">Report ID: 20251230_124847</p>
684
+ </div>
685
+ </div>
686
+ </body>
687
+ </html>
688
+
output/reports/accident_report_20251231_092601.html ADDED
@@ -0,0 +1,688 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ <!DOCTYPE html>
3
+ <html lang="en">
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Traffic Accident Analysis Report</title>
8
+ <style>
9
+ * {
10
+ margin: 0;
11
+ padding: 0;
12
+ box-sizing: border-box;
13
+ }
14
+
15
+ body {
16
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
17
+ line-height: 1.6;
18
+ color: #333;
19
+ background: #f5f5f5;
20
+ }
21
+
22
+ .container {
23
+ max-width: 1000px;
24
+ margin: 0 auto;
25
+ background: white;
26
+ box-shadow: 0 0 20px rgba(0,0,0,0.1);
27
+ }
28
+
29
+ .header {
30
+ background: linear-gradient(135deg, #1e3a5f 0%, #2d5a87 100%);
31
+ color: white;
32
+ padding: 40px;
33
+ text-align: center;
34
+ }
35
+
36
+ .header h1 {
37
+ font-size: 2.5rem;
38
+ margin-bottom: 10px;
39
+ }
40
+
41
+ .header .subtitle {
42
+ opacity: 0.9;
43
+ font-size: 1.1rem;
44
+ }
45
+
46
+ .section {
47
+ padding: 30px 40px;
48
+ border-bottom: 1px solid #eee;
49
+ }
50
+
51
+ .section h2 {
52
+ color: #1e3a5f;
53
+ margin-bottom: 20px;
54
+ padding-bottom: 10px;
55
+ border-bottom: 2px solid #2d5a87;
56
+ }
57
+
58
+ .summary-grid {
59
+ display: grid;
60
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
61
+ gap: 20px;
62
+ margin: 20px 0;
63
+ }
64
+
65
+ .summary-card {
66
+ background: #f8f9fa;
67
+ padding: 20px;
68
+ border-radius: 10px;
69
+ text-align: center;
70
+ border-top: 4px solid #2d5a87;
71
+ }
72
+
73
+ .summary-card .label {
74
+ font-size: 0.9rem;
75
+ color: #666;
76
+ margin-bottom: 5px;
77
+ }
78
+
79
+ .summary-card .value {
80
+ font-size: 1.8rem;
81
+ font-weight: bold;
82
+ color: #1e3a5f;
83
+ }
84
+
85
+ .summary-card .delta {
86
+ font-size: 0.85rem;
87
+ margin-top: 5px;
88
+ }
89
+
90
+ .delta.high { color: #28a745; }
91
+ .delta.medium { color: #ffc107; }
92
+ .delta.low { color: #dc3545; }
93
+
94
+ .info-grid {
95
+ display: grid;
96
+ grid-template-columns: repeat(2, 1fr);
97
+ gap: 30px;
98
+ }
99
+
100
+ .info-box {
101
+ background: #f8f9fa;
102
+ padding: 20px;
103
+ border-radius: 10px;
104
+ }
105
+
106
+ .info-box h3 {
107
+ color: #1e3a5f;
108
+ margin-bottom: 15px;
109
+ }
110
+
111
+ .info-row {
112
+ display: flex;
113
+ justify-content: space-between;
114
+ padding: 8px 0;
115
+ border-bottom: 1px solid #eee;
116
+ }
117
+
118
+ .info-row:last-child {
119
+ border-bottom: none;
120
+ }
121
+
122
+ .vehicle-card {
123
+ background: white;
124
+ border-radius: 10px;
125
+ padding: 20px;
126
+ margin: 15px 0;
127
+ }
128
+
129
+ .vehicle-card.v1 {
130
+ border-left: 4px solid #FF4B4B;
131
+ }
132
+
133
+ .vehicle-card.v2 {
134
+ border-left: 4px solid #4B7BFF;
135
+ }
136
+
137
+ .scenario-card {
138
+ background: #f8f9fa;
139
+ border-radius: 10px;
140
+ padding: 20px;
141
+ margin: 15px 0;
142
+ border-left: 4px solid #2d5a87;
143
+ }
144
+
145
+ .scenario-header {
146
+ display: flex;
147
+ justify-content: space-between;
148
+ align-items: center;
149
+ margin-bottom: 15px;
150
+ }
151
+
152
+ .probability {
153
+ font-size: 1.5rem;
154
+ font-weight: bold;
155
+ }
156
+
157
+ .probability.high { color: #28a745; }
158
+ .probability.medium { color: #ffc107; }
159
+ .probability.low { color: #dc3545; }
160
+
161
+ .progress-bar {
162
+ background: #e9ecef;
163
+ border-radius: 5px;
164
+ height: 10px;
165
+ margin: 10px 0;
166
+ overflow: hidden;
167
+ }
168
+
169
+ .progress-fill {
170
+ background: #2d5a87;
171
+ height: 100%;
172
+ border-radius: 5px;
173
+ }
174
+
175
+ .factors-list {
176
+ list-style: none;
177
+ padding: 0;
178
+ }
179
+
180
+ .factors-list li {
181
+ padding: 5px 0;
182
+ padding-left: 20px;
183
+ position: relative;
184
+ }
185
+
186
+ .factors-list li::before {
187
+ content: "•";
188
+ color: #2d5a87;
189
+ position: absolute;
190
+ left: 0;
191
+ }
192
+
193
+ .timeline {
194
+ margin: 20px 0;
195
+ }
196
+
197
+ .timeline-item {
198
+ display: flex;
199
+ margin: 10px 0;
200
+ }
201
+
202
+ .timeline-time {
203
+ min-width: 80px;
204
+ padding: 8px 15px;
205
+ background: #ffc107;
206
+ color: white;
207
+ font-weight: bold;
208
+ text-align: center;
209
+ border-radius: 5px;
210
+ }
211
+
212
+ .timeline-time.impact {
213
+ background: #dc3545;
214
+ }
215
+
216
+ .timeline-time.after {
217
+ background: #28a745;
218
+ }
219
+
220
+ .timeline-event {
221
+ flex: 1;
222
+ padding: 8px 15px;
223
+ background: #f8f9fa;
224
+ margin-left: 10px;
225
+ border-radius: 5px;
226
+ }
227
+
228
+ .fault-assessment {
229
+ background: #fff3cd;
230
+ padding: 20px;
231
+ border-radius: 10px;
232
+ border-left: 4px solid #ffc107;
233
+ margin: 20px 0;
234
+ }
235
+
236
+ .footer {
237
+ background: #1e3a5f;
238
+ color: white;
239
+ padding: 30px 40px;
240
+ text-align: center;
241
+ }
242
+
243
+ .footer p {
244
+ opacity: 0.8;
245
+ font-size: 0.9rem;
246
+ }
247
+
248
+ @media print {
249
+ .container {
250
+ box-shadow: none;
251
+ }
252
+
253
+ .section {
254
+ page-break-inside: avoid;
255
+ }
256
+ }
257
+ </style>
258
+ </head>
259
+ <body>
260
+ <div class="container">
261
+ <!-- Header -->
262
+ <div class="header">
263
+ <h1>🚗 Traffic Accident Analysis Report</h1>
264
+ <p class="subtitle">AI-Powered Analysis using Huawei MindSpore</p>
265
+ <p style="margin-top: 15px; opacity: 0.7;">Generated: December 31, 2025 at 09:26</p>
266
+ </div>
267
+
268
+ <!-- Executive Summary -->
269
+ <div class="section">
270
+ <h2>📊 Executive Summary</h2>
271
+
272
+ <div class="summary-grid">
273
+ <div class="summary-card">
274
+ <div class="label">Most Likely Scenario</div>
275
+ <div class="value">#1</div>
276
+ <div class="delta high">96.1% probability</div>
277
+ </div>
278
+
279
+ <div class="summary-card">
280
+ <div class="label">Scenarios Generated</div>
281
+ <div class="value">5</div>
282
+ <div class="delta">AI-generated</div>
283
+ </div>
284
+
285
+ <div class="summary-card">
286
+ <div class="label">Collision Certainty</div>
287
+ <div class="value">86.0%</div>
288
+ <div class="delta high">
289
+ High
290
+ </div>
291
+ </div>
292
+
293
+ <div class="summary-card">
294
+ <div class="label">Primary Factor</div>
295
+ <div class="value" style="font-size: 1.2rem;">Failure To Yield</div>
296
+ <div class="delta">Vehicle 2</div>
297
+ </div>
298
+ </div>
299
+ </div>
300
+
301
+ <!-- Accident Details -->
302
+ <div class="section">
303
+ <h2>📍 Accident Details</h2>
304
+
305
+ <div class="info-grid">
306
+ <div class="info-box">
307
+ <h3>Location Information</h3>
308
+ <div class="info-row">
309
+ <span>Location:</span>
310
+ <strong>دوار السيف - Seef District Roundabout</strong>
311
+ </div>
312
+ <div class="info-row">
313
+ <span>Coordinates:</span>
314
+ <strong>26.2397, 50.5369</strong>
315
+ </div>
316
+ <div class="info-row">
317
+ <span>Road Type:</span>
318
+ <strong>Roundabout</strong>
319
+ </div>
320
+ </div>
321
+
322
+ <div class="info-box">
323
+ <h3>Conditions</h3>
324
+ <div class="info-row">
325
+ <span>Date/Time:</span>
326
+ <strong>2025-12-30 10:30</strong>
327
+ </div>
328
+ <div class="info-row">
329
+ <span>Weather:</span>
330
+ <strong>Clear</strong>
331
+ </div>
332
+ <div class="info-row">
333
+ <span>Road Condition:</span>
334
+ <strong>Dry</strong>
335
+ </div>
336
+ </div>
337
+ </div>
338
+ </div>
339
+
340
+ <!-- Vehicle Information -->
341
+ <div class="section">
342
+ <h2>🚙 Vehicle Information</h2>
343
+
344
+ <div class="info-grid">
345
+ <div class="vehicle-card v1">
346
+ <h3 style="color: #FF4B4B;">Vehicle 1 (Red)</h3>
347
+ <div class="info-row">
348
+ <span>Type:</span>
349
+ <strong>Sedan</strong>
350
+ </div>
351
+ <div class="info-row">
352
+ <span>Speed:</span>
353
+ <strong>45 km/h</strong>
354
+ </div>
355
+ <div class="info-row">
356
+ <span>Direction:</span>
357
+ <strong>North</strong>
358
+ </div>
359
+ <div class="info-row">
360
+ <span>Action:</span>
361
+ <strong>Entering Roundabout</strong>
362
+ </div>
363
+ <div class="info-row">
364
+ <span>Braking:</span>
365
+ <strong>No</strong>
366
+ </div>
367
+ <div class="info-row">
368
+ <span>Signaling:</span>
369
+ <strong>Yes</strong>
370
+ </div>
371
+ </div>
372
+
373
+ <div class="vehicle-card v2">
374
+ <h3 style="color: #4B7BFF;">Vehicle 2 (Blue)</h3>
375
+ <div class="info-row">
376
+ <span>Type:</span>
377
+ <strong>SUV</strong>
378
+ </div>
379
+ <div class="info-row">
380
+ <span>Speed:</span>
381
+ <strong>55 km/h</strong>
382
+ </div>
383
+ <div class="info-row">
384
+ <span>Direction:</span>
385
+ <strong>East</strong>
386
+ </div>
387
+ <div class="info-row">
388
+ <span>Action:</span>
389
+ <strong>Going Straight</strong>
390
+ </div>
391
+ <div class="info-row">
392
+ <span>Braking:</span>
393
+ <strong>Yes</strong>
394
+ </div>
395
+ <div class="info-row">
396
+ <span>Signaling:</span>
397
+ <strong>No</strong>
398
+ </div>
399
+ </div>
400
+ </div>
401
+ </div>
402
+
403
+ <!-- Generated Scenarios -->
404
+ <div class="section">
405
+ <h2>🎯 AI-Generated Scenarios</h2>
406
+
407
+
408
+ <div class="scenario-card">
409
+ <div class="scenario-header">
410
+ <div>
411
+ <h3>Scenario 1: Side Impact</h3>
412
+ </div>
413
+ <div class="probability high">
414
+ 96.1%
415
+ </div>
416
+ </div>
417
+
418
+ <p>A Sedan was struck on the side by a SUV at an intersection. The impact angle was approximately 90 degrees.</p>
419
+
420
+ <div style="margin-top: 15px;">
421
+ <strong>Analysis Metrics:</strong>
422
+ <div style="margin-top: 10px;">
423
+ <span>Collision Probability: 86.0%</span>
424
+ <div class="progress-bar">
425
+ <div class="progress-fill" style="width: 86.0440679248718%"></div>
426
+ </div>
427
+ </div>
428
+ <div>
429
+ <span>Path Overlap: 87.6%</span>
430
+ <div class="progress-bar">
431
+ <div class="progress-fill" style="width: 87.64689308290596%"></div>
432
+ </div>
433
+ </div>
434
+ <p style="margin-top: 10px;">Speed Differential: 10.0 km/h | Time to Collision: 1.16s</p>
435
+ </div>
436
+
437
+ <div style="margin-top: 15px;">
438
+ <strong>Contributing Factors:</strong>
439
+ <ul class="factors-list">
440
+ <li>Failure To Yield</li>
441
+ </ul>
442
+ </div>
443
+ </div>
444
+
445
+ <div class="scenario-card">
446
+ <div class="scenario-header">
447
+ <div>
448
+ <h3>Scenario 2: Rear End Collision</h3>
449
+ </div>
450
+ <div class="probability low">
451
+ 1.0%
452
+ </div>
453
+ </div>
454
+
455
+ <p>A SUV traveling at 55 km/h rear-ended a Sedan traveling at 45 km/h in the same direction.</p>
456
+
457
+ <div style="margin-top: 15px;">
458
+ <strong>Analysis Metrics:</strong>
459
+ <div style="margin-top: 10px;">
460
+ <span>Collision Probability: 86.0%</span>
461
+ <div class="progress-bar">
462
+ <div class="progress-fill" style="width: 86.0440679248718%"></div>
463
+ </div>
464
+ </div>
465
+ <div>
466
+ <span>Path Overlap: 87.6%</span>
467
+ <div class="progress-bar">
468
+ <div class="progress-fill" style="width: 87.64689308290596%"></div>
469
+ </div>
470
+ </div>
471
+ <p style="margin-top: 10px;">Speed Differential: 10.0 km/h | Time to Collision: 1.16s</p>
472
+ </div>
473
+
474
+ <div style="margin-top: 15px;">
475
+ <strong>Contributing Factors:</strong>
476
+ <ul class="factors-list">
477
+ <li>Following Too Closely</li>
478
+ </ul>
479
+ </div>
480
+ </div>
481
+
482
+ <div class="scenario-card">
483
+ <div class="scenario-header">
484
+ <div>
485
+ <h3>Scenario 3: Roundabout Entry Collision</h3>
486
+ </div>
487
+ <div class="probability low">
488
+ 1.0%
489
+ </div>
490
+ </div>
491
+
492
+ <p>A Sedan entering the roundabout collided with a SUV already circulating within the roundabout.</p>
493
+
494
+ <div style="margin-top: 15px;">
495
+ <strong>Analysis Metrics:</strong>
496
+ <div style="margin-top: 10px;">
497
+ <span>Collision Probability: 86.0%</span>
498
+ <div class="progress-bar">
499
+ <div class="progress-fill" style="width: 86.0440679248718%"></div>
500
+ </div>
501
+ </div>
502
+ <div>
503
+ <span>Path Overlap: 87.6%</span>
504
+ <div class="progress-bar">
505
+ <div class="progress-fill" style="width: 87.64689308290596%"></div>
506
+ </div>
507
+ </div>
508
+ <p style="margin-top: 10px;">Speed Differential: 10.0 km/h | Time to Collision: 1.16s</p>
509
+ </div>
510
+
511
+ <div style="margin-top: 15px;">
512
+ <strong>Contributing Factors:</strong>
513
+ <ul class="factors-list">
514
+ <li>Failure To Yield</li>
515
+ </ul>
516
+ </div>
517
+ </div>
518
+
519
+ <div class="scenario-card">
520
+ <div class="scenario-header">
521
+ <div>
522
+ <h3>Scenario 4: Lane Change Collision</h3>
523
+ </div>
524
+ <div class="probability low">
525
+ 1.0%
526
+ </div>
527
+ </div>
528
+
529
+ <p>A collision occurred when one vehicle changed lanes without properly checking for the other vehicle.</p>
530
+
531
+ <div style="margin-top: 15px;">
532
+ <strong>Analysis Metrics:</strong>
533
+ <div style="margin-top: 10px;">
534
+ <span>Collision Probability: 86.0%</span>
535
+ <div class="progress-bar">
536
+ <div class="progress-fill" style="width: 86.0440679248718%"></div>
537
+ </div>
538
+ </div>
539
+ <div>
540
+ <span>Path Overlap: 87.6%</span>
541
+ <div class="progress-bar">
542
+ <div class="progress-fill" style="width: 87.64689308290596%"></div>
543
+ </div>
544
+ </div>
545
+ <p style="margin-top: 10px;">Speed Differential: 10.0 km/h | Time to Collision: 1.16s</p>
546
+ </div>
547
+
548
+ <div style="margin-top: 15px;">
549
+ <strong>Contributing Factors:</strong>
550
+ <ul class="factors-list">
551
+ <li>Improper Lane Change</li><li>Improper Turn</li>
552
+ </ul>
553
+ </div>
554
+ </div>
555
+
556
+ <div class="scenario-card">
557
+ <div class="scenario-header">
558
+ <div>
559
+ <h3>Scenario 5: Head On Collision</h3>
560
+ </div>
561
+ <div class="probability low">
562
+ 1.0%
563
+ </div>
564
+ </div>
565
+
566
+ <p>A Sedan traveling north at 45 km/h collided head-on with a SUV traveling east at 55 km/h.</p>
567
+
568
+ <div style="margin-top: 15px;">
569
+ <strong>Analysis Metrics:</strong>
570
+ <div style="margin-top: 10px;">
571
+ <span>Collision Probability: 86.0%</span>
572
+ <div class="progress-bar">
573
+ <div class="progress-fill" style="width: 86.0440679248718%"></div>
574
+ </div>
575
+ </div>
576
+ <div>
577
+ <span>Path Overlap: 87.6%</span>
578
+ <div class="progress-bar">
579
+ <div class="progress-fill" style="width: 87.64689308290596%"></div>
580
+ </div>
581
+ </div>
582
+ <p style="margin-top: 10px;">Speed Differential: 10.0 km/h | Time to Collision: 1.16s</p>
583
+ </div>
584
+
585
+ <div style="margin-top: 15px;">
586
+ <strong>Contributing Factors:</strong>
587
+ <ul class="factors-list">
588
+ <li>Improper Turn</li>
589
+ </ul>
590
+ </div>
591
+ </div>
592
+
593
+ </div>
594
+
595
+ <!-- Fault Assessment -->
596
+ <div class="section">
597
+ <h2>⚖️ Preliminary Fault Assessment</h2>
598
+
599
+ <div class="fault-assessment">
600
+ <h3>⚠️ Disclaimer</h3>
601
+ <p>This is a preliminary AI-generated assessment for reference purposes only. Final fault determination should be made by qualified traffic authorities based on comprehensive investigation.</p>
602
+ </div>
603
+
604
+ <div class="info-grid" style="margin-top: 20px;">
605
+ <div class="info-box">
606
+ <h3>Contribution Analysis</h3>
607
+ <div style="margin: 15px 0;">
608
+ <span style="color: #FF4B4B;">Vehicle 1: 36.4%</span>
609
+ <div class="progress-bar">
610
+ <div class="progress-fill" style="width: 36.44859813084113%; background: #FF4B4B;"></div>
611
+ </div>
612
+ </div>
613
+ <div>
614
+ <span style="color: #4B7BFF;">Vehicle 2: 63.6%</span>
615
+ <div class="progress-bar">
616
+ <div class="progress-fill" style="width: 63.55140186915889%; background: #4B7BFF;"></div>
617
+ </div>
618
+ </div>
619
+ </div>
620
+
621
+ <div class="info-box">
622
+ <h3>Assessment Summary</h3>
623
+ <div class="info-row">
624
+ <span>Higher Contribution:</span>
625
+ <strong>Vehicle 2</strong>
626
+ </div>
627
+ <div class="info-row">
628
+ <span>Primary Factor:</span>
629
+ <strong>Failure To Yield</strong>
630
+ </div>
631
+ <div class="info-row">
632
+ <span>Assessment Confidence:</span>
633
+ <strong>63.6%</strong>
634
+ </div>
635
+ </div>
636
+ </div>
637
+ </div>
638
+
639
+ <!-- Timeline -->
640
+ <div class="section">
641
+ <h2>⏱️ Estimated Accident Timeline</h2>
642
+
643
+ <div class="timeline">
644
+
645
+ <div class="timeline-item">
646
+ <div class="timeline-time ">-1.2s</div>
647
+ <div class="timeline-event">Vehicles approaching conflict zone</div>
648
+ </div>
649
+
650
+ <div class="timeline-item">
651
+ <div class="timeline-time ">-0.7s</div>
652
+ <div class="timeline-event">Vehicle paths begin to converge</div>
653
+ </div>
654
+
655
+ <div class="timeline-item">
656
+ <div class="timeline-time ">-0.3s</div>
657
+ <div class="timeline-event">Collision becomes imminent</div>
658
+ </div>
659
+
660
+ <div class="timeline-item">
661
+ <div class="timeline-time ">-0.1s</div>
662
+ <div class="timeline-event">Point of no return - evasive action no longer possible</div>
663
+ </div>
664
+
665
+ <div class="timeline-item">
666
+ <div class="timeline-time impact">+0.0s</div>
667
+ <div class="timeline-event">Impact - Collision occurs</div>
668
+ </div>
669
+
670
+ <div class="timeline-item">
671
+ <div class="timeline-time after">+0.5s</div>
672
+ <div class="timeline-event">Vehicles come to rest after impact</div>
673
+ </div>
674
+
675
+ </div>
676
+ </div>
677
+
678
+ <!-- Footer -->
679
+ <div class="footer">
680
+ <p><strong>Traffic Accident Reconstruction System</strong></p>
681
+ <p>Huawei AI Innovation Challenge 2026 | Jubail Industrial College</p>
682
+ <p style="margin-top: 10px;">Powered by Huawei MindSpore AI Framework</p>
683
+ <p style="margin-top: 15px; font-size: 0.8rem;">Report ID: 20251231_092601</p>
684
+ </div>
685
+ </div>
686
+ </body>
687
+ </html>
688
+
requirements.txt CHANGED
@@ -1,31 +1,31 @@
1
- # CrashLens - Traffic Accident Reconstruction System
2
  # Huawei AI Innovation Challenge 2026
 
3
 
4
- # Core Web Framework
5
- streamlit==1.29.0
6
 
7
- # AI/ML - ONNX Runtime
8
- onnxruntime==1.16.3
9
-
10
- # Fix protobuf version conflict
11
  protobuf>=3.20.0,<4.0.0
12
 
13
- # Map - FIXED VERSIONS (this fixes the WebSocket crash!)
14
- folium==0.14.0
15
- streamlit-folium==0.13.0
 
 
 
16
 
17
  # Data Processing
18
- numpy==1.24.3
19
- pandas==2.0.3
20
 
21
  # Visualization
22
- matplotlib==3.7.3
23
- plotly==5.18.0
24
 
25
  # Report Generation
26
- jinja2==3.1.2
27
 
28
  # Utilities
29
- python-dateutil==2.8.2
30
- requests==2.31.0
31
- psutil==5.9.6
 
1
+ # Traffic Accident Reconstruction System
2
  # Huawei AI Innovation Challenge 2026
3
+ # =====================================
4
 
5
+ # AI/ML - ONNX Runtime (works on Windows/Mac/Linux!)
6
+ onnxruntime>=1.16.0
7
 
8
+ # IMPORTANT: Fix protobuf version conflict
 
 
 
9
  protobuf>=3.20.0,<4.0.0
10
 
11
+ # Web Interface
12
+ streamlit>=1.29.0
13
+ streamlit-folium>=0.15.0
14
+
15
+ # Map and Geographic Data
16
+ folium>=0.15.0
17
 
18
  # Data Processing
19
+ numpy>=1.24.0
20
+ pandas>=2.0.0
21
 
22
  # Visualization
23
+ matplotlib>=3.7.0
24
+ plotly>=5.18.0
25
 
26
  # Report Generation
27
+ jinja2>=3.1.0
28
 
29
  # Utilities
30
+ python-dateutil>=2.8.0
31
+ requests>=2.31.0
 
ui/map_viewer.py CHANGED
@@ -1,20 +1,33 @@
1
  """
2
- Map Viewer Component - FIXED VERSION
3
- Uses folium_static to avoid WebSocket crashes
 
4
  """
5
 
6
  import streamlit as st
7
  import folium
8
- from streamlit_folium import folium_static
 
 
 
9
 
10
  from config import CASE_STUDY_LOCATION, MAP_CONFIG, COLORS
11
 
12
 
13
  def create_base_map(location: dict = None) -> folium.Map:
14
- """Create a base Folium map."""
 
 
 
 
 
 
 
 
15
  if location is None:
16
  location = CASE_STUDY_LOCATION
17
 
 
18
  m = folium.Map(
19
  location=[location['latitude'], location['longitude']],
20
  zoom_start=MAP_CONFIG['default_zoom'],
@@ -29,11 +42,63 @@ def create_base_map(location: dict = None) -> folium.Map:
29
  tooltip="Accident Location"
30
  ).add_to(m)
31
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  return m
33
 
34
 
35
  def add_vehicle_path(m: folium.Map, path: list, vehicle_id: int) -> folium.Map:
36
- """Add vehicle path to map."""
 
 
 
 
 
 
 
 
 
 
37
  if not path or len(path) < 2:
38
  return m
39
 
@@ -45,28 +110,59 @@ def add_vehicle_path(m: folium.Map, path: list, vehicle_id: int) -> folium.Map:
45
  color=color,
46
  weight=4,
47
  opacity=0.8,
48
- popup=f"Vehicle {vehicle_id} Path"
 
49
  ).add_to(m)
50
 
51
  # Add start marker
52
  folium.Marker(
53
  location=path[0],
54
  popup=f"Vehicle {vehicle_id} Start",
55
- icon=folium.Icon(color='green' if vehicle_id == 1 else 'blue', icon='play')
 
 
 
56
  ).add_to(m)
57
 
58
- # Add end marker
59
  folium.Marker(
60
  location=path[-1],
61
  popup=f"Vehicle {vehicle_id} End",
62
- icon=folium.Icon(color='red' if vehicle_id == 1 else 'darkblue', icon='stop')
 
 
 
63
  ).add_to(m)
64
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  return m
66
 
67
 
68
  def add_collision_point(m: folium.Map, collision_point: list) -> folium.Map:
69
- """Add collision point marker."""
 
 
 
 
 
 
 
 
 
70
  if not collision_point:
71
  return m
72
 
@@ -77,6 +173,7 @@ def add_collision_point(m: folium.Map, collision_point: list) -> folium.Map:
77
  tooltip="💥 Collision Point"
78
  ).add_to(m)
79
 
 
80
  folium.Circle(
81
  location=collision_point,
82
  radius=10,
@@ -91,23 +188,212 @@ def add_collision_point(m: folium.Map, collision_point: list) -> folium.Map:
91
 
92
  def render_map_section(vehicle_id: int = None):
93
  """
94
- Render the map section - FIXED VERSION (static display)
95
- Shows accident location on map
 
 
96
  """
97
  location = st.session_state.accident_info['location']
98
 
99
- # Create map
100
  m = create_base_map(location)
101
 
102
- # Display static map (NO CRASHES!)
103
- folium_static(m, width=700, height=400)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
 
105
- st.success(f"📍 Accident Location: {location.get('name', 'Unknown')}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
 
107
 
108
  def render_results_map(scenarios: list, selected_scenario: int = 0):
109
  """
110
  Render a map showing analysis results for a specific scenario.
 
 
 
 
111
  """
112
  if not scenarios:
113
  st.warning("No scenarios to display")
@@ -119,23 +405,23 @@ def render_results_map(scenarios: list, selected_scenario: int = 0):
119
  # Create map
120
  m = create_base_map(location)
121
 
122
- # Add vehicle paths if available
123
  if scenario.get('vehicle_1_path'):
124
  m = add_vehicle_path(m, scenario['vehicle_1_path'], 1)
125
 
126
  if scenario.get('vehicle_2_path'):
127
  m = add_vehicle_path(m, scenario['vehicle_2_path'], 2)
128
 
129
- # Add collision point if available
130
  if scenario.get('collision_point'):
131
  m = add_collision_point(m, scenario['collision_point'])
132
 
133
- # Add scenario info
134
  info_html = f"""
135
  <div style="width: 200px;">
136
  <h4>Scenario {selected_scenario + 1}</h4>
137
  <p><b>Probability:</b> {scenario.get('probability', 0)*100:.1f}%</p>
138
- <p><b>Type:</b> {scenario.get('type', 'Unknown').replace('_', ' ').title()}</p>
139
  </div>
140
  """
141
 
@@ -145,5 +431,5 @@ def render_results_map(scenarios: list, selected_scenario: int = 0):
145
  icon=folium.Icon(color='purple', icon='info-sign')
146
  ).add_to(m)
147
 
148
- # Display static map
149
  folium_static(m, width=700, height=500)
 
1
  """
2
+ Map Viewer Component
3
+ ====================
4
+ Handles map display and vehicle path drawing using Folium.
5
  """
6
 
7
  import streamlit as st
8
  import folium
9
+ from streamlit_folium import st_folium, folium_static
10
+ from folium.plugins import Draw
11
+ import json
12
+ from typing import List
13
 
14
  from config import CASE_STUDY_LOCATION, MAP_CONFIG, COLORS
15
 
16
 
17
  def create_base_map(location: dict = None) -> folium.Map:
18
+ """
19
+ Create a base Folium map centered on the accident location.
20
+
21
+ Args:
22
+ location: Dictionary with latitude, longitude, and name
23
+
24
+ Returns:
25
+ Folium Map object
26
+ """
27
  if location is None:
28
  location = CASE_STUDY_LOCATION
29
 
30
+ # Create map
31
  m = folium.Map(
32
  location=[location['latitude'], location['longitude']],
33
  zoom_start=MAP_CONFIG['default_zoom'],
 
42
  tooltip="Accident Location"
43
  ).add_to(m)
44
 
45
+ # Add circle to show area of interest
46
+ folium.Circle(
47
+ location=[location['latitude'], location['longitude']],
48
+ radius=location.get('radius_meters', 200),
49
+ color='blue',
50
+ fill=True,
51
+ fill_opacity=0.1,
52
+ popup="Analysis Area"
53
+ ).add_to(m)
54
+
55
+ return m
56
+
57
+
58
+ def add_draw_control(m: folium.Map) -> folium.Map:
59
+ """
60
+ Add drawing controls to the map for path definition.
61
+
62
+ Args:
63
+ m: Folium Map object
64
+
65
+ Returns:
66
+ Folium Map with draw controls
67
+ """
68
+ draw = Draw(
69
+ draw_options={
70
+ 'polyline': {
71
+ 'allowIntersection': True,
72
+ 'shapeOptions': {
73
+ 'color': '#FF4B4B',
74
+ 'weight': 4
75
+ }
76
+ },
77
+ 'polygon': False,
78
+ 'circle': False,
79
+ 'rectangle': False,
80
+ 'circlemarker': False,
81
+ 'marker': True
82
+ },
83
+ edit_options={'edit': True, 'remove': True}
84
+ )
85
+ draw.add_to(m)
86
+
87
  return m
88
 
89
 
90
  def add_vehicle_path(m: folium.Map, path: list, vehicle_id: int) -> folium.Map:
91
+ """
92
+ Add a vehicle path to the map.
93
+
94
+ Args:
95
+ m: Folium Map object
96
+ path: List of [lat, lng] coordinates
97
+ vehicle_id: 1 or 2 to determine color
98
+
99
+ Returns:
100
+ Folium Map with vehicle path
101
+ """
102
  if not path or len(path) < 2:
103
  return m
104
 
 
110
  color=color,
111
  weight=4,
112
  opacity=0.8,
113
+ popup=f"Vehicle {vehicle_id} Path",
114
+ tooltip=f"Vehicle {vehicle_id}"
115
  ).add_to(m)
116
 
117
  # Add start marker
118
  folium.Marker(
119
  location=path[0],
120
  popup=f"Vehicle {vehicle_id} Start",
121
+ icon=folium.Icon(
122
+ color='green' if vehicle_id == 1 else 'blue',
123
+ icon='play'
124
+ )
125
  ).add_to(m)
126
 
127
+ # Add end marker
128
  folium.Marker(
129
  location=path[-1],
130
  popup=f"Vehicle {vehicle_id} End",
131
+ icon=folium.Icon(
132
+ color='red' if vehicle_id == 1 else 'darkblue',
133
+ icon='stop'
134
+ )
135
  ).add_to(m)
136
 
137
+ # Add direction arrows
138
+ for i in range(len(path) - 1):
139
+ mid_lat = (path[i][0] + path[i+1][0]) / 2
140
+ mid_lng = (path[i][1] + path[i+1][1]) / 2
141
+
142
+ folium.RegularPolygonMarker(
143
+ location=[mid_lat, mid_lng],
144
+ number_of_sides=3,
145
+ radius=8,
146
+ color=color,
147
+ fill=True,
148
+ fill_color=color,
149
+ fill_opacity=0.7
150
+ ).add_to(m)
151
+
152
  return m
153
 
154
 
155
  def add_collision_point(m: folium.Map, collision_point: list) -> folium.Map:
156
+ """
157
+ Add a collision point marker to the map.
158
+
159
+ Args:
160
+ m: Folium Map object
161
+ collision_point: [lat, lng] of collision
162
+
163
+ Returns:
164
+ Folium Map with collision marker
165
+ """
166
  if not collision_point:
167
  return m
168
 
 
173
  tooltip="💥 Collision Point"
174
  ).add_to(m)
175
 
176
+ # Add impact radius
177
  folium.Circle(
178
  location=collision_point,
179
  radius=10,
 
188
 
189
  def render_map_section(vehicle_id: int = None):
190
  """
191
+ Render the map section in Streamlit.
192
+
193
+ Args:
194
+ vehicle_id: If provided, enables path drawing for that vehicle
195
  """
196
  location = st.session_state.accident_info['location']
197
 
198
+ # Create base map
199
  m = create_base_map(location)
200
 
201
+ # Add existing vehicle paths
202
+ if st.session_state.vehicle_1.get('path'):
203
+ m = add_vehicle_path(m, st.session_state.vehicle_1['path'], 1)
204
+
205
+ if st.session_state.vehicle_2.get('path'):
206
+ m = add_vehicle_path(m, st.session_state.vehicle_2['path'], 2)
207
+
208
+ # Add draw controls if editing a specific vehicle
209
+ if vehicle_id:
210
+ m = add_draw_control(m)
211
+
212
+ st.info(f"🖊️ Draw the path for Vehicle {vehicle_id} on the map. Click points to create a path, then click 'Finish' to complete.")
213
+
214
+ # Render map and get drawing data
215
+ map_data = st_folium(
216
+ m,
217
+ width=700,
218
+ height=500,
219
+ returned_objects=["last_active_drawing", "all_drawings"]
220
+ )
221
+
222
+ # Process drawn paths
223
+ if vehicle_id and map_data and map_data.get('last_active_drawing'):
224
+ drawing = map_data['last_active_drawing']
225
+
226
+ if drawing.get('geometry', {}).get('type') == 'LineString':
227
+ coords = drawing['geometry']['coordinates']
228
+ # Convert from [lng, lat] to [lat, lng]
229
+ path = [[c[1], c[0]] for c in coords]
230
+
231
+ if vehicle_id == 1:
232
+ st.session_state.vehicle_1['path'] = path
233
+ else:
234
+ st.session_state.vehicle_2['path'] = path
235
+
236
+ st.success(f"✅ Path saved for Vehicle {vehicle_id}!")
237
 
238
+ # Show path info and preset options
239
+ if vehicle_id:
240
+ vehicle_key = f'vehicle_{vehicle_id}'
241
+ current_path = st.session_state[vehicle_key].get('path', [])
242
+
243
+ if current_path:
244
+ st.success(f"**✅ Path defined:** {len(current_path)} points")
245
+ else:
246
+ st.warning("⚠️ No path drawn yet. Use the drawing tools on the map OR select a preset path below.")
247
+
248
+ # Preset path options for roundabout
249
+ st.markdown("---")
250
+ st.markdown("**🛣️ Quick Path Selection (Roundabout)**")
251
+
252
+ preset_col1, preset_col2 = st.columns(2)
253
+
254
+ with preset_col1:
255
+ entry_direction = st.selectbox(
256
+ f"Entry Direction (V{vehicle_id})",
257
+ options=['north', 'south', 'east', 'west'],
258
+ key=f"entry_dir_{vehicle_id}"
259
+ )
260
+
261
+ with preset_col2:
262
+ exit_direction = st.selectbox(
263
+ f"Exit Direction (V{vehicle_id})",
264
+ options=['north', 'south', 'east', 'west'],
265
+ index=2 if entry_direction == 'north' else 0,
266
+ key=f"exit_dir_{vehicle_id}"
267
+ )
268
+
269
+ if st.button(f"🔄 Generate Path for Vehicle {vehicle_id}", key=f"gen_path_{vehicle_id}"):
270
+ generated_path = generate_roundabout_path(
271
+ location['latitude'],
272
+ location['longitude'],
273
+ entry_direction,
274
+ exit_direction
275
+ )
276
+
277
+ if vehicle_id == 1:
278
+ st.session_state.vehicle_1['path'] = generated_path
279
+ else:
280
+ st.session_state.vehicle_2['path'] = generated_path
281
+
282
+ st.success(f"✅ Path generated: {entry_direction.title()} → {exit_direction.title()}")
283
+ st.rerun()
284
+
285
+ # Manual path input as fallback
286
+ with st.expander("📝 Or enter path manually"):
287
+ st.write("Enter coordinates as: lat1,lng1;lat2,lng2;...")
288
+ manual_path = st.text_input(
289
+ "Path coordinates",
290
+ key=f"manual_path_{vehicle_id}",
291
+ placeholder="26.2397,50.5369;26.2400,50.5372"
292
+ )
293
+
294
+ if st.button(f"Set Manual Path", key=f"set_path_{vehicle_id}"):
295
+ if manual_path:
296
+ try:
297
+ path = []
298
+ for point in manual_path.split(';'):
299
+ lat, lng = point.strip().split(',')
300
+ path.append([float(lat), float(lng)])
301
+
302
+ if vehicle_id == 1:
303
+ st.session_state.vehicle_1['path'] = path
304
+ else:
305
+ st.session_state.vehicle_2['path'] = path
306
+
307
+ st.success(f"Path set with {len(path)} points!")
308
+ st.rerun()
309
+ except Exception as e:
310
+ st.error(f"Invalid format: {e}")
311
+
312
+
313
+ def generate_roundabout_path(
314
+ center_lat: float,
315
+ center_lng: float,
316
+ entry_direction: str,
317
+ exit_direction: str
318
+ ) -> List[List[float]]:
319
+ """
320
+ Generate a realistic path through a roundabout.
321
+
322
+ Args:
323
+ center_lat: Center latitude of roundabout
324
+ center_lng: Center longitude of roundabout
325
+ entry_direction: Direction vehicle enters from
326
+ exit_direction: Direction vehicle exits to
327
+
328
+ Returns:
329
+ List of [lat, lng] coordinates forming the path
330
+ """
331
+ import math
332
+
333
+ # Roundabout parameters
334
+ approach_distance = 0.0015 # ~150 meters
335
+ roundabout_radius = 0.0004 # ~40 meters
336
+
337
+ # Direction to angle mapping (0 = North, clockwise)
338
+ dir_to_angle = {
339
+ 'north': 90, # Top
340
+ 'east': 0, # Right
341
+ 'south': 270, # Bottom
342
+ 'west': 180 # Left
343
+ }
344
+
345
+ entry_angle = math.radians(dir_to_angle[entry_direction])
346
+ exit_angle = math.radians(dir_to_angle[exit_direction])
347
+
348
+ path = []
349
+
350
+ # Entry point (outside roundabout)
351
+ entry_lat = center_lat + approach_distance * math.sin(entry_angle)
352
+ entry_lng = center_lng + approach_distance * math.cos(entry_angle)
353
+ path.append([entry_lat, entry_lng])
354
+
355
+ # Entry to roundabout edge
356
+ edge_lat = center_lat + roundabout_radius * 1.5 * math.sin(entry_angle)
357
+ edge_lng = center_lng + roundabout_radius * 1.5 * math.cos(entry_angle)
358
+ path.append([edge_lat, edge_lng])
359
+
360
+ # Points along the roundabout (clockwise)
361
+ entry_deg = dir_to_angle[entry_direction]
362
+ exit_deg = dir_to_angle[exit_direction]
363
+
364
+ # Calculate arc (always go clockwise in roundabout)
365
+ if exit_deg <= entry_deg:
366
+ exit_deg += 360
367
+
368
+ # Add intermediate points along the arc
369
+ num_arc_points = max(2, (exit_deg - entry_deg) // 45)
370
+ for i in range(1, num_arc_points + 1):
371
+ angle = entry_deg + (exit_deg - entry_deg) * i / (num_arc_points + 1)
372
+ angle_rad = math.radians(angle)
373
+ arc_lat = center_lat + roundabout_radius * math.sin(angle_rad)
374
+ arc_lng = center_lng + roundabout_radius * math.cos(angle_rad)
375
+ path.append([arc_lat, arc_lng])
376
+
377
+ # Exit from roundabout edge
378
+ exit_edge_lat = center_lat + roundabout_radius * 1.5 * math.sin(exit_angle)
379
+ exit_edge_lng = center_lng + roundabout_radius * 1.5 * math.cos(exit_angle)
380
+ path.append([exit_edge_lat, exit_edge_lng])
381
+
382
+ # Exit point (outside roundabout)
383
+ exit_lat = center_lat + approach_distance * math.sin(exit_angle)
384
+ exit_lng = center_lng + approach_distance * math.cos(exit_angle)
385
+ path.append([exit_lat, exit_lng])
386
+
387
+ return path
388
 
389
 
390
  def render_results_map(scenarios: list, selected_scenario: int = 0):
391
  """
392
  Render a map showing analysis results for a specific scenario.
393
+
394
+ Args:
395
+ scenarios: List of generated scenarios
396
+ selected_scenario: Index of selected scenario
397
  """
398
  if not scenarios:
399
  st.warning("No scenarios to display")
 
405
  # Create map
406
  m = create_base_map(location)
407
 
408
+ # Add vehicle paths
409
  if scenario.get('vehicle_1_path'):
410
  m = add_vehicle_path(m, scenario['vehicle_1_path'], 1)
411
 
412
  if scenario.get('vehicle_2_path'):
413
  m = add_vehicle_path(m, scenario['vehicle_2_path'], 2)
414
 
415
+ # Add collision point
416
  if scenario.get('collision_point'):
417
  m = add_collision_point(m, scenario['collision_point'])
418
 
419
+ # Add scenario info popup
420
  info_html = f"""
421
  <div style="width: 200px;">
422
  <h4>Scenario {selected_scenario + 1}</h4>
423
  <p><b>Probability:</b> {scenario.get('probability', 0)*100:.1f}%</p>
424
+ <p><b>Type:</b> {scenario.get('accident_type', 'Unknown')}</p>
425
  </div>
426
  """
427
 
 
431
  icon=folium.Icon(color='purple', icon='info-sign')
432
  ).add_to(m)
433
 
434
+ # Display map
435
  folium_static(m, width=700, height=500)