File size: 7,632 Bytes
e8f37df 1ee6bb0 883845f 13830e0 883845f e8f37df 1ee6bb0 7b1e0a1 1ee6bb0 7b1e0a1 1ee6bb0 09b2e24 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 | import gradio as gr
import numpy as np
import joblib
from PIL import Image
from skimage.transform import resize
from huggingface_hub import hf_hub_download
from skimage.feature import hog, local_binary_pattern
from skimage.color import rgb2gray, rgb2hsv
import numpy as np
# 0. Inject feature extraction functions
def extract_hog_features_from_list(X_images_rgb):
"""
Takes a list of pre-resized RGB images and returns HOG features.
INPUTS:
- X_images_rgb: list of images in memory
OUTPUTS:
- np.array(X_hog_features): Numpy array of HOG features
"""
X_hog_features = []
for img_rgb in X_images_rgb:
# 1. Convert RGB to Gray using skimage
img_gray = rgb2gray(img_rgb)
# 2. Extract HOG
hog_features = hog(
img_gray,
orientations=9,
pixels_per_cell=(8, 8),
cells_per_block=(2, 2),
visualize=False
)
X_hog_features.append(hog_features)
return np.array(X_hog_features)
def extract_lbp_features_from_list(X_images_rgb):
"""
Standardized LBP extraction using pre-resized RGB images.
INPUTS
- X_images_rgb: list of images in memory
OUTPUTS:
- np.array(X_lbp_features): numpy array of LBP features for each image
"""
X_lbp_features = []
for img_rgb in X_images_rgb:
# 1. Convert RGB to Gray
img_gray = rgb2gray(img_rgb)
# 2. Extract LBP
lbp = local_binary_pattern(img_gray.astype(dtype="int32"), P=8, R=1, method='uniform')
# 3. Create Histogram (10 bins for P=8 uniform)
lbp_hist, _ = np.histogram(lbp.ravel(), bins=np.arange(0, 11), density=True)
X_lbp_features.append(lbp_hist)
return np.array(X_lbp_features)
def extract_hsv_features_from_list(X_images_rgb, hue_lower_bound=0.2, hue_upper_bound=0.45, sat_lower=0.25, bins=10):
"""
Extracts masked HSV histograms from a list of RGB images.
Uses color-based thresholding to isolate plant matter from the background.
INPUTS:
- X_images_rgb: List of RGB images in memory
- hue_lower_bound: Lower threshold for green hue (default 0.2)
- hue_upper_bound: Upper threshold for green hue (default 0.45)
- sat_lower: Minimum saturation to filter out gray/background (default 0.25)
- bins: Number of histogram bins per channel
OUTPUTS:
- np.array(X_hsv_features): Numpy array of concatenated H-S-V histograms
"""
X_hsv_features = []
for img_rgb in X_images_rgb:
# 1. Extract channels
hsv_img = rgb2hsv(img_rgb)
hue_chan = hsv_img[:, :, 0]
sat_chan = hsv_img[:, :, 1]
val_chan = hsv_img[:, :, 2]
# 2. Build mask
hsv_mask = (hue_chan > hue_lower_bound) & (hue_chan < hue_upper_bound) & (sat_chan > sat_lower)
# 3. Apply mask
plant_hue = hue_chan[hsv_mask]
plant_sat = sat_chan[hsv_mask]
plant_val = val_chan[hsv_mask]
# 4. Create Histogram for each channel
if plant_hue.size > 0:
hue_hist, _ = np.histogram(plant_hue, bins=bins, range=(0, 1), density=True)
sat_hist, _ = np.histogram(plant_sat, bins=bins, range=(0, 1), density=True)
val_hist, _ = np.histogram(plant_val, bins=bins, range=(0, 1), density=True)
else:
# print("Zeroed image")
hue_hist = np.zeros(bins)
sat_hist = np.zeros(bins)
val_hist = np.zeros(bins)
# 5. Combine channels into one vector
hsv_vector = np.concatenate([hue_hist, sat_hist, val_hist]) # Flat
# 6. Append to feature list
X_hsv_features.append(hsv_vector)
return np.array(X_hsv_features)
def feature_fusion(super_matrix:np.ndarray=None, feature_list:list =[]):
"""
Fuses multiple feature sets into a single matrix via horizontal stacking.
Used to combine HOG, LBP, and HSV features into a unified feature vector.
INPUTS:
- super_matrix: Existing numpy matrix of features (None for first fusion)
- feature_list: List of feature arrays to be appended to the super_matrix
OUTPUTS:
- super_matrix: Numpy matrix with features stacked horizontally per image
"""
new_features = [np.array(f) for f in feature_list]
if super_matrix is None:
super_matrix = np.hstack([ np.array(f) for f in feature_list ])
else:
super_matrix = np.hstack([super_matrix] + new_features)
return super_matrix
# 1. Load artifacts
REPO_ID = "ipetrousov/weedcrop_svm_classifier"
model_file = hf_hub_download(repo_id=REPO_ID, filename="svc_opt.joblib")
scaler_file = hf_hub_download(repo_id=REPO_ID, filename="scaler.joblib")
pca_file = hf_hub_download(repo_id=REPO_ID, filename="pca.joblib")
model = joblib.load(model_file)
scaler = joblib.load(scaler_file)
pca = joblib.load(pca_file)
# 2. Optimized threshold
OPT_THRESHOLD = 0.023699424100647747
def classify_plant(image_input):
if image_input is None:
return "No image provided", "0.00%"
# Convert to NumPy array and resize to target dimension (256, 256)
img_np = np.array(image_input)
img_resized = resize(img_np, (256, 256), anti_aliasing=True)
img_list = [img_resized]
# Feature extraction
img_hog = extract_hog_features_from_list(img_list)
img_lbp = extract_lbp_features_from_list(img_list)
img_hsv = extract_hsv_features_from_list(img_list)
# Dimensionality reduction on HOG features
img_hog_scaled = scaler.transform(img_hog)
img_hog_reduced = pca.transform(img_hog_scaled)
# Feature fusion
X_combined = feature_fusion(
feature_list=[img_lbp, img_hog_reduced, img_hsv]
)
# Probabilistic inference
probs = model.predict_proba(X_combined)
crop_prob = probs[0, 0]
# Apply threshold logic
if crop_prob >= OPT_THRESHOLD:
prediction = "CROP"
confidence = crop_prob
else:
prediction = "WEED"
confidence = probs[0, 1]
confidence_str = f"{confidence:.4%}"
detailed_metrics = (
f"Decision: {prediction}\n"
f"Confidence: {confidence_str}\n"
f"Raw Crop Probability: {crop_prob:.6f}\n"
f"Operating Threshold: {OPT_THRESHOLD:.6f}"
)
return prediction, detailed_metrics
description_html = """
Drop a plant image to evaluate it using an optimized SVM classifier with multi-modal feature fusion (HOG + LBP + HSV).
<div style="display: flex; gap: 10px; margin-top: 15px;">
<a href="https://github.com/gpetrousov/ml_assignment_demokritos" target="_blank">
<img src="https://img.shields.io/badge/GitHub-View_Repository-181717?style=for-the-badge&logo=github" alt="GitHub Repository" />
</a>
<a href="https://github.com/gpetrousov/ml_assignment_demokritos/blob/master/notebooks/01_report.ipynb" target="_blank">
<img src="https://img.shields.io/badge/Presentation-View_Notebook-0052CC?style=for-the-badge&logo=googleslides" alt="Presentation Notebook" />
</a>
</div>
"""
# 3. Gradio Interface Construction
demo = gr.Interface(
fn=classify_plant,
inputs=gr.Image(type="pil", label="Upload Seedling Image"),
outputs=[
gr.Textbox(label="Predicted Class"),
gr.Textbox(label="Evaluation Breakdown")
],
title="Autofarm: Surgical Weed & Crop Classifier",
description=description_html,
examples=[]
)
if __name__ == "__main__":
demo.launch(ssr_mode=False) |