Naman2302 commited on
Commit
b95e704
·
verified ·
1 Parent(s): 9074b47

more files

Browse files
Files changed (5) hide show
  1. .gitattributes +1 -0
  2. Readme.md +3 -0
  3. prediction_result.png +3 -0
  4. requirements.txt +7 -0
  5. train_pipeline.py +289 -0
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ prediction_result.png filter=lfs diff=lfs merge=lfs -text
Readme.md ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ # Bone Fracture Detection Gradio App
2
+
3
+ Upload an X-ray image to detect bone fractures using GLCM features and an SVM classifier.
prediction_result.png ADDED

Git LFS Details

  • SHA256: 7610e34d1fd2685c8f0719ac04cb1796f156b51ec96f68f10a4a1d05c98149e1
  • Pointer size: 131 Bytes
  • Size of remote file: 138 kB
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ numpy==1.24.3
2
+ opencv-python==4.8.0.76
3
+ scikit-learn==1.3.0
4
+ scikit-image==0.21.0
5
+ matplotlib==3.7.2
6
+ joblib==1.3.2
7
+ argparse==1.4.0
train_pipeline.py ADDED
@@ -0,0 +1,289 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ from skimage.feature import graycomatrix, graycoprops
3
+ import os
4
+ from glob import glob
5
+ from PIL import Image, UnidentifiedImageError
6
+ import joblib
7
+ from sklearn.svm import SVC
8
+ from sklearn.metrics import classification_report, accuracy_score, recall_score
9
+ from sklearn.preprocessing import LabelEncoder
10
+ import matplotlib
11
+ matplotlib.use('Agg') # Use non-interactive backend
12
+ import matplotlib.pyplot as plt
13
+ import argparse
14
+
15
+ class GLCMFeatureExtractor:
16
+ def __init__(self, distances=[1, 3, 5], angles=[0, np.pi/4, np.pi/2, 3*np.pi/4]):
17
+ self.distances = distances
18
+ self.angles = angles
19
+
20
+ def preprocess_xray(self, img_path):
21
+ """Robust image loading with PIL only"""
22
+ try:
23
+ with Image.open(img_path) as pil_img:
24
+ # Convert to grayscale
25
+ if pil_img.mode != 'L':
26
+ pil_img = pil_img.convert('L')
27
+
28
+ # Resize and convert to numpy array
29
+ pil_img = pil_img.resize((256, 256))
30
+ img = np.array(pil_img)
31
+
32
+ # Handle empty images
33
+ if img.size == 0:
34
+ raise ValueError(f"Empty image: {img_path}")
35
+
36
+ # Improved normalization
37
+ img = img.astype(np.float32)
38
+ min_val = np.min(img)
39
+ max_val = np.max(img)
40
+
41
+ # Handle zero-contrast images
42
+ if max_val - min_val < 1e-5:
43
+ img = np.zeros_like(img) # Return black image
44
+ else:
45
+ img = (img - min_val) / (max_val - min_val) * 255
46
+
47
+ return img.astype(np.uint8)
48
+ except Exception as e:
49
+ print(f"Error processing {img_path}: {str(e)}")
50
+ return None
51
+
52
+ def extract_features(self, img):
53
+ """Extract GLCM features with validation"""
54
+ if img is None:
55
+ return None
56
+
57
+ try:
58
+ # Calculate GLCM with optimized parameters
59
+ glcm = graycomatrix(
60
+ img,
61
+ distances=self.distances,
62
+ angles=self.angles,
63
+ levels=256,
64
+ symmetric=True,
65
+ normed=True
66
+ )
67
+
68
+ # Extract texture properties
69
+ features = []
70
+ props = ['contrast', 'dissimilarity', 'homogeneity',
71
+ 'energy', 'correlation', 'ASM']
72
+
73
+ for prop in props:
74
+ feat = graycoprops(glcm, prop)
75
+ features.extend(feat.flatten())
76
+
77
+ return np.array(features)
78
+ except Exception as e:
79
+ print(f"Feature extraction error: {str(e)}")
80
+ return None
81
+
82
+ def extract_from_folder(self, folder_path, max_samples=None):
83
+ """Batch feature extraction with error handling"""
84
+ features = []
85
+ labels = []
86
+ class_name = os.path.basename(folder_path)
87
+
88
+ # Find all image files
89
+ image_paths = []
90
+ for ext in ('*.png', '*.jpg', '*.jpeg', '*.dcm', '*.tif', '*.bmp'):
91
+ image_paths.extend(glob(os.path.join(folder_path, ext)))
92
+
93
+ if not image_paths:
94
+ print(f"Warning: No images found in {folder_path}")
95
+ return np.array([]), np.array([])
96
+
97
+ # Apply sampling if requested
98
+ if max_samples and len(image_paths) > max_samples:
99
+ image_paths = np.random.choice(image_paths, max_samples, replace=False)
100
+
101
+ # Process each image
102
+ for img_path in image_paths:
103
+ img = self.preprocess_xray(img_path)
104
+ if img is None:
105
+ continue
106
+
107
+ feat = self.extract_features(img)
108
+ if feat is not None:
109
+ features.append(feat)
110
+ labels.append(class_name)
111
+
112
+ print(f"Successfully processed {len(features)}/{len(image_paths)} images in {folder_path}")
113
+ return np.array(features), np.array(labels)
114
+
115
+
116
+ def load_dataset(dataset_path):
117
+ splits = ['train', 'val', 'test']
118
+ features = {split: [] for split in splits}
119
+ labels = {split: [] for split in splits}
120
+ extractor = GLCMFeatureExtractor()
121
+
122
+ for split in splits:
123
+ for label in ['fractured', 'not_fractured']:
124
+ folder = os.path.join(dataset_path, split, label)
125
+ if not os.path.exists(folder):
126
+ print(f"Warning: Missing folder {folder}")
127
+ continue
128
+
129
+ feats, lbls = extractor.extract_from_folder(folder)
130
+ if len(feats) > 0:
131
+ features[split].extend(feats)
132
+ labels[split].extend(lbls)
133
+ print(f"Extracted {len(feats)} samples from {split}/{label}")
134
+ else:
135
+ print(f"No valid samples found in {split}/{label}")
136
+
137
+ return features, labels
138
+
139
+
140
+ def train_and_evaluate(features, labels, model_save_path='models'):
141
+ os.makedirs(model_save_path, exist_ok=True)
142
+
143
+ le = LabelEncoder()
144
+ all_labels = []
145
+ for split in labels:
146
+ all_labels.extend(labels[split])
147
+ le.fit(all_labels)
148
+
149
+ # Prepare data splits
150
+ X_train = np.array(features['train'])
151
+ y_train = le.transform(labels['train'])
152
+
153
+ X_val = np.array(features['val'])
154
+ y_val = le.transform(labels['val'])
155
+
156
+ X_test = np.array(features['test'])
157
+ y_test = le.transform(labels['test'])
158
+
159
+ # Check data availability
160
+ if len(X_train) == 0:
161
+ raise ValueError("No training data available!")
162
+
163
+ # Train SVM classifier
164
+ clf = SVC(kernel='rbf', C=10, gamma='scale', probability=True, class_weight='balanced')
165
+ clf.fit(X_train, y_train)
166
+
167
+ # Evaluate on validation set
168
+ print("\nValidation Set Performance:")
169
+ if len(X_val) > 0:
170
+ y_val_pred = clf.predict(X_val)
171
+ print(classification_report(y_val, y_val_pred, target_names=le.classes_))
172
+ print(f"Validation Accuracy: {accuracy_score(y_val, y_val_pred):.4f}")
173
+ print(f"Validation Recall: {recall_score(y_val, y_val_pred):.4f}")
174
+ else:
175
+ print("No validation data available")
176
+
177
+ # Evaluate on test set
178
+ print("\nTest Set Performance:")
179
+ if len(X_test) > 0:
180
+ y_test_pred = clf.predict(X_test)
181
+ print(classification_report(y_test, y_test_pred, target_names=le.classes_))
182
+ print(f"Test Accuracy: {accuracy_score(y_test, y_test_pred):.4f}")
183
+ print(f"Test Recall: {recall_score(y_test, y_test_pred):.4f}")
184
+ else:
185
+ print("No test data available")
186
+
187
+ # Save model
188
+ model_path = os.path.join(model_save_path, 'fracture_detection_model.joblib')
189
+ encoder_path = os.path.join(model_save_path, 'label_encoder.joblib')
190
+ joblib.dump(clf, model_path)
191
+ joblib.dump(le, encoder_path)
192
+ print(f"\nModel saved to {model_path}")
193
+ print(f"Label encoder saved to {encoder_path}")
194
+
195
+ return clf, le
196
+
197
+ class FracturePredictor:
198
+ def __init__(self, model_path='models/fracture_detection_model.joblib',
199
+ encoder_path='models/label_encoder.joblib'):
200
+ # Verify model paths
201
+ if not os.path.exists(model_path):
202
+ raise FileNotFoundError(f"Model file not found: {model_path}")
203
+ if not os.path.exists(encoder_path):
204
+ raise FileNotFoundError(f"Encoder file not found: {encoder_path}")
205
+
206
+ self.model = joblib.load(model_path)
207
+ self.le = joblib.load(encoder_path)
208
+ self.extractor = GLCMFeatureExtractor()
209
+
210
+ def predict(self, img_input, visualize=True, save_path='prediction_result.png'):
211
+ """
212
+ Predict fracture from image input (file path)
213
+ Returns: (label, confidence, visualization_path)
214
+ """
215
+ try:
216
+ # Preprocess image
217
+ img = self.extractor.preprocess_xray(img_input)
218
+ if img is None:
219
+ return "Error: Invalid image", 0.0, None
220
+
221
+ # Extract features
222
+ feat = self.extractor.extract_features(img)
223
+ if feat is None:
224
+ return "Error: Feature extraction failed", 0.0, None
225
+
226
+ # Make prediction
227
+ proba = self.model.predict_proba(feat.reshape(1, -1))[0]
228
+ pred = self.model.predict(feat.reshape(1, -1))[0]
229
+ label = self.le.inverse_transform([pred])[0]
230
+ confidence = max(proba)
231
+
232
+ # Generate visualization
233
+ vis_path = None
234
+ if visualize:
235
+ vis_path = save_path
236
+ self.visualize_prediction(img, label, confidence, proba, save_path)
237
+
238
+ return label, confidence, vis_path
239
+ except Exception as e:
240
+ print(f"Prediction error: {str(e)}")
241
+ return "Prediction error", 0.0, None
242
+
243
+ def visualize_prediction(self, img, label, confidence, proba, save_path):
244
+ """Create and save prediction visualization"""
245
+ plt.figure(figsize=(12, 6))
246
+
247
+ # Original image
248
+ plt.subplot(1, 2, 1)
249
+ plt.imshow(img, cmap='gray')
250
+ plt.title(f"Original Image\nPrediction: {label}\nConfidence: {confidence:.2f}")
251
+ plt.axis('off')
252
+
253
+ # Probability distribution
254
+ plt.subplot(1, 2, 2)
255
+ colors = ['red' if cls != label else 'green' for cls in self.le.classes_]
256
+ plt.bar(self.le.classes_, proba, color=colors)
257
+ plt.title("Classification Probabilities")
258
+ plt.ylabel("Probability")
259
+ plt.ylim(0, 1)
260
+
261
+ plt.tight_layout()
262
+ plt.savefig(save_path)
263
+ plt.close()
264
+ return save_path
265
+
266
+ if __name__ == '__main__':
267
+ parser = argparse.ArgumentParser(description='Bone Fracture Detection System')
268
+ parser.add_argument('--dataset_path', default='dataset', help='Path to dataset directory')
269
+ parser.add_argument('--model_save_path', default='models', help='Path to save trained models')
270
+ parser.add_argument('--predict_image', default=None, help='Path to image for prediction')
271
+ args = parser.parse_args()
272
+
273
+ if args.predict_image:
274
+ # Predict mode
275
+ predictor = FracturePredictor(
276
+ model_path=os.path.join(args.model_save_path, 'fracture_detection_model.joblib'),
277
+ encoder_path=os.path.join(args.model_save_path, 'label_encoder.joblib')
278
+ )
279
+ label, confidence, vis_path = predictor.predict(args.predict_image)
280
+ print(f"Prediction: {label}")
281
+ print(f"Confidence: {confidence:.4f}")
282
+ if vis_path:
283
+ print(f"Visualization saved to {vis_path}")
284
+ else:
285
+ # Train mode
286
+ print("Loading dataset and extracting features...")
287
+ features, labels = load_dataset(args.dataset_path)
288
+ print("\nTraining and evaluating model...")
289
+ train_and_evaluate(features, labels, args.model_save_path)