Raman Kamran Claude commited on
Commit
57dbac5
Β·
0 Parent(s):

Initial commit: Crack Detection System for HuggingFace Space

Browse files

- Added crack-detection.py Streamlit application
- Added model weights (best.weights.h5) via Git LFS
- Added performance visualizations (PNG files) via Git LFS
- Configured requirements.txt with dependencies
- Added README.md with project documentation
- Configured .gitattributes for LFS tracking (.h5 and .png files)

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

Files changed (10) hide show
  1. .gitattributes +2 -0
  2. .gitignore +30 -0
  3. README.md +60 -0
  4. best.weights.h5 +3 -0
  5. confusion_matrix.png +3 -0
  6. crack-detection.py +288 -0
  7. predictions.png +3 -0
  8. requirements.txt +4 -0
  9. roc.png +3 -0
  10. training.png +3 -0
.gitattributes ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ *.h5 filter=lfs diff=lfs merge=lfs -text
2
+ *.png filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ env/
8
+ venv/
9
+ ENV/
10
+ *.egg-info/
11
+
12
+ # Jupyter Notebook
13
+ .ipynb_checkpoints
14
+
15
+ # IDE
16
+ .vscode/
17
+ .idea/
18
+ *.swp
19
+ *.swo
20
+
21
+ # OS
22
+ .DS_Store
23
+ Thumbs.db
24
+
25
+ # Temporary files
26
+ ~$*
27
+ *.tmp
28
+
29
+ # Training artifacts (keep only necessary model files)
30
+ training.py.txt
README.md ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Crack Detection System
3
+ emoji: πŸ”
4
+ colorFrom: red
5
+ colorTo: yellow
6
+ sdk: streamlit
7
+ sdk_version: 1.32.0
8
+ app_file: crack-detection.py
9
+ pinned: false
10
+ license: apache-2.0
11
+ ---
12
+
13
+ # πŸ” Crack Detection System
14
+
15
+ An AI-powered crack detection system using ResNet50 deep learning model. This application can analyze images and detect structural cracks with high accuracy.
16
+
17
+ ## Features
18
+
19
+ - **High Accuracy**: ~98% accuracy on test dataset
20
+ - **ResNet50 Model**: Pre-trained on ImageNet and fine-tuned for crack detection
21
+ - **Real-time Detection**: Upload images and get instant predictions
22
+ - **Visual Feedback**: Clear visualization of results with confidence scores
23
+
24
+ ## Model Details
25
+
26
+ - **Architecture**: ResNet50 with custom classification head
27
+ - **Training Dataset**: 40,000 images of cracked and non-cracked surfaces
28
+ - **Performance Metrics**:
29
+ - Accuracy: ~98%
30
+ - AUC: ~99.9%
31
+
32
+ ## Classes
33
+
34
+ - **negative**: No crack detected (Class 0)
35
+ - **positive**: Crack detected (Class 1)
36
+
37
+ ## How to Use
38
+
39
+ 1. Upload an image (JPG, JPEG, PNG, or BMP format)
40
+ 2. The system will analyze the image
41
+ 3. View the prediction result with confidence score
42
+ 4. Check the debug info in the sidebar for detailed prediction values
43
+
44
+ ## Technical Stack
45
+
46
+ - **Framework**: Streamlit
47
+ - **Deep Learning**: TensorFlow/Keras
48
+ - **Model**: ResNet50
49
+ - **Image Processing**: PIL/Pillow, NumPy
50
+
51
+ ## Model Performance
52
+
53
+ The model includes performance visualizations:
54
+ - Confusion Matrix
55
+ - ROC Curve
56
+ - Sample Predictions
57
+
58
+ ---
59
+
60
+ Built with ❀️ using Streamlit and TensorFlow
best.weights.h5 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3a9ed76b0624d7995c8179d4f75a2a840b89f3d40cb8b354b7a402145a6f3a1e
3
+ size 118608120
confusion_matrix.png ADDED

Git LFS Details

  • SHA256: 5041f01b986fab994fae9ef7ee6e4a46eb2429a5ceff0344a887ba73ba441b8b
  • Pointer size: 130 Bytes
  • Size of remote file: 89.1 kB
crack-detection.py ADDED
@@ -0,0 +1,288 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ πŸ” Crack Detection System - Streamlit App
3
+ FINAL FIXED VERSION - Preprocessing matches training EXACTLY
4
+ """
5
+
6
+ import streamlit as st
7
+ import tensorflow as tf
8
+ import numpy as np
9
+ from PIL import Image
10
+
11
+ # ============================================
12
+ # PAGE CONFIG
13
+ # ============================================
14
+ st.set_page_config(
15
+ page_title="Crack Detection System",
16
+ page_icon="πŸ”",
17
+ layout="wide"
18
+ )
19
+
20
+ # ============================================
21
+ # CSS
22
+ # ============================================
23
+ st.markdown("""
24
+ <style>
25
+ .result-crack {
26
+ background-color: #FFE5E5;
27
+ border: 3px solid #FF4B4B;
28
+ border-radius: 15px;
29
+ padding: 2rem;
30
+ text-align: center;
31
+ }
32
+ .result-no-crack {
33
+ background-color: #E5FFE5;
34
+ border: 3px solid #4CAF50;
35
+ border-radius: 15px;
36
+ padding: 2rem;
37
+ text-align: center;
38
+ }
39
+ </style>
40
+ """, unsafe_allow_html=True)
41
+
42
+ # ============================================
43
+ # BUILD MODEL - MUST MATCH TRAINING EXACTLY
44
+ # ============================================
45
+ def build_model():
46
+ """
47
+ Build EXACT same architecture as training.
48
+
49
+ IMPORTANT: Training model included preprocess_input as a LAYER,
50
+ so the model expects RAW pixels (0-255), not rescaled!
51
+
52
+ But training used ImageDataGenerator with rescale=1./255,
53
+ which means during training the model received pixels in [0,1] range,
54
+ and then preprocess_input converted them further.
55
+
56
+ This is the conflict we need to resolve.
57
+ """
58
+ from tensorflow.keras.applications import ResNet50
59
+ from tensorflow.keras import layers, Model
60
+
61
+ base_model = ResNet50(
62
+ weights='imagenet',
63
+ include_top=False,
64
+ input_shape=(224, 224, 3)
65
+ )
66
+
67
+ # EXACT architecture from training
68
+ inputs = tf.keras.Input(shape=(224, 224, 3))
69
+ x = tf.keras.applications.resnet50.preprocess_input(inputs)
70
+ x = base_model(x, training=False)
71
+ x = layers.GlobalAveragePooling2D()(x)
72
+ x = layers.BatchNormalization()(x)
73
+ x = layers.Dropout(0.5)(x)
74
+ x = layers.Dense(512, activation='relu')(x)
75
+ x = layers.BatchNormalization()(x)
76
+ x = layers.Dropout(0.3)(x)
77
+ x = layers.Dense(256, activation='relu')(x)
78
+ x = layers.Dropout(0.2)(x)
79
+ outputs = layers.Dense(1, activation='sigmoid', dtype='float32')(x)
80
+
81
+ model = Model(inputs, outputs)
82
+ return model
83
+
84
+ # ============================================
85
+ # LOAD MODEL
86
+ # ============================================
87
+ @st.cache_resource
88
+ def load_model():
89
+ """Load model weights"""
90
+ import os
91
+
92
+ # Try weight files in order
93
+ for wf in ['best.weights.h5', 'final_weights.weights.h5']:
94
+ if os.path.exists(wf):
95
+ try:
96
+ model = build_model()
97
+ model.load_weights(wf)
98
+ return model, wf
99
+ except Exception as e:
100
+ st.warning(f"Failed {wf}: {e}")
101
+
102
+ # Try full model
103
+ if os.path.exists('crack_model.h5'):
104
+ try:
105
+ model = tf.keras.models.load_model('crack_model.h5', compile=False)
106
+ return model, 'crack_model.h5'
107
+ except:
108
+ pass
109
+
110
+ return None, None
111
+
112
+ # ============================================
113
+ # PREPROCESSING - CRITICAL FIX!
114
+ # ============================================
115
+ def preprocess_image(image):
116
+ """
117
+ CRITICAL: Match EXACT preprocessing from training!
118
+
119
+ Training pipeline:
120
+ 1. ImageDataGenerator with rescale=1./255 β†’ pixels become [0, 1]
121
+ 2. Model has preprocess_input layer β†’ expects [0, 255], outputs [-1, 1]
122
+
123
+ This is a CONFLICT in the training code!
124
+
125
+ The training fed [0,1] pixels to preprocess_input which expects [0,255].
126
+ So preprocess_input received already-scaled values and scaled them again.
127
+
128
+ To match this EXACT behavior in inference:
129
+ - We need to rescale to [0,1] first (like ImageDataGenerator did)
130
+ - Then feed to model (which applies preprocess_input internally)
131
+ """
132
+ # Convert to RGB
133
+ if image.mode != 'RGB':
134
+ image = image.convert('RGB')
135
+
136
+ # Resize to 224x224
137
+ image = image.resize((224, 224), Image.Resampling.LANCZOS)
138
+
139
+ # Convert to array
140
+ img_array = np.array(image, dtype=np.float32)
141
+
142
+ # MATCH TRAINING: Apply rescale=1./255 like ImageDataGenerator did
143
+ img_array = img_array / 255.0
144
+
145
+ # Add batch dimension
146
+ img_array = np.expand_dims(img_array, axis=0)
147
+
148
+ # Model will apply preprocess_input internally
149
+ return img_array
150
+
151
+ # ============================================
152
+ # PREDICTION
153
+ # ============================================
154
+ def predict_crack(model, image):
155
+ """
156
+ Make prediction.
157
+
158
+ Training class indices (alphabetical from folder names):
159
+ - 'negative' = 0 (no crack)
160
+ - 'positive' = 1 (crack)
161
+
162
+ Model output sigmoid:
163
+ - Close to 0 β†’ class 0 β†’ negative β†’ NO CRACK
164
+ - Close to 1 β†’ class 1 β†’ positive β†’ CRACK
165
+ """
166
+ img = preprocess_image(image)
167
+
168
+ # Predict
169
+ pred = model.predict(img, verbose=0)[0][0]
170
+ pred = float(pred)
171
+
172
+ # Class mapping
173
+ if pred > 0.5:
174
+ label = "πŸ”΄ CRACK DETECTED"
175
+ confidence = pred * 100
176
+ is_crack = True
177
+ else:
178
+ label = "🟒 NO CRACK"
179
+ confidence = (1.0 - pred) * 100
180
+ is_crack = False
181
+
182
+ return label, confidence, is_crack, pred
183
+
184
+ # ============================================
185
+ # MAIN APP
186
+ # ============================================
187
+ def main():
188
+ st.markdown("<h1 style='text-align:center;color:#FF4B4B'>πŸ” Crack Detection System</h1>", unsafe_allow_html=True)
189
+ st.markdown("<p style='text-align:center;color:#666'>Upload an image to detect cracks using AI (ResNet50)</p>", unsafe_allow_html=True)
190
+
191
+ # Sidebar
192
+ with st.sidebar:
193
+ st.header("πŸ“Š Model Info")
194
+ st.markdown("""
195
+ **Model:** ResNet50
196
+ **Dataset:** 40,000 images
197
+ **Accuracy:** ~98%
198
+ **AUC:** ~99.9%
199
+ """)
200
+
201
+ st.header("πŸ“ Classes")
202
+ st.markdown("""
203
+ - **negative** β†’ No Crack (0)
204
+ - **positive** β†’ Crack (1)
205
+ """)
206
+
207
+ # Load model
208
+ model, source = load_model()
209
+
210
+ if model is None:
211
+ st.error("❌ Model not found!")
212
+ st.stop()
213
+
214
+ st.success(f"βœ… Model loaded: {source}")
215
+
216
+ # Show performance in sidebar
217
+ with st.sidebar:
218
+ st.header("πŸ“ˆ Performance")
219
+ try:
220
+ st.image("confusion_matrix.png", caption="Confusion Matrix")
221
+ except:
222
+ pass
223
+ try:
224
+ st.image("roc.png", caption="ROC Curve")
225
+ except:
226
+ pass
227
+
228
+ # File uploader
229
+ st.markdown("---")
230
+ uploaded = st.file_uploader("πŸ“€ Upload Image", type=['jpg', 'jpeg', 'png', 'bmp'])
231
+
232
+ if uploaded:
233
+ col1, col2 = st.columns(2)
234
+
235
+ with col1:
236
+ st.subheader("πŸ“· Uploaded Image")
237
+ image = Image.open(uploaded)
238
+ st.image(image, use_container_width=True)
239
+ st.caption(f"Size: {image.size[0]}x{image.size[1]}")
240
+
241
+ with col2:
242
+ st.subheader("πŸ€– Prediction")
243
+
244
+ with st.spinner("Analyzing..."):
245
+ label, conf, is_crack, raw = predict_crack(model, image)
246
+
247
+ # Debug info
248
+ st.sidebar.markdown("---")
249
+ st.sidebar.markdown("### πŸ”§ Debug")
250
+ st.sidebar.write(f"Raw output: **{raw:.6f}**")
251
+ st.sidebar.write(f"Threshold: 0.5")
252
+ st.sidebar.write(f"Result: {'CRACK' if raw > 0.5 else 'NO CRACK'}")
253
+
254
+ # Display result
255
+ if is_crack:
256
+ st.markdown(f"""
257
+ <div class="result-crack">
258
+ <h1 style="color:#FF4B4B;margin:0">{label}</h1>
259
+ <h2>Confidence: {conf:.1f}%</h2>
260
+ <p>⚠️ Crack detected!</p>
261
+ </div>
262
+ """, unsafe_allow_html=True)
263
+ else:
264
+ st.markdown(f"""
265
+ <div class="result-no-crack">
266
+ <h1 style="color:#4CAF50;margin:0">{label}</h1>
267
+ <h2>Confidence: {conf:.1f}%</h2>
268
+ <p>βœ… No crack detected!</p>
269
+ </div>
270
+ """, unsafe_allow_html=True)
271
+
272
+ st.progress(int(min(conf, 100)))
273
+
274
+ c1, c2 = st.columns(2)
275
+ c1.metric("Result", "Crack" if is_crack else "No Crack")
276
+ c2.metric("Confidence", f"{conf:.1f}%")
277
+ else:
278
+ st.info("πŸ‘† Upload an image to analyze")
279
+ try:
280
+ st.image("predictions.png", caption="Sample Predictions from Training")
281
+ except:
282
+ pass
283
+
284
+ st.markdown("---")
285
+ st.caption("πŸ” Crack Detection | ResNet50 | 40k images | ~98% accuracy")
286
+
287
+ if __name__ == "__main__":
288
+ main()
predictions.png ADDED

Git LFS Details

  • SHA256: 53b944026ff15e354d45e1249417a119789459731111d1291b34bcd558141307
  • Pointer size: 132 Bytes
  • Size of remote file: 1.48 MB
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ streamlit==1.32.0
2
+ tensorflow==2.15.0
3
+ numpy==1.24.3
4
+ Pillow==10.2.0
roc.png ADDED

Git LFS Details

  • SHA256: 7c3700b0eb7ae0deea5e4784ca348fee432553d650b810ab46ff71ff8cb91ad6
  • Pointer size: 131 Bytes
  • Size of remote file: 127 kB
training.png ADDED

Git LFS Details

  • SHA256: a6d0263867b0ffc6c6af23ef2e3579279e708888df5aadd38ae65538656ed859
  • Pointer size: 131 Bytes
  • Size of remote file: 302 kB