Vedant2421 commited on
Commit
71dd4c8
Β·
verified Β·
1 Parent(s): 81d7170

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +1131 -38
src/streamlit_app.py CHANGED
@@ -1,40 +1,1133 @@
1
- import altair as alt
2
- import numpy as np
3
- import pandas as pd
4
  import streamlit as st
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
- """
7
- # Welcome to Streamlit!
8
-
9
- Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
10
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
11
- forums](https://discuss.streamlit.io).
12
-
13
- In the meantime, below is an example of what you can do with just a few lines of code:
14
- """
15
-
16
- num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
17
- num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
18
-
19
- indices = np.linspace(0, 1, num_points)
20
- theta = 2 * np.pi * num_turns * indices
21
- radius = indices
22
-
23
- x = radius * np.cos(theta)
24
- y = radius * np.sin(theta)
25
-
26
- df = pd.DataFrame({
27
- "x": x,
28
- "y": y,
29
- "idx": indices,
30
- "rand": np.random.randn(num_points),
31
- })
32
-
33
- st.altair_chart(alt.Chart(df, height=700, width=700)
34
- .mark_point(filled=True)
35
- .encode(
36
- x=alt.X("x", axis=None),
37
- y=alt.Y("y", axis=None),
38
- color=alt.Color("idx", legend=None, scale=alt.Scale()),
39
- size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
40
- ))
 
 
 
 
1
  import streamlit as st
2
+ import tensorflow as tf
3
+ import numpy as np
4
+ import time
5
+
6
+ # ==========================================================
7
+ # PROJECT: Machine Fault Detection using CNN
8
+ # ==========================================================
9
+
10
+ PROJECT_INFO = {
11
+ "name" : "Machine Fault Diagnosis Using Deep Learning Approach",
12
+ "version" : "1.0.0",
13
+ "description" : "Vibration signal image classification",
14
+ "classes" : ["Bearing Fault","Bent Shaft","Foundation Looseness","Healthy","Misalignment"],
15
+ "channels" : ["CH1", "CH2", "CH3"],
16
+ "img_size" : (224, 224),
17
+ "num_classes" : 5,
18
+ "framework" : "TensorFlow / Keras",
19
+ }
20
+
21
+ # ══════════════════════════════════════════════════════════════
22
+ # CONFIG
23
+ # ══════════════════════════════════════════════════════════════
24
+ MODEL_PATH = r"C:\Users\HP\Python\saved_models\best_model.keras"
25
+ IMG_H, IMG_W = 128, 256
26
+
27
+ CLASS_NAMES = [
28
+ "Bearing Fault",
29
+ "Bent Shaft",
30
+ "Foundation Looseness",
31
+ "Healthy",
32
+ "Misalignment",
33
+ ]
34
+
35
+ CLASS_INFO = {
36
+ "Bearing Fault":
37
+ "A bearing fault refers to damage or defects in the rolling elements, "
38
+ "inner race, or outer race of a bearing. This causes periodic impulses "
39
+ "in the vibration signal at characteristic defect frequencies (BPFO, BPFI, BSF, FTF).",
40
+ "Bent Shaft":
41
+ "A bent shaft causes excessive vibration at 1Γ— and 2Γ— the running speed. "
42
+ "It leads to unbalanced rotational forces, increased bearing load, and "
43
+ "accelerated wear of connected components.",
44
+ "Foundation Looseness":
45
+ "Foundation looseness occurs when the machine base or structural mounts "
46
+ "are not properly secured. This creates non-linear vibration patterns "
47
+ "and can cause secondary damage if left unaddressed.",
48
+ "Healthy":
49
+ "The machine is operating under normal healthy conditions. No faults "
50
+ "detected in the vibration signal. Routine monitoring and scheduled "
51
+ "maintenance is recommended to maintain this condition.",
52
+ "Misalignment":
53
+ "Shaft misalignment occurs when two coupled shafts are not collinear. "
54
+ "Angular or parallel misalignment generates high vibration at 1Γ— and 2Γ— "
55
+ "frequencies and causes premature bearing and coupling failure.",
56
+ }
57
+
58
+ CLASS_ACTION = {
59
+ "Bearing Fault":
60
+ "πŸ”§ Schedule immediate bearing inspection. Check lubrication levels and "
61
+ "bearing clearances. Replace damaged bearing within the next maintenance window. "
62
+ "Monitor temperature and vibration amplitude until replacement.",
63
+ "Bent Shaft":
64
+ "πŸ”§ Shut down the machine for shaft inspection. Perform dial-indicator runout "
65
+ "measurement. Replace or straighten the shaft before resuming operation. "
66
+ "Inspect associated couplings and bearings for secondary damage.",
67
+ "Foundation Looseness":
68
+ "πŸ”§ Inspect all anchor bolts and mounting hardware. Re-torque foundation bolts "
69
+ "to specification. Check for cracks in the machine base or mounting surface. "
70
+ "Perform resonance test after re-tightening.",
71
+ "Healthy":
72
+ "βœ… No immediate action required. Continue routine vibration monitoring as per "
73
+ "maintenance schedule. Log this reading for trend analysis and baseline comparison.",
74
+ "Misalignment":
75
+ "πŸ”§ Perform precision shaft alignment using laser alignment tools. Check coupling "
76
+ "condition and re-align to manufacturer tolerance before next operation. "
77
+ "Record alignment readings before and after correction.",
78
+ }
79
+
80
+ CLASS_SEVERITY = {
81
+ "Bearing Fault": ("HIGH", "#E53E3E"),
82
+ "Bent Shaft": ("HIGH", "#E53E3E"),
83
+ "Foundation Looseness": ("MEDIUM", "#DD6B20"),
84
+ "Healthy": ("NONE", "#38A169"),
85
+ "Misalignment": ("MEDIUM", "#DD6B20"),
86
+ }
87
+
88
+ CLASS_ICONS = {
89
+ "Bearing Fault": "βŠ™",
90
+ "Bent Shaft": "↬",
91
+ "Foundation Looseness": "⚠",
92
+ "Healthy": "βœ”",
93
+ "Misalignment": "β‡Ή",
94
+ }
95
+
96
+ # ══════════════════════════════════════════════════════════════
97
+ # PAGE CONFIG
98
+ # ══════════════════════════════════════════════════════════════
99
+ st.set_page_config(
100
+ page_title="Machine Fault Diagnosis | CNN",
101
+ page_icon="βš™οΈ",
102
+ layout="wide",
103
+ initial_sidebar_state="expanded"
104
+ )
105
+
106
+ # ═════════════════════��════════════════════════════════════════
107
+ # CSS
108
+ # ══════════════════════════════════════════════════════════════
109
+ st.markdown("""
110
+ <style>
111
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap');
112
+
113
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
114
+
115
+ html, body, [class*="css"], .stApp {
116
+ font-family: 'Inter', sans-serif !important;
117
+ background: #0D1117 !important;
118
+ color: #E2E8F0 !important;
119
+ }
120
+
121
+ .main .block-container {
122
+ padding: 1.5rem 2rem 3rem !important;
123
+ max-width: 1400px !important;
124
+ }
125
+
126
+ /* ══ SIDEBAR ══ */
127
+ [data-testid="stSidebar"] {
128
+ background: #161B22 !important;
129
+ border-right: 1px solid #21262D !important;
130
+ }
131
+ [data-testid="stSidebar"] > div:first-child { padding: 1.25rem 1rem !important; }
132
+
133
+ [data-testid="stSidebar"] .stButton > button {
134
+ background: #1C2333 !important;
135
+ color: #94A3B8 !important;
136
+ border: 1px solid #21262D !important;
137
+ border-radius: 8px !important;
138
+ font-size: 0.82rem !important;
139
+ font-weight: 500 !important;
140
+ transition: all 0.15s ease !important;
141
+ text-align: left !important;
142
+ }
143
+ [data-testid="stSidebar"] .stButton > button:hover {
144
+ background: #1E3A5F !important;
145
+ color: #60A5FA !important;
146
+ border-color: #2563EB !important;
147
+ }
148
+ [data-testid="stSidebar"] .stButton > button[kind="primary"] {
149
+ background: #1E3A5F !important;
150
+ color: #60A5FA !important;
151
+ border-color: #2563EB !important;
152
+ font-weight: 600 !important;
153
+ }
154
+
155
+ .sb-logo {
156
+ display: flex; align-items: center; gap: 10px;
157
+ padding-bottom: 1.1rem;
158
+ border-bottom: 1px solid #21262D;
159
+ margin-bottom: 1.1rem;
160
+ }
161
+ .sb-logo-icon {
162
+ width: 36px; height: 36px; border-radius: 9px;
163
+ background: linear-gradient(135deg, #1D4ED8, #3B82F6);
164
+ display: flex; align-items: center; justify-content: center;
165
+ font-size: 1rem; flex-shrink: 0;
166
+ box-shadow: 0 0 14px rgba(59,130,246,0.35);
167
+ }
168
+ .sb-logo-text { font-size: 0.8rem; font-weight: 700; color: #F1F5F9; line-height: 1.2; }
169
+ .sb-logo-sub { font-size: 0.68rem; color: #64748B; font-weight: 400; }
170
+
171
+ .sb-nav-label {
172
+ font-size: 0.62rem; font-weight: 700; letter-spacing: 0.12em;
173
+ text-transform: uppercase; color: #4B5563; margin: 1rem 0 0.45rem;
174
+ }
175
+
176
+ .sb-fault-item {
177
+ display: flex; align-items: center; gap: 10px;
178
+ padding: 0.5rem 0.7rem; border-radius: 8px;
179
+ background: #1C2333; margin-bottom: 5px;
180
+ border: 1px solid #21262D;
181
+ transition: border-color 0.15s;
182
+ }
183
+ .sb-fault-item:hover { border-color: #2563EB; }
184
+ .sb-fault-icon { font-size: 1.15rem; width: 26px; text-align: center; flex-shrink: 0; }
185
+ .sb-fault-name { font-size: 0.79rem; font-weight: 600; color: #E2E8F0; line-height: 1.2; }
186
+ .sb-fault-sev { font-size: 0.62rem; font-weight: 700; letter-spacing: 0.05em; }
187
+
188
+ .sb-stat-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; margin-top: 4px; }
189
+ .sb-stat {
190
+ background: #1C2333; border: 1px solid #21262D;
191
+ border-radius: 8px; padding: 0.55rem 0.5rem; text-align: center;
192
+ }
193
+ .sb-stat-val { font-size: 1rem; font-weight: 700; color: #60A5FA; }
194
+ .sb-stat-lbl { font-size: 0.62rem; color: #64748B; margin-top: 1px; }
195
+
196
+ .sb-footer {
197
+ margin-top: 1.25rem; padding-top: 0.85rem;
198
+ border-top: 1px solid #21262D;
199
+ font-size: 0.68rem; color: #4B5563; text-align: center; line-height: 1.7;
200
+ }
201
+
202
+ /* ══ NAV ROW (top of each page) ══ */
203
+ div[data-testid="stButton"]:has(button[data-testid="topbar_about"]) button,
204
+ div[data-testid="stButton"]:has(button[data-testid="back_to_diag"]) button {
205
+ background: #1E3A5F !important;
206
+ color: #60A5FA !important;
207
+ border: 1.5px solid #2563EB !important;
208
+ border-radius: 8px !important;
209
+ font-size: 0.76rem !important;
210
+ font-weight: 600 !important;
211
+ padding: 0.32rem 0.95rem !important;
212
+ height: 2.1rem !important;
213
+ white-space: nowrap !important;
214
+ transition: all 0.15s ease !important;
215
+ box-shadow: 0 0 10px rgba(37,99,235,0.2) !important;
216
+ }
217
+ div[data-testid="stButton"]:has(button[data-testid="topbar_about"]) button:hover,
218
+ div[data-testid="stButton"]:has(button[data-testid="back_to_diag"]) button:hover {
219
+ background: #1D4ED8 !important;
220
+ color: #FFFFFF !important;
221
+ box-shadow: 0 0 16px rgba(37,99,235,0.45) !important;
222
+ }
223
+
224
+ /* ══ TOPBAR ══ */
225
+ .topbar {
226
+ background: linear-gradient(135deg, #0F172A 0%, #1E3A5F 60%, #1D4ED8 100%);
227
+ border: 1px solid #2563EB33;
228
+ border-radius: 14px;
229
+ padding: 1.75rem 1.75rem;
230
+ margin-bottom: 1.25rem;
231
+ display: flex; align-items: center; justify-content: space-between;
232
+ box-shadow: 0 0 30px rgba(37,99,235,0.15);
233
+ gap: 1rem; position: relative; overflow: hidden;
234
+ color: white;
235
+ }
236
+ .topbar::before {
237
+ content: 'βš™'; position: absolute; right: 1.75rem; top: 50%;
238
+ transform: translateY(-50%); font-size: 7rem; opacity: 0.06;
239
+ line-height: 1; pointer-events: none;
240
+ }
241
+ .topbar-left h1 {
242
+ font-size: 1.45rem; font-weight: 800; color: #FFFFFF;
243
+ margin: 0 0 0.2rem; letter-spacing: -0.4px;
244
+ }
245
+ .topbar-left p {
246
+ font-size: 0.855rem; color: rgba(255,255,255,0.72);
247
+ margin: 0; font-weight: 400; line-height: 1.5;
248
+ }
249
+ .topbar-right { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
250
+ .topbar-badge {
251
+ padding: 0.28rem 0.8rem; border-radius: 20px;
252
+ font-size: 0.7rem; font-weight: 600;
253
+ background: rgba(255,255,255,0.12); color: #FFFFFF;
254
+ border: 1px solid rgba(255,255,255,0.22);
255
+ }
256
+ .topbar-badge.green {
257
+ background: rgba(74,222,128,0.18); color: #4ADE80;
258
+ border-color: rgba(74,222,128,0.35);
259
+ }
260
+
261
+ /* ══ STATUS BAR ══ */
262
+ .status-bar {
263
+ background: #14532D22; border: 1px solid #16A34A33; border-radius: 9px;
264
+ padding: 0.55rem 1.1rem; margin-bottom: 1.25rem;
265
+ display: flex; align-items: center; gap: 0.85rem;
266
+ font-size: 0.78rem; color: #4ADE80; font-weight: 500; flex-wrap: wrap;
267
+ }
268
+ .status-dot {
269
+ width: 7px; height: 7px; border-radius: 50%;
270
+ background: #22C55E; flex-shrink: 0;
271
+ box-shadow: 0 0 6px #22C55E;
272
+ animation: pulse 2s infinite;
273
+ }
274
+ @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } }
275
+ .status-divider { color: #16A34A66; }
276
+
277
+ /* ══ CARDS ══ */
278
+ .card {
279
+ background: #161B22; border: 1px solid #21262D;
280
+ border-radius: 12px; padding: 1.25rem;
281
+ box-shadow: 0 2px 8px rgba(0,0,0,0.3); margin-bottom: 1rem;
282
+ }
283
+ .card-header {
284
+ display: flex; align-items: center; gap: 7px;
285
+ font-size: 0.78rem; font-weight: 700; text-transform: uppercase;
286
+ letter-spacing: 0.12em; color: #64748B;
287
+ margin-bottom: 1rem; padding-bottom: 0.75rem;
288
+ border-bottom: 1px solid #21262D;
289
+ }
290
+ .card-header-icon { font-size: 0.9rem; }
291
+ .card-header-lg {
292
+ display: flex; align-items: center; gap: 7px;
293
+ font-size: 0.92rem; font-weight: 700; text-transform: uppercase;
294
+ letter-spacing: 0.1em; color: #94A3B8;
295
+ margin-bottom: 1rem; padding-bottom: 0.75rem;
296
+ border-bottom: 1px solid #21262D;
297
+ }
298
+
299
+ /* ══ CHIP ROW ══ */
300
+ .chip-row { display: flex; gap: 7px; flex-wrap: wrap; margin-bottom: 1rem; }
301
+ .chip {
302
+ display: flex; align-items: center; gap: 5px;
303
+ background: #1C2333; border: 1px solid #21262D;
304
+ border-radius: 7px; padding: 0.3rem 0.75rem;
305
+ font-size: 0.75rem; font-weight: 500; color: #94A3B8;
306
+ }
307
+ .chip-icon { font-size: 0.8rem; }
308
+
309
+ /* ══ RESULT CARD ══ */
310
+ .result-card {
311
+ border-radius: 12px; padding: 1.5rem 1.25rem 1.25rem;
312
+ text-align: center; border: 1.5px solid;
313
+ margin-bottom: 0; position: relative; overflow: hidden;
314
+ display: flex; flex-direction: column; align-items: center;
315
+ height: 100%;
316
+ }
317
+ .result-card::before {
318
+ content: ''; position: absolute; top: 0; left: 0; right: 0; height: 3px;
319
+ background: currentColor;
320
+ }
321
+ .result-icon { font-size: 2.5rem; margin-bottom: 0.5rem; display: block; line-height: 1; }
322
+ .result-eyebrow {
323
+ font-size: 0.62rem; font-weight: 700; letter-spacing: 0.14em;
324
+ text-transform: uppercase; color: #64748B; margin-bottom: 0.25rem;
325
+ }
326
+ .result-class { font-size: 1.35rem; font-weight: 700; margin-bottom: 0.6rem; line-height: 1.2; }
327
+ .result-conf { font-size: 2.4rem; font-weight: 800; line-height: 1; }
328
+ .result-conf-sub { font-size: 0.7rem; color: #64748B; margin-top: 0.2rem; margin-bottom: 0.75rem; }
329
+ .sev-pill {
330
+ display: inline-block; padding: 0.22rem 0.9rem; border-radius: 20px;
331
+ font-size: 0.65rem; font-weight: 700; letter-spacing: 0.08em;
332
+ text-transform: uppercase; border: 1.5px solid;
333
+ }
334
+
335
+ /* ══ INFO & ACTION BOX ══ */
336
+ .info-box {
337
+ background: #1E3A5F22; border-left: 3px solid #3B82F6;
338
+ border-radius: 0 8px 8px 0; padding: 0.9rem 1.1rem;
339
+ margin-bottom: 0.75rem; font-size: 0.855rem;
340
+ color: #CBD5E1; line-height: 1.75;
341
+ }
342
+ .action-box {
343
+ background: #14532D22; border-left: 3px solid #22C55E;
344
+ border-radius: 0 8px 8px 0; padding: 0.9rem 1.1rem;
345
+ font-size: 0.855rem; color: #CBD5E1; line-height: 1.75;
346
+ }
347
+ .box-label {
348
+ font-size: 0.62rem; font-weight: 700; letter-spacing: 0.12em;
349
+ text-transform: uppercase; margin-bottom: 0.35rem; display: block;
350
+ }
351
+ .box-label.blue { color: #3B82F6; }
352
+ .box-label.green { color: #22C55E; }
353
+
354
+ /* ══ FAULT GRID ══ */
355
+ .fault-grid-wrapper {
356
+ display: grid; grid-template-columns: repeat(5, 1fr);
357
+ gap: 10px; align-items: stretch;
358
+ }
359
+ .fault-grid-item {
360
+ background: #161B22; border: 1px solid #21262D;
361
+ border-radius: 11px; padding: 1.1rem 0.85rem; text-align: center;
362
+ display: flex; flex-direction: column; align-items: center; justify-content: center;
363
+ height: 100%; transition: border-color 0.15s, transform 0.15s;
364
+ }
365
+ .fault-grid-item:hover { border-color: #2563EB; transform: translateY(-2px); }
366
+ .fault-grid-icon-box {
367
+ width: 54px; height: 54px; border-radius: 14px;
368
+ display: flex; align-items: center; justify-content: center;
369
+ font-size: 1.65rem; line-height: 1; font-weight: 700;
370
+ margin: 0 auto 0.65rem;
371
+ background: #1C2333; border: 1px solid #30374A;
372
+ }
373
+ .fault-grid-name { font-size: 0.79rem; font-weight: 600; color: #E2E8F0; margin-bottom: 0.35rem; }
374
+ .fault-grid-sev {
375
+ display: inline-block; padding: 0.18rem 0.6rem;
376
+ border-radius: 12px; font-size: 0.62rem; font-weight: 700;
377
+ letter-spacing: 0.06em; text-transform: uppercase;
378
+ }
379
+
380
+ /* ══ STEP CARDS ══ */
381
+ .step-grid-wrapper {
382
+ display: grid; grid-template-columns: repeat(3, 1fr);
383
+ gap: 10px; align-items: stretch; margin-bottom: 1rem;
384
+ }
385
+ .step-card {
386
+ background: #161B22; border: 1px solid #21262D;
387
+ border-radius: 11px; padding: 1.4rem 1.1rem; text-align: center;
388
+ display: flex; flex-direction: column; align-items: center; justify-content: flex-start;
389
+ height: 100%;
390
+ }
391
+ .step-num {
392
+ width: 38px; height: 38px; border-radius: 11px;
393
+ background: linear-gradient(135deg, #1D4ED8, #3B82F6);
394
+ color: white; font-size: 0.95rem; font-weight: 700;
395
+ display: flex; align-items: center; justify-content: center;
396
+ margin: 0 auto 0.85rem;
397
+ box-shadow: 0 0 12px rgba(59,130,246,0.35);
398
+ }
399
+ .step-title { font-size: 0.88rem; font-weight: 700; color: #F1F5F9; margin-bottom: 0.3rem; }
400
+ .step-desc { font-size: 0.76rem; color: #64748B; line-height: 1.55; }
401
+
402
+ /* ══ ABOUT PAGE ══ */
403
+ .about-hero {
404
+ background: linear-gradient(135deg, #0F172A 0%, #1E3A5F 60%, #1D4ED8 100%);
405
+ border-radius: 14px; padding: 2.25rem 2rem;
406
+ margin-bottom: 1.25rem; color: white;
407
+ position: relative; overflow: hidden;
408
+ border: 1px solid #2563EB33;
409
+ box-shadow: 0 0 30px rgba(37,99,235,0.15);
410
+ }
411
+ .about-hero::before {
412
+ content: 'βš™'; position: absolute; right: 1.75rem; top: 50%;
413
+ transform: translateY(-50%); font-size: 7rem; opacity: 0.06; line-height: 1;
414
+ }
415
+ .about-hero h2 { font-size: 1.5rem; font-weight: 800; margin: 0 0 0.35rem; letter-spacing: -0.3px; }
416
+ .about-hero p { font-size: 0.875rem; opacity: 0.8; margin: 0; max-width: 580px; line-height: 1.65; }
417
+ .about-hero-badges { display: flex; gap: 7px; flex-wrap: wrap; margin-top: 1rem; }
418
+ .about-hero-badge {
419
+ background: rgba(255,255,255,0.1); border: 1px solid rgba(255,255,255,0.2);
420
+ border-radius: 20px; padding: 0.22rem 0.8rem;
421
+ font-size: 0.7rem; font-weight: 600; color: rgba(255,255,255,0.9);
422
+ }
423
+
424
+ /* ══ PROJECT INFO TABLE ══ */
425
+ .proj-info-table { width: 100%; border-collapse: collapse; font-size: 0.845rem; }
426
+ .proj-info-table tr { border-bottom: 1px solid #21262D; }
427
+ .proj-info-table tr:last-child { border-bottom: none; }
428
+ .proj-info-table td { padding: 0.65rem 0.5rem; vertical-align: top; line-height: 1.5; }
429
+ .proj-info-table td:first-child { width: 36%; padding-right: 0.75rem; }
430
+ .proj-info-table .td-label { display: flex; align-items: flex-start; gap: 7px; color: #64748B; font-weight: 500; }
431
+ .proj-info-table .td-icon { font-size: 0.85rem; margin-top: 1px; flex-shrink: 0; }
432
+ .proj-info-table .td-key { font-size: 0.8rem; font-weight: 600; color: #94A3B8; }
433
+ .proj-info-table .td-val { color: #E2E8F0; font-weight: 500; }
434
+ .proj-info-table .td-badge {
435
+ display: inline-block; background: #1E3A5F; color: #60A5FA;
436
+ border: 1px solid #2563EB44; border-radius: 6px; padding: 0.15rem 0.6rem;
437
+ font-size: 0.75rem; font-weight: 600; font-family: 'JetBrains Mono', monospace;
438
+ }
439
+
440
+ /* ══ OBJECTIVES ══ */
441
+ .obj-item {
442
+ display: flex; align-items: flex-start; gap: 10px;
443
+ padding: 0.7rem 0; border-bottom: 1px solid #21262D;
444
+ font-size: 0.845rem; color: #94A3B8; line-height: 1.55;
445
+ }
446
+ .obj-item:last-child { border-bottom: none; padding-bottom: 0; }
447
+ .obj-num {
448
+ width: 26px; height: 26px; border-radius: 7px;
449
+ background: #1E3A5F; color: #60A5FA; font-size: 0.7rem; font-weight: 700;
450
+ display: flex; align-items: center; justify-content: center; flex-shrink: 0;
451
+ border: 1px solid #2563EB33;
452
+ }
453
+ .obj-title { color: #E2E8F0; font-weight: 600; }
454
+
455
+ /* ══ METHODOLOGY TIMELINE ══ */
456
+ .method-timeline { display: flex; flex-direction: column; gap: 0; }
457
+ .method-step { display: flex; gap: 12px; align-items: flex-start; }
458
+ .method-line { display: flex; flex-direction: column; align-items: center; }
459
+ .method-dot {
460
+ width: 11px; height: 11px; border-radius: 50%;
461
+ background: #2563EB; flex-shrink: 0; margin-top: 4px;
462
+ box-shadow: 0 0 8px rgba(37,99,235,0.5);
463
+ }
464
+ .method-connector {
465
+ width: 2px; background: linear-gradient(to bottom, #2563EB44, #21262D);
466
+ flex: 1; min-height: 28px; margin-top: 3px;
467
+ }
468
+ .method-content { padding-bottom: 1.1rem; }
469
+ .method-content .m-title { font-size: 0.845rem; font-weight: 600; color: #E2E8F0; }
470
+ .method-content .m-desc { font-size: 0.775rem; color: #64748B; margin-top: 2px; line-height: 1.5; }
471
+
472
+ /* ══ TEAM ══ */
473
+ .guide-card {
474
+ background: #1E3A5F22; border: 1.5px solid #2563EB44;
475
+ border-radius: 11px; padding: 1rem 1.1rem;
476
+ display: flex; align-items: center; gap: 12px; margin-bottom: 10px;
477
+ box-shadow: 0 0 16px rgba(37,99,235,0.1);
478
+ }
479
+ .guide-avatar {
480
+ width: 44px; height: 44px; border-radius: 11px;
481
+ background: linear-gradient(135deg, #0F3D99, #1D4ED8);
482
+ display: flex; align-items: center; justify-content: center;
483
+ font-size: 1.1rem; color: white; flex-shrink: 0;
484
+ box-shadow: 0 0 12px rgba(29,78,216,0.4);
485
+ }
486
+ .guide-label { font-size: 0.62rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: #60A5FA; }
487
+ .guide-name { font-size: 0.875rem; font-weight: 700; color: #F1F5F9; margin-top: 1px; }
488
+ .guide-dept { font-size: 0.73rem; color: #64748B; margin-top: 1px; }
489
+ .team-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
490
+ .team-card {
491
+ background: #1C2333; border: 1px solid #21262D;
492
+ border-radius: 10px; padding: 0.85rem 1rem;
493
+ display: flex; align-items: center; gap: 10px; transition: border-color 0.15s;
494
+ }
495
+ .team-card:hover { border-color: #2563EB44; }
496
+ .team-avatar {
497
+ width: 38px; height: 38px; border-radius: 9px;
498
+ background: linear-gradient(135deg, #1D4ED8, #3B82F6);
499
+ display: flex; align-items: center; justify-content: center;
500
+ font-size: 0.95rem; color: white; flex-shrink: 0;
501
+ }
502
+ .team-name { font-size: 0.845rem; font-weight: 600; color: #E2E8F0; }
503
+ .team-id { font-size: 0.72rem; color: #64748B; margin-top: 1px; }
504
+
505
+ /* ══ TECH PILLS ══ */
506
+ .tech-pill {
507
+ display: inline-flex; align-items: center; gap: 5px;
508
+ background: #1C2333; border: 1px solid #21262D;
509
+ border-radius: 7px; padding: 0.38rem 0.75rem;
510
+ font-size: 0.775rem; font-weight: 500; color: #94A3B8; margin: 3px;
511
+ transition: border-color 0.15s;
512
+ }
513
+ .tech-pill:hover { border-color: #2563EB44; color: #60A5FA; }
514
+
515
+ /* ══ CNN ARCH ══ */
516
+ .arch-row {
517
+ display: flex; align-items: center; gap: 10px;
518
+ padding: 0.55rem 0.85rem; border-radius: 8px;
519
+ background: #1C2333; border: 1px solid #21262D; margin-bottom: 5px;
520
+ }
521
+ .arch-name {
522
+ font-size: 0.8rem; font-weight: 600; color: #60A5FA;
523
+ min-width: 130px; font-family: 'JetBrains Mono', monospace;
524
+ }
525
+ .arch-detail { font-size: 0.76rem; color: #64748B; line-height: 1.4; }
526
+ .problem-text { font-size: 0.855rem; color: #94A3B8; line-height: 1.8; }
527
+
528
+ /* ══ EXPANDER ══ */
529
+ [data-testid="stExpander"] {
530
+ background: #161B22 !important;
531
+ border: 1px solid #21262D !important;
532
+ border-radius: 10px !important;
533
+ }
534
+ [data-testid="stExpander"] summary { color: #64748B !important; font-size: 0.82rem !important; }
535
+ pre, code {
536
+ font-family: 'JetBrains Mono', monospace !important;
537
+ background: #0D1117 !important; border-radius: 7px !important;
538
+ font-size: 0.78rem !important; color: #94A3B8 !important;
539
+ border: 1px solid #21262D !important;
540
+ }
541
+
542
+ /* ══ FILE UPLOADER ══ */
543
+ [data-testid="stFileUploader"] {
544
+ background: #161B22 !important;
545
+ border: 1.5px dashed #21262D !important;
546
+ border-radius: 10px !important; padding: 1rem !important;
547
+ }
548
+ [data-testid="stFileUploader"]:hover { border-color: #2563EB !important; }
549
+ .stSpinner > div { border-top-color: #3B82F6 !important; }
550
+
551
+ /* ══ FOOTER ══ */
552
+ .footer {
553
+ text-align: center; padding: 1.25rem; color: #374151; font-size: 0.73rem;
554
+ border-top: 1px solid #21262D; margin-top: 1.5rem;
555
+ font-family: 'JetBrains Mono', monospace;
556
+ }
557
+ </style>
558
+ """, unsafe_allow_html=True)
559
+
560
+
561
+ # ══════════════════════════════════════════════════════════════
562
+ # LOAD MODEL
563
+ # ══════════════════════════════════════════════════════════════
564
+ @st.cache_resource
565
+ def load_model():
566
+ return tf.keras.models.load_model(MODEL_PATH)
567
+
568
+
569
+ # ══════════════════════════════════════════════════════════════
570
+ # PREPROCESS
571
+ # ══════════════════════════════════════════════════════════════
572
+ def preprocess(uploaded_file) -> np.ndarray:
573
+ raw_bytes = uploaded_file.getvalue()
574
+ img = tf.image.decode_png(raw_bytes, channels=1)
575
+ img = tf.image.resize(img, [IMG_H, IMG_W])
576
+ img = tf.cast(img, tf.float32) / 255.0
577
+ img = tf.expand_dims(img, axis=0)
578
+ return img.numpy()
579
+
580
+
581
+ # ══════════════════════════════════════════════════════════════
582
+ # SESSION STATE
583
+ # ══════════════════════════════════════════════════════════════
584
+ if "page" not in st.session_state:
585
+ st.session_state.page = "diagnosis"
586
+
587
+
588
+ # ══════════════════════════════��═══════════════════════════════
589
+ # SIDEBAR
590
+ # ══════════════════════════════════════════════════════════════
591
+ with st.sidebar:
592
+ st.markdown("""
593
+ <div class='sb-logo'>
594
+ <div class='sb-logo-icon'>βš™οΈ</div>
595
+ <div>
596
+ <div class='sb-logo-text'>Machine Fault Diagnosis</div>
597
+ <div class='sb-logo-sub'>CNN Β· Deep Learning System</div>
598
+ </div>
599
+ </div>
600
+ """, unsafe_allow_html=True)
601
+
602
+ st.markdown("<div class='sb-nav-label'>Navigation</div>", unsafe_allow_html=True)
603
+
604
+ if st.button("πŸ” Fault Diagnosis", key="sb_diag",
605
+ use_container_width=True,
606
+ type="primary" if st.session_state.page == "diagnosis" else "secondary"):
607
+ st.session_state.page = "diagnosis"
608
+ st.rerun()
609
+
610
+ if st.button("πŸ“˜ Project Info", key="sb_about",
611
+ use_container_width=True,
612
+ type="primary" if st.session_state.page == "about" else "secondary"):
613
+ st.session_state.page = "about"
614
+ st.rerun()
615
+
616
+ st.markdown("<div class='sb-nav-label'>Fault Reference</div>", unsafe_allow_html=True)
617
+ for cls in CLASS_NAMES:
618
+ severity, sev_color = CLASS_SEVERITY[cls]
619
+ icon = CLASS_ICONS[cls]
620
+ st.markdown(
621
+ "<div class='sb-fault-item'>"
622
+ f"<span class='sb-fault-icon'>{icon}</span>"
623
+ "<div>"
624
+ f"<div class='sb-fault-name'>{cls}</div>"
625
+ f"<div class='sb-fault-sev' style='color:{sev_color}'>{severity} SEVERITY</div>"
626
+ "</div>"
627
+ "</div>",
628
+ unsafe_allow_html=True
629
+ )
630
+
631
+ st.markdown("<div class='sb-nav-label'>Dataset</div>", unsafe_allow_html=True)
632
+ st.markdown("""
633
+ <div class='sb-stat-grid'>
634
+ <div class='sb-stat'><div class='sb-stat-val'>2,400</div><div class='sb-stat-lbl'>Train</div></div>
635
+ <div class='sb-stat'><div class='sb-stat-val'>300</div><div class='sb-stat-lbl'>Val</div></div>
636
+ <div class='sb-stat'><div class='sb-stat-val'>300</div><div class='sb-stat-lbl'>Test</div></div>
637
+ <div class='sb-stat'><div class='sb-stat-val'>5</div><div class='sb-stat-lbl'>Classes</div></div>
638
+ </div>
639
+ """, unsafe_allow_html=True)
640
+
641
+ st.markdown("""
642
+ <div class='sb-footer'>
643
+ Final Year Project Β· 2026–27<br>
644
+ Machine Fault Diagnosis Using Deep Learning Approach<br>
645
+ Dept. of Mechanical Engineering
646
+ </div>
647
+ """, unsafe_allow_html=True)
648
+
649
+
650
+ # ══════════════════════════════════════════════════════════════
651
+ # PAGE: ABOUT PROJECT
652
+ # ══════════════════════════════════════════════════════════════
653
+ if st.session_state.page == "about":
654
+
655
+ # Nav row: spacer (left) | Back button (right)
656
+ _spacer, _btn_col = st.columns([12, 2])
657
+ with _btn_col:
658
+ if st.button("← Back to Diagnosis", key="back_to_diag"):
659
+ st.session_state.page = "diagnosis"
660
+ st.rerun()
661
+
662
+ st.markdown("""
663
+ <div class='about-hero'>
664
+ <h2>Machine Fault Diagnosis using Deep Learning</h2>
665
+ <p>An AI-powered predictive maintenance system that classifies rotating machine
666
+ faults from vibration signal images using a custom 4-block Convolutional Neural Network
667
+ trained on multi-channel accelerometer data.</p>
668
+ <div class='about-hero-badges'>
669
+ <span class='about-hero-badge'>πŸŽ“ Final Year Project</span>
670
+ <span class='about-hero-badge'>πŸ€– Deep Learning</span>
671
+ <span class='about-hero-badge'>πŸ“‘ Vibration Analysis</span>
672
+ <span class='about-hero-badge'>🏭 Predictive Maintenance</span>
673
+ <span class='about-hero-badge'>πŸ”¬ Signal Processing</span>
674
+ </div>
675
+ </div>
676
+ """, unsafe_allow_html=True)
677
+
678
+ col1, col2 = st.columns([1.05, 1], gap="large")
679
+
680
+ with col1:
681
+
682
+ # Project Info table
683
+ st.markdown("""
684
+ <div class='card'>
685
+ <div class='card-header'><span class='card-header-icon'>🏫</span> Project Information</div>
686
+ <table class='proj-info-table'>
687
+ <tr>
688
+ <td><div class='td-label'><span class='td-icon'>πŸ“Œ</span><span class='td-key'>Project Title</span></div></td>
689
+ <td class='td-val'>Machine Fault Diagnosis using Deep Learning Approach</td>
690
+ </tr>
691
+ <tr>
692
+ <td><div class='td-label'><span class='td-icon'>πŸ›οΈ</span><span class='td-key'>College</span></div></td>
693
+ <td class='td-val'>St. Vincent Pallotti College of Engineering and Technology, Nagpur</td>
694
+ </tr>
695
+ <tr>
696
+ <td><div class='td-label'><span class='td-icon'>βš™οΈ</span><span class='td-key'>Department</span></div></td>
697
+ <td class='td-val'>Mechanical Engineering</td>
698
+ </tr>
699
+ <tr>
700
+ <td><div class='td-label'><span class='td-icon'>πŸ“…</span><span class='td-key'>Academic Year</span></div></td>
701
+ <td class='td-val'><span class='td-badge'>2026 – 2027</span>&nbsp; Final Year Β· Sem VIII</td>
702
+ </tr>
703
+ <tr>
704
+ <td><div class='td-label'><span class='td-icon'>🧠</span><span class='td-key'>Framework</span></div></td>
705
+ <td class='td-val'><span class='td-badge'>TensorFlow 2.x</span>&nbsp;<span class='td-badge'>Keras</span></td>
706
+ </tr>
707
+ <tr>
708
+ <td><div class='td-label'><span class='td-icon'>πŸš€</span><span class='td-key'>Deployment</span></div></td>
709
+ <td class='td-val'><span class='td-badge'>Streamlit</span>&nbsp; Web Application</td>
710
+ </tr>
711
+ </table>
712
+ </div>
713
+ """, unsafe_allow_html=True)
714
+
715
+ # Problem Statement
716
+ st.markdown("""
717
+ <div class='card'>
718
+ <div class='card-header'><span class='card-header-icon'>❗</span> Problem Statement</div>
719
+ <div class='problem-text'>
720
+ Faults in rotating machinery pose significant risks to operational reliability, safety,
721
+ and maintenance efficiency. Conventional fault diagnosis techniques rely heavily on manual
722
+ feature extraction and expert knowledge, limiting their accuracy under complex operating
723
+ conditions. This project addresses the need for an automated and reliable machine fault
724
+ diagnosis system by employing
725
+ deep learning techniques for accurate detection and classification of machine faults.
726
+ <br>
727
+ </div>
728
+ </div>
729
+ """, unsafe_allow_html=True)
730
+
731
+ # Objectives
732
+ st.markdown("""
733
+ <div class='card'>
734
+ <div class='card-header'><span class='card-header-icon'>🎯</span> Project Objectives</div>
735
+ """, unsafe_allow_html=True)
736
+
737
+ objectives = [
738
+ ("Collect & Organise Data",
739
+ "Build a balanced vibration signal image dataset across 5 fault classes and 3 sensor channels (CH1, CH2, CH3)."),
740
+ ("Preprocess Signals",
741
+ "Convert raw time-domain vibration signals to 2D image representations; normalise and augment for robust training."),
742
+ ("Design CNN Architecture",
743
+ "Develop a custom 4-block CNN with BatchNorm, Dropout, and Global Average Pooling for efficient feature extraction."),
744
+ ("Train & Optimise",
745
+ "Train using Adam optimizer with EarlyStopping, ReduceLROnPlateau, and ModelCheckpoint callbacks."),
746
+ ("Evaluate Rigorously",
747
+ "Assess model using accuracy, precision, recall, F1-score, and confusion matrix on a held-out test set."),
748
+ ("Deploy as Web Application",
749
+ "Build a real-time Streamlit diagnostic application for live vibration signal image classification."),
750
+ ]
751
+ for i, (title, desc) in enumerate(objectives, 1):
752
+ st.markdown(
753
+ "<div class='obj-item'>"
754
+ f"<div class='obj-num'>{i}</div>"
755
+ f"<div><span class='obj-title'>{title}:</span> {desc}</div>"
756
+ "</div>",
757
+ unsafe_allow_html=True
758
+ )
759
+ st.markdown("</div>", unsafe_allow_html=True)
760
+
761
+ with col2:
762
+
763
+ # Team
764
+ st.markdown("""
765
+ <div class='card'>
766
+ <div class='card-header'><span class='card-header-icon'>πŸ‘₯</span> Project Team</div>
767
+ <div class='guide-card'>
768
+ <div class='guide-avatar'>πŸŽ“</div>
769
+ <div>
770
+ <div class='guide-label'>Project Guide</div>
771
+ <div class='guide-name'>Dr. Amit R Bhende</div>
772
+ <div class='guide-dept'>Department of Mechanical Engineering</div>
773
+ </div>
774
+ </div>
775
+ <div style='font-size:0.62rem;font-weight:700;letter-spacing:0.12em;text-transform:uppercase;color:#4B5563;margin:0.85rem 0 0.5rem;'>
776
+ Student Members
777
+ </div>
778
+ <div class='team-grid'>
779
+ <div class='team-card'>
780
+ <div class='team-avatar'>πŸ‘¨β€πŸ’»</div>
781
+ <div><div class='team-name'>Vedant Giri</div><div class='team-id'>Member 1</div></div>
782
+ </div>
783
+ <div class='team-card'>
784
+ <div class='team-avatar'>πŸ‘¨β€πŸ’»</div>
785
+ <div><div class='team-name'>Tushar Kamble</div><div class='team-id'>Member 2</div></div>
786
+ </div>
787
+ <div class='team-card'>
788
+ <div class='team-avatar'>πŸ‘¨β€πŸ’»</div>
789
+ <div><div class='team-name'>Sanskar Patil</div><div class='team-id'>Member 3</div></div>
790
+ </div>
791
+ <div class='team-card'>
792
+ <div class='team-avatar'>πŸ‘¨β€πŸ’»</div>
793
+ <div><div class='team-name'>Ansheel Salodkar</div><div class='team-id'>Member 4</div></div>
794
+ </div>
795
+ </div>
796
+ </div>
797
+ """, unsafe_allow_html=True)
798
+
799
+ # Technologies
800
+ st.markdown("""
801
+ <div class='card'>
802
+ <div class='card-header'><span class='card-header-icon'>πŸ› οΈ</span> Technologies Used</div>
803
+ """, unsafe_allow_html=True)
804
+
805
+ tech_groups = {
806
+ "Deep Learning" : ["TensorFlow 2.x", "Keras", "NumPy"],
807
+ "Data & Viz" : ["Matplotlib", "Seaborn", "Scikit-learn"],
808
+ "Deployment" : ["Streamlit", "Python 3.x"],
809
+ "Environment" : ["Anaconda", "Jupyter Notebook"],
810
+ }
811
+ for group, techs in tech_groups.items():
812
+ st.markdown(
813
+ f"<div style='font-size:0.62rem;font-weight:700;color:#4B5563;text-transform:uppercase;"
814
+ f"letter-spacing:0.1em;margin:0.6rem 0 0.3rem'>{group}</div>",
815
+ unsafe_allow_html=True
816
+ )
817
+ pills = "".join([f"<span class='tech-pill'>{t}</span>" for t in techs])
818
+ st.markdown(f"<div>{pills}</div>", unsafe_allow_html=True)
819
+ st.markdown("</div>", unsafe_allow_html=True)
820
+
821
+ # Methodology timeline
822
+ method_steps = [
823
+ ("Data Collection",
824
+ "Vibration signals recorded via accelerometers at CH1, CH2, CH3 across 5 fault conditions."),
825
+ ("Signal to Image",
826
+ "Raw time-domain signals converted to 2D grayscale image representations."),
827
+ ("Preprocessing",
828
+ "Images resized to 224x224, normalised to [0,1]; augmentation applied on training set only."),
829
+ ("CNN Training",
830
+ "4-block custom CNN with BatchNorm, Dropout, GAP; Adam optimizer with smart callbacks."),
831
+ ("Evaluation",
832
+ "Confusion matrix, per-class F1-score, precision, recall on 300-image held-out test set."),
833
+ ("Deployment",
834
+ "Interactive Streamlit web application for real-time image upload and diagnosis."),
835
+ ]
836
+ parts = [
837
+ "<div class='card'>",
838
+ "<div class='card-header'><span class='card-header-icon'>πŸ”„</span> Methodology</div>",
839
+ "<div class='method-timeline'>",
840
+ ]
841
+ for idx, (title, desc) in enumerate(method_steps):
842
+ connector = "" if idx == len(method_steps) - 1 else "<div class='method-connector'></div>"
843
+ parts.append(
844
+ "<div class='method-step'>"
845
+ "<div class='method-line'>"
846
+ "<div class='method-dot'></div>"
847
+ + connector +
848
+ "</div>"
849
+ "<div class='method-content'>"
850
+ "<div class='m-title'>" + title + "</div>"
851
+ "<div class='m-desc'>" + desc + "</div>"
852
+ "</div>"
853
+ "</div>"
854
+ )
855
+ parts.append("</div></div>")
856
+ st.markdown("".join(parts), unsafe_allow_html=True)
857
+
858
+ # CNN Architecture
859
+ st.markdown("""
860
+ <div class='card'>
861
+ <div class='card-header'><span class='card-header-icon'>🧠</span> CNN Architecture Overview</div>
862
+ """, unsafe_allow_html=True)
863
+
864
+ arch_cols = st.columns(2, gap="medium")
865
+ arch_left = [
866
+ ("Input Layer", "224 Γ— 224 Γ— 3 β€” normalised to [0, 1]"),
867
+ ("Conv Block 1", "Conv2D(32) β†’ BN β†’ Conv2D(32) β†’ BN β†’ MaxPool β†’ Dropout(0.25)"),
868
+ ("Conv Block 2", "Conv2D(64) β†’ BN β†’ Conv2D(64) β†’ BN β†’ MaxPool β†’ Dropout(0.25)"),
869
+ ("Conv Block 3", "Conv2D(128) β†’ BN β†’ Conv2D(128) β†’ BN β†’ MaxPool β†’ Dropout(0.30)"),
870
+ ]
871
+ arch_right = [
872
+ ("Conv Block 4", "Conv2D(256) β†’ BN β†’ Conv2D(256) β†’ BN β†’ MaxPool β†’ Dropout(0.30)"),
873
+ ("Global Avg Pool", "Replaces Flatten β€” reduces parameters, controls overfitting"),
874
+ ("Dense Head", "Dense(256, L2) β†’ BN β†’ Dropout(0.50) β†’ Dense(128) β†’ Dropout(0.40)"),
875
+ ("Output Layer", "Dense(5) β†’ Softmax β€” probability over 5 fault classes"),
876
+ ]
877
+ with arch_cols[0]:
878
+ for name, detail in arch_left:
879
+ st.markdown(
880
+ "<div class='arch-row'>"
881
+ f"<span class='arch-name'>{name}</span>"
882
+ f"<span class='arch-detail'>{detail}</span>"
883
+ "</div>",
884
+ unsafe_allow_html=True
885
+ )
886
+ with arch_cols[1]:
887
+ for name, detail in arch_right:
888
+ st.markdown(
889
+ "<div class='arch-row'>"
890
+ f"<span class='arch-name'>{name}</span>"
891
+ f"<span class='arch-detail'>{detail}</span>"
892
+ "</div>",
893
+ unsafe_allow_html=True
894
+ )
895
+ st.markdown("</div>", unsafe_allow_html=True)
896
+
897
+ # Project Description
898
+ st.markdown("""
899
+ <div class='card'>
900
+ <div class='card-header'><span class='card-header-icon'>πŸ“„</span> Project Description</div>
901
+ <div class='problem-text'>
902
+ This project presents an end-to-end deep learning pipeline for automated
903
+ machine fault detection and classification. Vibration signals from rotating
904
+ machinery are acquired using accelerometers placed at three different positions
905
+ on the machine (Channel 1, 2, and 3), and the time-domain signals are converted
906
+ into 2D image representations suitable for CNN-based feature extraction.
907
+ <br><br>
908
+ The custom CNN model consists of four progressively deeper convolutional blocks,
909
+ each employing dual convolution layers with Batch Normalisation for training
910
+ stability, followed by Max Pooling for spatial downsampling and Dropout for
911
+ regularisation. Global Average Pooling replaces the traditional Flatten layer,
912
+ significantly reducing parameter count and mitigating overfitting.
913
+ <br><br>
914
+ The training strategy employs the Adam optimiser with a starting learning rate
915
+ of 0.001, complemented by three callbacks: <strong style='color:#E2E8F0'>EarlyStopping</strong>
916
+ (patience=10) to prevent overfitting, <strong style='color:#E2E8F0'>ReduceLROnPlateau</strong>
917
+ (factor=0.5, patience=5) to escape training plateaus, and
918
+ <strong style='color:#E2E8F0'>ModelCheckpoint</strong> to automatically preserve the
919
+ best-performing weights based on validation accuracy.
920
+ <br><br>
921
+ Each sensor channel is treated as an independent sample, tripling the effective
922
+ training dataset from 800 to 2,400 images. The model is evaluated on a
923
+ completely held-out test set of 300 images using accuracy, per-class F1-score,
924
+ precision, recall, and confusion matrix analysis to ensure robust, generalisable
925
+ performance across all five fault categories.
926
+ </div>
927
+ </div>
928
+ """, unsafe_allow_html=True)
929
+
930
+ st.markdown("""
931
+ <div class='footer'>
932
+ Machine Fault Diagnosis System &nbsp;Β·&nbsp; St. Vincent Pallotti College of Engineering &nbsp;Β·&nbsp;
933
+ Mechanical Engineering &nbsp;Β·&nbsp; Final Year Project 2026–27 &nbsp;Β·&nbsp;
934
+ TensorFlow &amp; Streamlit
935
+ </div>
936
+ """, unsafe_allow_html=True)
937
+
938
+
939
+ # ══════════════════════════════════════════════════════════════
940
+ # PAGE: FAULT DIAGNOSIS
941
+ # ══════════════════════════════════════════════════════════════
942
+ else:
943
+
944
+ # Nav row: spacer (left) | Project Info button (right)
945
+ _spacer, _btn_col = st.columns([15, 2])
946
+ with _btn_col:
947
+ if st.button("πŸ“˜ Project Info", key="topbar_about"):
948
+ st.session_state.page = "about"
949
+ st.rerun()
950
+
951
+ st.markdown("""
952
+ <div class='topbar'>
953
+ <div class='topbar-left'>
954
+ <h1>Machine Fault Diagnosis System</h1>
955
+ <p>CNN-based vibration signal analysis for predictive maintenance and condition monitoring.</p>
956
+ </div>
957
+ <div class='topbar-right'>
958
+ <span class='topbar-badge green'>🟒 System Ready</span>
959
+ <span class='topbar-badge'>βš™οΈ CNN Model</span>
960
+ </div>
961
+ </div>
962
+ """, unsafe_allow_html=True)
963
+
964
+ with st.spinner("Initialising model..."):
965
+ model = load_model()
966
+
967
+ st.markdown(
968
+ "<div class='status-bar'>"
969
+ "<div class='status-dot'></div>"
970
+ "<span>Model loaded successfully</span>"
971
+ "<span class='status-divider'>|</span>"
972
+ f"<span>Input: {model.input_shape}</span>"
973
+ "<span class='status-divider'>|</span>"
974
+ f"<span>Parameters: {model.count_params():,}</span>"
975
+ "<span class='status-divider'>|</span>"
976
+ f"<span>Classes: {len(CLASS_NAMES)}</span>"
977
+ "<span class='status-divider'>|</span>"
978
+ "<span>βœ… Ready for inference</span>"
979
+ "</div>",
980
+ unsafe_allow_html=True
981
+ )
982
+
983
+ st.markdown("""
984
+ <div class='card'>
985
+ <div class='card-header-lg'><span style='font-size:1rem'>πŸ“€</span>&nbsp; Upload Vibration Signal Image</div>
986
+ </div>
987
+ """, unsafe_allow_html=True)
988
+
989
+ uploaded = st.file_uploader(
990
+ "Upload a vibration signal graph image (PNG / JPG) from CH1, CH2, or CH3",
991
+ type=["png", "jpg", "jpeg"],
992
+ label_visibility="visible"
993
+ )
994
+
995
+ # ── EMPTY STATE ───────────────────────────────────────────
996
+ if uploaded is None:
997
+ st.markdown("<br>", unsafe_allow_html=True)
998
+
999
+ st.markdown("""
1000
+ <div class='step-grid-wrapper'>
1001
+ <div class='step-card'>
1002
+ <div class='step-num'>1</div>
1003
+ <div class='step-title'>Upload Image</div>
1004
+ <div class='step-desc'>Select a PNG/JPG vibration signal graph from sensor channels CH1, CH2, or CH3.</div>
1005
+ </div>
1006
+ <div class='step-card'>
1007
+ <div class='step-num'>2</div>
1008
+ <div class='step-title'>CNN Analysis</div>
1009
+ <div class='step-desc'>The deep learning model automatically extracts fault features from the signal image.</div>
1010
+ </div>
1011
+ <div class='step-card'>
1012
+ <div class='step-num'>3</div>
1013
+ <div class='step-title'>Get Diagnosis</div>
1014
+ <div class='step-desc'>View fault class, confidence score, engineering explanation, and recommended action.</div>
1015
+ </div>
1016
+ </div>
1017
+ """, unsafe_allow_html=True)
1018
+
1019
+ st.markdown("<br>", unsafe_allow_html=True)
1020
+
1021
+ st.markdown("""
1022
+ <div class='card'>
1023
+ <div class='card-header-lg'><span style='font-size:1rem'>πŸ“‹</span>&nbsp; Detectable Fault Classes</div>
1024
+ """, unsafe_allow_html=True)
1025
+
1026
+ fault_html = "<div class='fault-grid-wrapper'>"
1027
+ for cls in CLASS_NAMES:
1028
+ severity, sev_color = CLASS_SEVERITY[cls]
1029
+ icon = CLASS_ICONS[cls]
1030
+ fault_html += (
1031
+ "<div class='fault-grid-item'>"
1032
+ f"<div class='fault-grid-icon-box'>{icon}</div>"
1033
+ f"<div class='fault-grid-name'>{cls}</div>"
1034
+ f"<span class='fault-grid-sev' style='background:{sev_color}18;color:{sev_color};border:1px solid {sev_color}44'>"
1035
+ f"{severity}"
1036
+ "</span>"
1037
+ "</div>"
1038
+ )
1039
+ fault_html += "</div>"
1040
+ st.markdown(fault_html, unsafe_allow_html=True)
1041
+ st.markdown("</div>", unsafe_allow_html=True)
1042
+
1043
+ # ── PREDICTION STATE ──────────────────────────────────────
1044
+ else:
1045
+ with st.spinner("πŸ” Analysing vibration signal..."):
1046
+ t0 = time.time()
1047
+ arr = preprocess(uploaded)
1048
+ preds = model.predict(arr, verbose=0)[0]
1049
+ elapsed = time.time() - t0
1050
+
1051
+ pred_idx = int(np.argmax(preds))
1052
+ pred_class = CLASS_NAMES[pred_idx]
1053
+ confidence = float(preds[pred_idx]) * 100
1054
+ severity, sev_color = CLASS_SEVERITY[pred_class]
1055
+ icon = CLASS_ICONS[pred_class]
1056
+
1057
+ st.markdown(
1058
+ "<div class='chip-row'>"
1059
+ f"<span class='chip'><span class='chip-icon'>πŸ“</span>{uploaded.name}</span>"
1060
+ f"<span class='chip'><span class='chip-icon'>⏱️</span>{elapsed*1000:.0f} ms inference</span>"
1061
+ f"<span class='chip'><span class='chip-icon'>πŸ“</span>{IMG_W} Γ— {IMG_H} px input</span>"
1062
+ "<span class='chip'><span class='chip-icon'>🧠</span>CNN · Softmax output</span>"
1063
+ "</div>",
1064
+ unsafe_allow_html=True
1065
+ )
1066
+
1067
+ left, right = st.columns([1.1, 1], gap="large")
1068
+
1069
+ with left:
1070
+ st.markdown("""
1071
+ <div class='card'>
1072
+ <div class='card-header'><span class='card-header-icon'>πŸ–ΌοΈ</span> Uploaded Vibration Signal</div>
1073
+ """, unsafe_allow_html=True)
1074
+ st.image(
1075
+ uploaded.getvalue(),
1076
+ caption=f"{uploaded.name} | Resized to {IMG_W}Γ—{IMG_H} for inference",
1077
+ use_container_width=True
1078
+ )
1079
+ st.markdown("</div>", unsafe_allow_html=True)
1080
+
1081
+ with right:
1082
+ st.markdown(
1083
+ "<div class='card' style='height:100%;display:flex;flex-direction:column;'>"
1084
+ "<div class='card-header'><span class='card-header-icon'>πŸ”</span> Diagnosis Result</div>"
1085
+ f"<div class='result-card' style='border-color:{sev_color};background:{sev_color}0D;color:{sev_color};flex:1;'>"
1086
+ f"<span class='result-icon'>{icon}</span>"
1087
+ "<div class='result-eyebrow'>Detected Fault Condition</div>"
1088
+ f"<div class='result-class' style='color:{sev_color}'>{pred_class}</div>"
1089
+ f"<div class='result-conf' style='color:{sev_color}'>{confidence:.1f}%</div>"
1090
+ "<div class='result-conf-sub'>Model Confidence Score</div>"
1091
+ f"<div class='sev-pill' style='background:{sev_color}18;color:{sev_color};border-color:{sev_color}55'>"
1092
+ f"{severity} SEVERITY"
1093
+ "</div>"
1094
+ "</div>"
1095
+ "</div>",
1096
+ unsafe_allow_html=True
1097
+ )
1098
+
1099
+ st.markdown(
1100
+ "<div class='card'>"
1101
+ "<div class='card-header'><span class='card-header-icon'>πŸ“–</span> Fault Explanation &amp; Recommended Action</div>"
1102
+ f"<span class='box-label blue'>Diagnosis β€” {icon} {pred_class}</span>"
1103
+ f"<div class='info-box'>{CLASS_INFO[pred_class]}</div>"
1104
+ "<span class='box-label green'>Recommended Action</span>"
1105
+ f"<div class='action-box'>{CLASS_ACTION[pred_class]}</div>"
1106
+ "</div>",
1107
+ unsafe_allow_html=True
1108
+ )
1109
+
1110
+ with st.expander("πŸ”¬ Technical Details β€” Raw Prediction Data"):
1111
+ d1, d2 = st.columns(2)
1112
+ with d1:
1113
+ st.markdown("**Preprocessed Tensor Info**")
1114
+ st.code(
1115
+ f"Shape : {arr.shape}\n"
1116
+ f"Dtype : {arr.dtype}\n"
1117
+ f"Pixel min : {arr.min():.4f}\n"
1118
+ f"Pixel max : {arr.max():.4f}\n"
1119
+ f"Pixel mean : {arr.mean():.4f}\n"
1120
+ f"Inference : {elapsed*1000:.1f} ms"
1121
+ )
1122
+ with d2:
1123
+ st.markdown("**Raw Softmax Probabilities**")
1124
+ for cls, p in zip(CLASS_NAMES, preds):
1125
+ bar = "β–ˆ" * int(p * 28)
1126
+ st.code(f"{cls:<22}: {p*100:>6.3f}% {bar}")
1127
 
1128
+ st.markdown("""
1129
+ <div class='footer'>
1130
+ Machine Fault Diagnosis Using Deep Learning &nbsp;Β·&nbsp;
1131
+ Final Year Project 2026–27 &nbsp;Β·&nbsp; Built with TensorFlow &amp; Streamlit
1132
+ </div>
1133
+ """, unsafe_allow_html=True)