Naman2302 commited on
Commit
62bd7f8
·
verified ·
1 Parent(s): a4b8d60

Upload 15 files

Browse files
.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.
app.py ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import numpy as np
3
+ import cv2
4
+ import tempfile
5
+ import os
6
+ import sys
7
+
8
+ # Add project root to Python path
9
+ sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
10
+
11
+ # Import predictor class
12
+ from src.predict_fracture import FracturePredictor
13
+
14
+ # Get current script location
15
+ current_dir = os.path.dirname(os.path.abspath(__file__))
16
+ project_root = os.path.dirname(current_dir) # Go up from app/ to project root
17
+
18
+ # CORRECTED MODEL PATHS
19
+ MODEL_PATH = 'models/fracture_detection_model.joblib'
20
+ ENCODER_PATH = 'models/label_encoder.joblib'
21
+ # Debugging output
22
+ print(f"Project root: {project_root}")
23
+ print(f"Model path: {MODEL_PATH}")
24
+ print(f"Model exists: {os.path.exists(MODEL_PATH)}")
25
+ print(f"Encoder exists: {os.path.exists(ENCODER_PATH)}")
26
+
27
+ # Initialize predictor only if files exist
28
+ if os.path.exists(MODEL_PATH) and os.path.exists(ENCODER_PATH):
29
+ predictor = FracturePredictor(model_path=MODEL_PATH, encoder_path=ENCODER_PATH)
30
+ else:
31
+ print("ERROR: Model files not found. Please run training first.")
32
+ exit(1)
33
+
34
+ def predict_fracture(img):
35
+ """Process uploaded image and return prediction results"""
36
+ try:
37
+ # Handle different input types
38
+ if isinstance(img, np.ndarray):
39
+ # Convert to BGR format for OpenCV
40
+ if img.shape[2] == 4: # RGBA image
41
+ img_bgr = cv2.cvtColor(img, cv2.COLOR_RGBA2BGR)
42
+ else: # RGB image
43
+ img_bgr = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
44
+
45
+ # Save to temp file
46
+ with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as tmp:
47
+ tmp_path = tmp.name
48
+ cv2.imwrite(tmp_path, img_bgr)
49
+ else:
50
+ # Already a file path
51
+ tmp_path = img
52
+
53
+ # Get prediction
54
+ label, confidence, vis_path = predictor.predict(tmp_path)
55
+
56
+ # Read visualization image
57
+ vis_img = cv2.imread(vis_path)
58
+ if vis_img is not None:
59
+ vis_img = cv2.cvtColor(vis_img, cv2.COLOR_BGR2RGB)
60
+
61
+ # Clean up temporary file
62
+ if isinstance(img, np.ndarray) and os.path.exists(tmp_path):
63
+ os.unlink(tmp_path)
64
+
65
+ return label, f"{confidence:.4f}", vis_img
66
+
67
+ except Exception as e:
68
+ print(f"Prediction error: {str(e)}")
69
+ return "Error", "N/A", None
70
+
71
+ # Create Gradio interface
72
+ iface = gr.Interface(
73
+ fn=predict_fracture,
74
+ inputs=gr.Image(label="Upload X-Ray Image"),
75
+ outputs=[
76
+ gr.Label(label="Prediction Result"),
77
+ gr.Textbox(label="Confidence Score"),
78
+ gr.Image(label="Prediction Visualization")
79
+ ],
80
+ title="🦴 Bone Fracture Detection System",
81
+ description="Upload an X-ray image to detect bone fractures using GLCM features and SVM classifier",
82
+ examples=[
83
+ [os.path.join("samples", "fractured_1.jpg")],
84
+ [os.path.join("samples", "normal_1.jpg")]
85
+ ],
86
+ flagging_mode="never"
87
+ )
88
+
89
+ if __name__ == "__main__":
90
+ iface.launch(server_name="0.0.0.0", server_port=7860)
models/fracture_detection_model.joblib ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:4db0d63390ac0f3a3c825ed7991fc2b629fd6d6ac9c080bd19096814f6bd7720
3
+ size 2750987
models/label_encoder.joblib ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3e1714f5e0054a603f22d2487236446ad12c2fabb39f38024c622c46bbabe834
3
+ size 431
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
samples/fractured_1.jpg ADDED
samples/normal_1.jpg ADDED
src/__init__.py ADDED
File without changes
src/__pycache__/__init__.cpython-310.pyc ADDED
Binary file (148 Bytes). View file
 
src/__pycache__/glcm_feature_extractor.cpython-310.pyc ADDED
Binary file (3.09 kB). View file
 
src/__pycache__/predict_fracture.cpython-310.pyc ADDED
Binary file (2.79 kB). View file
 
src/glcm_feature_extractor.py ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import numpy as np
3
+ from skimage.feature import graycomatrix, graycoprops
4
+ import os
5
+ from glob import glob
6
+ from PIL import Image, UnidentifiedImageError
7
+
8
+ class GLCMFeatureExtractor:
9
+ def __init__(self, distances=[1, 3, 5], angles=[0, np.pi/4, np.pi/2, 3*np.pi/4]):
10
+ self.distances = distances
11
+ self.angles = angles
12
+
13
+ def preprocess_xray(self, img_path):
14
+ """Robust image loading with multiple fallbacks"""
15
+ try:
16
+ # First try with OpenCV
17
+ img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)
18
+ if img is None:
19
+ # Fallback to PIL for problematic images
20
+ try:
21
+ with Image.open(img_path) as pil_img:
22
+ img = np.array(pil_img.convert('L'))
23
+ except (IOError, UnidentifiedImageError) as e:
24
+ raise ValueError(f"PIL cannot read image: {img_path}") from e
25
+
26
+ # Handle empty images
27
+ if img.size == 0:
28
+ raise ValueError(f"Empty image: {img_path}")
29
+
30
+ # Resize and normalize
31
+ img = cv2.resize(img, (256, 256))
32
+
33
+ # Improved normalization
34
+ img = img.astype(np.float32)
35
+ min_val = np.min(img)
36
+ max_val = np.max(img)
37
+
38
+ # Handle zero-contrast images
39
+ if max_val - min_val < 1e-5:
40
+ img = np.zeros_like(img) # Return black image
41
+ else:
42
+ img = (img - min_val) / (max_val - min_val) * 255
43
+
44
+ return img.astype(np.uint8)
45
+ except Exception as e:
46
+ print(f"Error processing {img_path}: {str(e)}")
47
+ return None
48
+
49
+ def extract_features(self, img):
50
+ """Extract GLCM features with validation"""
51
+ if img is None:
52
+ return None
53
+
54
+ try:
55
+ # Calculate GLCM with optimized parameters
56
+ glcm = graycomatrix(
57
+ img,
58
+ distances=self.distances,
59
+ angles=self.angles,
60
+ levels=256,
61
+ symmetric=True,
62
+ normed=True
63
+ )
64
+
65
+ # Extract texture properties
66
+ features = []
67
+ props = ['contrast', 'dissimilarity', 'homogeneity',
68
+ 'energy', 'correlation', 'ASM']
69
+
70
+ for prop in props:
71
+ feat = graycoprops(glcm, prop)
72
+ features.extend(feat.flatten())
73
+
74
+ return np.array(features)
75
+ except Exception as e:
76
+ print(f"Feature extraction error: {str(e)}")
77
+ return None
78
+
79
+ def extract_from_folder(self, folder_path, max_samples=None):
80
+ """Batch feature extraction with error handling"""
81
+ features = []
82
+ labels = []
83
+ class_name = os.path.basename(folder_path)
84
+
85
+ # Find all image files
86
+ image_paths = []
87
+ for ext in ('*.png', '*.jpg', '*.jpeg', '*.dcm', '*.tif', '*.bmp'):
88
+ image_paths.extend(glob(os.path.join(folder_path, ext)))
89
+
90
+ if not image_paths:
91
+ print(f"Warning: No images found in {folder_path}")
92
+ return np.array([]), np.array([])
93
+
94
+ # Apply sampling if requested
95
+ if max_samples and len(image_paths) > max_samples:
96
+ image_paths = np.random.choice(image_paths, max_samples, replace=False)
97
+
98
+ # Process each image
99
+ for img_path in image_paths:
100
+ img = self.preprocess_xray(img_path)
101
+ if img is None:
102
+ continue
103
+
104
+ feat = self.extract_features(img)
105
+ if feat is not None:
106
+ features.append(feat)
107
+ labels.append(class_name)
108
+
109
+ print(f"Successfully processed {len(features)}/{len(image_paths)} images in {folder_path}")
110
+ return np.array(features), np.array(labels)
src/predict_fracture.py ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import numpy as np
3
+ import joblib
4
+ from matplotlib import pyplot as plt
5
+ import os
6
+ import matplotlib
7
+ matplotlib.use('Agg') # For headless environments
8
+ from .glcm_feature_extractor import GLCMFeatureExtractor
9
+
10
+ class FracturePredictor:
11
+ def __init__(self, model_path='models/fracture_detection_model.joblib',
12
+ encoder_path='models/label_encoder.joblib'):
13
+ # Verify model paths
14
+ if not os.path.exists(model_path):
15
+ raise FileNotFoundError(f"Model file not found: {model_path}")
16
+ if not os.path.exists(encoder_path):
17
+ raise FileNotFoundError(f"Encoder file not found: {encoder_path}")
18
+
19
+ self.model = joblib.load(model_path)
20
+ self.le = joblib.load(encoder_path)
21
+ self.extractor = GLCMFeatureExtractor()
22
+
23
+ def predict(self, img_input, visualize=True, save_path='prediction_result.png'):
24
+ """
25
+ Predict fracture from image input (file path)
26
+ Returns: (label, confidence, visualization_path)
27
+ """
28
+ try:
29
+ # Preprocess image
30
+ img = self.extractor.preprocess_xray(img_input)
31
+ if img is None:
32
+ return "Error: Invalid image", 0.0, None
33
+
34
+ # Extract features
35
+ feat = self.extractor.extract_features(img)
36
+ if feat is None:
37
+ return "Error: Feature extraction failed", 0.0, None
38
+
39
+ # Make prediction
40
+ proba = self.model.predict_proba(feat.reshape(1, -1))[0]
41
+ pred = self.model.predict(feat.reshape(1, -1))[0]
42
+ label = self.le.inverse_transform([pred])[0]
43
+ confidence = max(proba)
44
+
45
+ # Generate visualization
46
+ vis_path = None
47
+ if visualize:
48
+ vis_path = save_path
49
+ self.visualize_prediction(img, label, confidence, proba, save_path)
50
+
51
+ return label, confidence, vis_path
52
+ except Exception as e:
53
+ print(f"Prediction error: {str(e)}")
54
+ return "Prediction error", 0.0, None
55
+
56
+ def visualize_prediction(self, img, label, confidence, proba, save_path):
57
+ """Create and save prediction visualization"""
58
+ plt.figure(figsize=(12, 6))
59
+
60
+ # Original image
61
+ plt.subplot(1, 2, 1)
62
+ plt.imshow(img, cmap='gray')
63
+ plt.title(f"Original Image\nPrediction: {label}\nConfidence: {confidence:.2f}")
64
+ plt.axis('off')
65
+
66
+ # Probability distribution
67
+ plt.subplot(1, 2, 2)
68
+ colors = ['red' if cls != label else 'green' for cls in self.le.classes_]
69
+ plt.bar(self.le.classes_, proba, color=colors)
70
+ plt.title("Classification Probabilities")
71
+ plt.ylabel("Probability")
72
+ plt.ylim(0, 1)
73
+
74
+ plt.tight_layout()
75
+ plt.savefig(save_path)
76
+ plt.close()
77
+ return save_path
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)