ipetrousov's picture
Disable SSR mode for custom domain
09b2e24 verified
Raw History Blame Contribute Delete
7.63 kB
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)