sharvari0b26 commited on
Commit
617684f
·
1 Parent(s): bcef29b

Add changes

Browse files
phase_1a_sample_solution_multiclass.ipynb ADDED
@@ -0,0 +1,226 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "# A. Extract Features"
8
+ ]
9
+ },
10
+ {
11
+ "cell_type": "code",
12
+ "execution_count": 23,
13
+ "metadata": {},
14
+ "outputs": [
15
+ {
16
+ "data": {
17
+ "text/plain": [
18
+ "<module 'submission.utils.utils' from 'c:\\\\Users\\\\sharv\\\\Documents\\\\TUHH\\\\sem-3\\\\intelligent systems in medicine\\\\project\\\\baselines\\\\phase_1a\\\\submission\\\\utils\\\\utils.py'>"
19
+ ]
20
+ },
21
+ "execution_count": 23,
22
+ "metadata": {},
23
+ "output_type": "execute_result"
24
+ }
25
+ ],
26
+ "source": [
27
+ "# from submission.utils.utils import extract_features_from_image, perform_pca\n",
28
+ "import submission.utils.utils as utils\n",
29
+ "import importlib\n",
30
+ "importlib.reload(utils)"
31
+ ]
32
+ },
33
+ {
34
+ "cell_type": "markdown",
35
+ "metadata": {},
36
+ "source": [
37
+ "## A.1. Extract Features for Multiclass"
38
+ ]
39
+ },
40
+ {
41
+ "cell_type": "code",
42
+ "execution_count": 24,
43
+ "metadata": {},
44
+ "outputs": [
45
+ {
46
+ "name": "stdout",
47
+ "output_type": "stream",
48
+ "text": [
49
+ "Features shape: (2845, 2213)\n",
50
+ "Labels shape: (2845,)\n",
51
+ "[1 1 1 ... 1 2 1]\n"
52
+ ]
53
+ }
54
+ ],
55
+ "source": [
56
+ "from sklearn.model_selection import train_test_split\n",
57
+ "from sklearn.metrics import classification_report\n",
58
+ "import os\n",
59
+ "import pandas as pd\n",
60
+ "import cv2\n",
61
+ "import numpy as np\n",
62
+ "\n",
63
+ "BASE_PATH = \"C:/Users/sharv/Documents/TUHH/sem-3/intelligent systems in medicine/project/baselines/phase_1a\"\n",
64
+ "PATH_TO_GT = os.path.join(BASE_PATH, \"gt_for_classification_multiclass_from_filenames_0_index.csv\")\n",
65
+ "PATH_TO_IMAGES = os.path.join(BASE_PATH, \"images\")\n",
66
+ "\n",
67
+ "df = pd.read_csv(PATH_TO_GT)\n",
68
+ "\n",
69
+ "images = df[\"file_name\"].tolist()\n",
70
+ "\n",
71
+ "features = []\n",
72
+ "labels = []\n",
73
+ "\n",
74
+ "for i in range(len(df)):\n",
75
+ " \n",
76
+ " image_name = df.iloc[i][\"file_name\"]\n",
77
+ " label = df.iloc[i][\"category_id\"]\n",
78
+ "\n",
79
+ " path_to_image = os.path.join(PATH_TO_IMAGES, image_name)\n",
80
+ " image = cv2.imread(path_to_image)\n",
81
+ " \n",
82
+ " image_features = utils.extract_features_from_image(image)\n",
83
+ " \n",
84
+ " features.append(image_features)\n",
85
+ " labels.append(label)\n",
86
+ " \n",
87
+ "features_multiclass = np.array(features)\n",
88
+ "labels_multiclass = np.array(labels)\n",
89
+ "\n",
90
+ "print(\"Features shape:\", features_multiclass.shape)\n",
91
+ "print(\"Labels shape:\", labels_multiclass.shape)\n",
92
+ "print(labels_multiclass)"
93
+ ]
94
+ },
95
+ {
96
+ "cell_type": "markdown",
97
+ "metadata": {},
98
+ "source": [
99
+ "## B.2. Use Prinicpal Component Anaylsis to reduce dimensionality"
100
+ ]
101
+ },
102
+ {
103
+ "cell_type": "code",
104
+ "execution_count": null,
105
+ "metadata": {},
106
+ "outputs": [
107
+ {
108
+ "name": "stdout",
109
+ "output_type": "stream",
110
+ "text": [
111
+ "PCA: Reduced from 433 to 100 components\n",
112
+ "Explained variance: 0.9929\n"
113
+ ]
114
+ }
115
+ ],
116
+ "source": [
117
+ "# k = 100\n",
118
+ "# features_multiclass_reduced = utils.perform_pca(features_multiclass, k)\n",
119
+ "\n",
120
+ "# did not perform psc for training"
121
+ ]
122
+ },
123
+ {
124
+ "cell_type": "markdown",
125
+ "metadata": {},
126
+ "source": [
127
+ "# C. Train Classification Model for Multiclass"
128
+ ]
129
+ },
130
+ {
131
+ "cell_type": "code",
132
+ "execution_count": 25,
133
+ "metadata": {},
134
+ "outputs": [
135
+ {
136
+ "name": "stdout",
137
+ "output_type": "stream",
138
+ "text": [
139
+ "Test Accuracy: 0.9666\n",
140
+ " precision recall f1-score support\n",
141
+ "\n",
142
+ " 0 0.98 0.95 0.96 167\n",
143
+ " 1 0.95 0.98 0.97 253\n",
144
+ " 2 0.99 0.96 0.97 149\n",
145
+ "\n",
146
+ " accuracy 0.97 569\n",
147
+ " macro avg 0.97 0.96 0.97 569\n",
148
+ "weighted avg 0.97 0.97 0.97 569\n",
149
+ "\n",
150
+ "Confusion matrix:\n",
151
+ " [[158 9 0]\n",
152
+ " [ 2 249 2]\n",
153
+ " [ 1 5 143]]\n"
154
+ ]
155
+ }
156
+ ],
157
+ "source": [
158
+ "multiclass_model, _, _ = utils.train_svm_model(features_multiclass, labels_multiclass)\n"
159
+ ]
160
+ },
161
+ {
162
+ "cell_type": "code",
163
+ "execution_count": null,
164
+ "metadata": {},
165
+ "outputs": [
166
+ {
167
+ "name": "stdout",
168
+ "output_type": "stream",
169
+ "text": [
170
+ "Pipeline(steps=[('scaler', StandardScaler()), ('select', SelectKBest(k=500)),\n",
171
+ " ('pca', PCA(n_components=100)),\n",
172
+ " ('svc',\n",
173
+ " SVC(class_weight='balanced', kernel='linear', probability=True,\n",
174
+ " random_state=42))])\n"
175
+ ]
176
+ }
177
+ ],
178
+ "source": [
179
+ "print(multiclass_model)\n"
180
+ ]
181
+ },
182
+ {
183
+ "cell_type": "code",
184
+ "execution_count": 26,
185
+ "metadata": {},
186
+ "outputs": [],
187
+ "source": [
188
+ "# save the weights of multiclass_model\n",
189
+ "import pickle\n",
190
+ "\n",
191
+ "SAVE_PATH = \"C:/Users/sharv/Documents/TUHH/sem-3/intelligent systems in medicine/project/baselines/phase_1a/submission\"\n",
192
+ "\n",
193
+ "with open(os.path.join(SAVE_PATH, \"multiclass_model.pkl\"), \"wb\") as f:\n",
194
+ " pickle.dump(multiclass_model, f)\n"
195
+ ]
196
+ },
197
+ {
198
+ "cell_type": "code",
199
+ "execution_count": null,
200
+ "metadata": {},
201
+ "outputs": [],
202
+ "source": []
203
+ }
204
+ ],
205
+ "metadata": {
206
+ "kernelspec": {
207
+ "display_name": "ism",
208
+ "language": "python",
209
+ "name": "python3"
210
+ },
211
+ "language_info": {
212
+ "codemirror_mode": {
213
+ "name": "ipython",
214
+ "version": 3
215
+ },
216
+ "file_extension": ".py",
217
+ "mimetype": "text/x-python",
218
+ "name": "python",
219
+ "nbconvert_exporter": "python",
220
+ "pygments_lexer": "ipython3",
221
+ "version": "3.9.25"
222
+ }
223
+ },
224
+ "nbformat": 4,
225
+ "nbformat_minor": 2
226
+ }
script.py CHANGED
@@ -3,39 +3,29 @@ import pickle
3
  import cv2
4
  import pandas as pd
5
  import numpy as np
6
- from utils.utils import extract_features_from_image, perform_pca, train_svm_model
7
 
8
 
9
- def run_inference(TEST_IMAGE_PATH, svm_model, k, SUBMISSION_CSV_SAVE_PATH):
10
-
11
- test_images = os.listdir(TEST_IMAGE_PATH)
12
- test_images.sort()
13
 
14
  image_feature_list = []
15
-
16
  for test_image in test_images:
17
-
18
  path_to_image = os.path.join(TEST_IMAGE_PATH, test_image)
19
-
20
  image = cv2.imread(path_to_image)
21
- image_features = extract_features_from_image(image)
22
-
23
- image_feature_list.append(image_features)
24
-
25
  features_multiclass = np.array(image_feature_list)
26
-
27
- features_multiclass_reduced = perform_pca(features_multiclass, k)
28
-
29
- multiclass_predictions = svm_model.predict(features_multiclass_reduced)
30
 
31
- df_predictions = pd.DataFrame(columns=["file_name", "category_id"])
 
 
 
 
 
32
 
33
- for i in range(len(test_images)):
34
- file_name = test_images[i]
35
- new_row = pd.DataFrame({"file_name": file_name,
36
- "category_id": multiclass_predictions[i]}, index=[0])
37
- df_predictions = pd.concat([df_predictions, new_row], ignore_index=True)
38
-
39
  df_predictions.to_csv(SUBMISSION_CSV_SAVE_PATH, index=False)
40
 
41
 
 
3
  import cv2
4
  import pandas as pd
5
  import numpy as np
6
+ from utils.utils import extract_features_from_image
7
 
8
 
9
+ def run_inference(TEST_IMAGE_PATH, pipeline_model, SUBMISSION_CSV_SAVE_PATH):
10
+ test_images = sorted(os.listdir(TEST_IMAGE_PATH))
 
 
11
 
12
  image_feature_list = []
13
+
14
  for test_image in test_images:
 
15
  path_to_image = os.path.join(TEST_IMAGE_PATH, test_image)
 
16
  image = cv2.imread(path_to_image)
17
+ features = extract_features_from_image(image)
18
+ image_feature_list.append(features)
19
+
 
20
  features_multiclass = np.array(image_feature_list)
 
 
 
 
21
 
22
+ multiclass_predictions = pipeline_model.predict(features_multiclass)
23
+
24
+ df_predictions = pd.DataFrame({
25
+ "file_name": test_images,
26
+ "category_id": multiclass_predictions
27
+ })
28
 
 
 
 
 
 
 
29
  df_predictions.to_csv(SUBMISSION_CSV_SAVE_PATH, index=False)
30
 
31
 
utils/__pycache__/utils.cpython-39.pyc CHANGED
Binary files a/utils/__pycache__/utils.cpython-39.pyc and b/utils/__pycache__/utils.cpython-39.pyc differ
 
utils/utils.py CHANGED
@@ -2,131 +2,144 @@ import cv2
2
  import numpy as np
3
  from skimage.feature.texture import graycomatrix, graycoprops
4
  from skimage.feature import local_binary_pattern ,hog
5
- from skimage.feature import local_binary_pattern
6
  from sklearn.decomposition import PCA
7
  from sklearn.svm import SVC
8
- from sklearn.model_selection import GridSearchCV
9
- from sklearn.model_selection import train_test_split
10
- from sklearn.metrics import accuracy_score
11
  from sklearn.preprocessing import StandardScaler
12
- from sklearn.metrics import classification_report
13
 
14
-
15
- def rgb_histogram(image, bins=64):
16
  features = []
17
-
18
- # RGB histograms (reduced bins)
19
  for i in range(3):
20
  hist = cv2.calcHist([image], [i], None, [bins], [0, 256])
21
  hist = cv2.normalize(hist, hist).flatten()
22
  features.extend(hist)
23
-
24
- # HSV color space (more discriminative)
25
- hsv = cv2.cvtColor(image, cv2.COLOR_RGB2HSV)
26
- for i in range(3):
27
- hist = cv2.calcHist([hsv], [i], None, [bins], [0, 256])
 
28
  hist = cv2.normalize(hist, hist).flatten()
29
  features.extend(hist)
30
-
31
- # Color moments (mean, std for each channel)
32
  for i in range(3):
33
  channel = image[:, :, i].astype(np.float32)
34
  features.append(np.mean(channel))
35
  features.append(np.std(channel))
36
  features.append(np.median(channel))
37
-
38
  return np.array(features)
39
 
 
40
  def hu_moments(image):
41
- # Convert to grayscale if the image is in RGB format
42
  gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
43
  moments = cv2.moments(gray)
44
  hu_moments = cv2.HuMoments(moments).flatten()
45
- # Apply log transform to reduce scale variance
46
  hu_moments = -np.sign(hu_moments) * np.log10(np.abs(hu_moments) + 1e-10)
47
  return hu_moments
48
 
49
- def glcm_features(image, distances=[1], angles=[0], levels=256, symmetric=True, normed=True):
50
- # Multiple distance-angle combinations for texture diversity
51
  gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
52
- glcm = graycomatrix(gray, distances=distances, angles=angles, levels=levels, symmetric=symmetric, normed=normed)
53
- contrast = graycoprops(glcm, 'contrast').flatten()
54
- dissimilarity = graycoprops(glcm, 'dissimilarity').flatten()
55
- homogeneity = graycoprops(glcm, 'homogeneity').flatten()
56
- energy = graycoprops(glcm, 'energy').flatten()
57
- correlation = graycoprops(glcm, 'correlation').flatten()
58
- asm = graycoprops(glcm, 'ASM').flatten()
59
- return np.concatenate([contrast, dissimilarity, homogeneity, energy, correlation, asm])
60
-
61
- def local_binary_pattern_features(image, P=8, R=1): #Higher P and R
 
 
 
 
 
 
 
62
  gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
63
  lbp = local_binary_pattern(gray, P, R, method='uniform')
64
  (hist, _) = np.histogram(lbp.ravel(), bins=np.arange(0, P + 3), range=(0, P + 2), density=True)
65
  return hist
66
 
67
-
68
-
69
- # Edge Density (Canny-based)
70
-
71
  def edge_density(image, low_threshold=50, high_threshold=150):
72
-
73
  gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
74
  edges = cv2.Canny(gray, low_threshold, high_threshold)
75
  density = np.sum(edges > 0) / edges.size
76
  return np.array([density])
77
 
78
-
79
-
80
-
81
- def hog_features(image, pixels_per_cell=(64, 64), cells_per_block=(1, 1), orientations=4):
82
- """
83
- Highly compressed HOG features to prevent overfitting
84
- """
85
  image_resized = cv2.resize(image, (128, 128))
86
  gray = cv2.cvtColor(image_resized, cv2.COLOR_RGB2GRAY)
 
 
87
  hog_feat = hog(gray,
88
- orientations=orientations,
89
- pixels_per_cell=pixels_per_cell,
90
- cells_per_block=cells_per_block,
91
  block_norm='L2-Hys',
92
- feature_vector=True)
 
93
  return hog_feat
94
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
 
96
  def extract_features_from_image(image):
97
-
98
- # 1. RGB Histogram
 
 
99
  hist_features = rgb_histogram(image)
100
 
101
-
102
  # 2. Hu Moments
103
  hu_features = hu_moments(image)
104
 
105
- # 3. GLCM Features
106
- glcm_features_vector = glcm_features(image)
107
-
108
- # 4. Local Binary Pattern (LBP)
109
- lbp_features = local_binary_pattern_features(image)
110
 
111
-
112
- #### Add more feature extraction methods here ####
113
-
114
- edge_feat = edge_density(image)
115
  hog_feat = hog_features(image)
116
-
117
 
118
- ##################################################
 
119
 
 
 
120
 
121
- # Concatenate all feature vectors
122
- image_features = np.concatenate([hist_features, hu_features, glcm_features_vector, lbp_features
123
- ,edge_feat,hog_feat])
 
 
 
 
 
124
 
125
-
126
  return image_features
127
 
128
-
129
-
130
  def perform_pca(data, num_components):
131
  # Clean data
132
  data = np.nan_to_num(data, nan=0.0, posinf=0.0, neginf=0.0)
@@ -145,53 +158,58 @@ def perform_pca(data, num_components):
145
 
146
  return data_reduced
147
 
148
-
149
- def train_svm_model(features, labels, test_size=0.2, k=100):
 
 
 
 
 
150
  """
151
- Trains an SVM model and returns the trained model.
152
-
153
- Parameters:
154
- - features: Feature matrix of shape (B, F)
155
- - labels: Label matrix of shape (B, C) if one-hot encoded, or (B,) for single labels
156
- - test_size: Proportion of the data to use for testing (default is 0.2)
157
-
158
  Returns:
159
- - svm_model: Trained SVM model
 
 
160
  """
161
- # Check if labels are one-hot encoded, convert if needed
162
  if labels.ndim > 1 and labels.shape[1] > 1:
163
- labels = np.argmax(labels, axis=1) # Convert one-hot to single label per sample
164
-
165
- # Split the data into training and testing sets
166
- X_train, X_test, y_train, y_test = train_test_split(features, labels, test_size=test_size, random_state=42)
167
-
168
- # ---------- FIX 1: Standardize TRAIN ONLY ----------
169
- scaler = StandardScaler()
170
- X_train_scaled = scaler.fit_transform(X_train)
171
- X_test_scaled = scaler.transform(X_test)
172
-
173
- # ---------- FIX 2: PCA fit ONLY on TRAIN ----------
174
- pca = PCA(n_components=min(k, X_train_scaled.shape[1]))
175
- X_train_reduced = pca.fit_transform(X_train_scaled)
176
- X_test_reduced = pca.transform(X_test_scaled)
177
-
178
- # SVM GridSearch
179
- param_grid = {
180
- 'C': [0.1, 1],
181
- 'gamma': [0.001, 0.0001],
182
- 'kernel': ['rbf']
183
- }
184
- grid = GridSearchCV(SVC(), param_grid, refit=True, verbose=3)
185
- grid.fit(X_train_reduced, y_train)
 
 
 
 
 
 
 
 
186
 
187
  # Evaluate
188
- preds = grid.predict(X_test_reduced)
189
- report = classification_report(y_test, preds)
190
-
191
- # Return EVERYTHING needed for inference
192
- return {
193
- "svm": grid,
194
- "scaler": scaler,
195
- "pca": pca,
196
- "report": report
197
- }
 
2
  import numpy as np
3
  from skimage.feature.texture import graycomatrix, graycoprops
4
  from skimage.feature import local_binary_pattern ,hog
 
5
  from sklearn.decomposition import PCA
6
  from sklearn.svm import SVC
7
+ from sklearn.model_selection import train_test_split, GridSearchCV, StratifiedKFold
8
+ from sklearn.metrics import accuracy_score, confusion_matrix, classification_report
9
+ from sklearn.feature_selection import SelectKBest, f_classif
10
  from sklearn.preprocessing import StandardScaler
11
+ from sklearn.pipeline import Pipeline
12
 
13
+ def rgb_histogram(image, bins=32):
 
14
  features = []
 
 
15
  for i in range(3):
16
  hist = cv2.calcHist([image], [i], None, [bins], [0, 256])
17
  hist = cv2.normalize(hist, hist).flatten()
18
  features.extend(hist)
19
+
20
+ hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
21
+ h_hist = cv2.calcHist([hsv], [0], None, [bins], [0, 180])
22
+ s_hist = cv2.calcHist([hsv], [1], None, [bins], [0, 256])
23
+ v_hist = cv2.calcHist([hsv], [2], None, [bins], [0, 256])
24
+ for hist in (h_hist, s_hist, v_hist):
25
  hist = cv2.normalize(hist, hist).flatten()
26
  features.extend(hist)
27
+
 
28
  for i in range(3):
29
  channel = image[:, :, i].astype(np.float32)
30
  features.append(np.mean(channel))
31
  features.append(np.std(channel))
32
  features.append(np.median(channel))
33
+
34
  return np.array(features)
35
 
36
+
37
  def hu_moments(image):
 
38
  gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
39
  moments = cv2.moments(gray)
40
  hu_moments = cv2.HuMoments(moments).flatten()
 
41
  hu_moments = -np.sign(hu_moments) * np.log10(np.abs(hu_moments) + 1e-10)
42
  return hu_moments
43
 
44
+ def glcm_features_improved(image):
 
45
  gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
46
+
47
+ gray = (gray // 4).astype(np.uint8)
48
+
49
+ features = []
50
+ for distance in [1, 3]:
51
+ for angle in [0, np.pi/4, np.pi/2, 3*np.pi/4]:
52
+ glcm = graycomatrix(gray, distances=[distance], angles=[angle],
53
+ levels=64, symmetric=True, normed=True)
54
+
55
+ props = ['contrast', 'dissimilarity', 'homogeneity', 'energy', 'correlation']
56
+ for prop in props:
57
+ feature_val = graycoprops(glcm, prop).flatten()
58
+ features.extend(feature_val)
59
+
60
+ return np.array(features)
61
+
62
+ def local_binary_pattern_features(image, P=8, R=1):
63
  gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
64
  lbp = local_binary_pattern(gray, P, R, method='uniform')
65
  (hist, _) = np.histogram(lbp.ravel(), bins=np.arange(0, P + 3), range=(0, P + 2), density=True)
66
  return hist
67
 
 
 
 
 
68
  def edge_density(image, low_threshold=50, high_threshold=150):
 
69
  gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
70
  edges = cv2.Canny(gray, low_threshold, high_threshold)
71
  density = np.sum(edges > 0) / edges.size
72
  return np.array([density])
73
 
74
+ def hog_features(image, pixels_per_cell=(32, 32), cells_per_block=(1, 1), orientations=8):
 
 
 
 
 
 
75
  image_resized = cv2.resize(image, (128, 128))
76
  gray = cv2.cvtColor(image_resized, cv2.COLOR_RGB2GRAY)
77
+
78
+ # More detailed HOG parameters
79
  hog_feat = hog(gray,
80
+ orientations=9,
81
+ pixels_per_cell=(16, 16),
82
+ cells_per_block=(2, 2),
83
  block_norm='L2-Hys',
84
+ feature_vector=True,
85
+ transform_sqrt=True)
86
  return hog_feat
87
 
88
+ def spatial_pyramid_features(image, levels=2):
89
+ features = []
90
+ h, w = image.shape[:2]
91
+
92
+ for level in range(levels):
93
+ num_rows = 2 ** level
94
+ num_cols = 2 ** level
95
+
96
+ for i in range(num_rows):
97
+ for j in range(num_cols):
98
+ row_start = int(i * h / num_rows)
99
+ row_end = int((i + 1) * h / num_rows)
100
+ col_start = int(j * w / num_cols)
101
+ col_end = int((j + 1) * w / num_cols)
102
+
103
+ patch = image[row_start:row_end, col_start:col_end]
104
+ if patch.size > 0:
105
+ patch_features = rgb_histogram(patch, bins=32)
106
+ features.extend(patch_features)
107
+
108
+ return np.array(features)
109
 
110
  def extract_features_from_image(image):
111
+ """
112
+ Select best features using correlation removal and ANOVA F-test
113
+ """
114
+ #1. RGB Histogram
115
  hist_features = rgb_histogram(image)
116
 
 
117
  # 2. Hu Moments
118
  hu_features = hu_moments(image)
119
 
120
+ # 3. GLCM Features with multiple distances/angles
121
+ glcm_features_vector = glcm_features_improved(image)
 
 
 
122
 
123
+ # 4. Improved HOG
 
 
 
124
  hog_feat = hog_features(image)
 
125
 
126
+ # 5. Spatial pyramid (level 1 only for efficiency)
127
+ spatial_feat = spatial_pyramid_features(image, levels=1)
128
 
129
+ # Remove less important features to reduce noise
130
+ # Consider removing edge_density or LBP if they don't help
131
 
132
+ # Concatenate selected features
133
+ image_features = np.concatenate([
134
+ hist_features,
135
+ hu_features,
136
+ glcm_features_vector,
137
+ hog_feat,
138
+ spatial_feat
139
+ ])
140
 
 
141
  return image_features
142
 
 
 
143
  def perform_pca(data, num_components):
144
  # Clean data
145
  data = np.nan_to_num(data, nan=0.0, posinf=0.0, neginf=0.0)
 
158
 
159
  return data_reduced
160
 
161
+ def train_svm_model(features, labels,
162
+ test_size=0.2,
163
+ random_state=42,
164
+ use_selectkbest=True,
165
+ k_best=500,
166
+ n_pca_components=100,
167
+ do_gridsearch=False):
168
  """
 
 
 
 
 
 
 
169
  Returns:
170
+ pipeline: trained sklearn Pipeline (scaler -> optional SelectKBest -> PCA -> SVC)
171
+ X_test, y_test, y_pred for quick evaluation
172
+ grid_search (if do_gridsearch True), else None
173
  """
 
174
  if labels.ndim > 1 and labels.shape[1] > 1:
175
+ labels = np.argmax(labels, axis=1)
176
+
177
+ # stratified split
178
+ X_train, X_test, y_train, y_test = train_test_split(
179
+ features, labels, test_size=test_size, random_state=random_state, stratify=labels)
180
+
181
+ # build pipeline steps
182
+ steps = []
183
+ steps.append(('scaler', StandardScaler()))
184
+ if use_selectkbest:
185
+ steps.append(('select', SelectKBest(score_func=f_classif, k=min(k_best, X_train.shape[1]))))
186
+ steps.append(('pca', PCA(n_components=min(n_pca_components, X_train.shape[1]))))
187
+ steps.append(('svc', SVC(kernel='linear', probability=True, class_weight='balanced', random_state=random_state)))
188
+ pipeline = Pipeline(steps)
189
+
190
+ grid_search = None
191
+ if do_gridsearch:
192
+ param_grid = {
193
+ 'select__k': [int(min(200, X_train.shape[1])), int(min(500, X_train.shape[1])), int(min(1000, X_train.shape[1]))] if use_selectkbest else [],
194
+ 'pca__n_components': [50, 100, 200],
195
+ 'svc__C': [0.1, 1, 5, 10]
196
+ }
197
+ # remove empty keys if use_selectkbest is False
198
+ param_grid = {k: v for k, v in param_grid.items() if v}
199
+ cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=random_state)
200
+ grid_search = GridSearchCV(pipeline, param_grid, cv=cv, n_jobs=-1, scoring='accuracy', verbose=2)
201
+ grid_search.fit(X_train, y_train)
202
+ best_model = grid_search.best_estimator_
203
+ pipeline = best_model
204
+ else:
205
+ pipeline.fit(X_train, y_train)
206
 
207
  # Evaluate
208
+ y_pred = pipeline.predict(X_test)
209
+ acc = accuracy_score(y_test, y_pred)
210
+ print(f"Test Accuracy: {acc:.4f}")
211
+ print(classification_report(y_test, y_pred))
212
+ print("Confusion matrix:\n", confusion_matrix(y_test, y_pred))
213
+
214
+ return pipeline, (X_test, y_test, y_pred), grid_search
215
+