Download app.py from ipetrousov/weedcrop_svm_classifier: direct link, hf CLI and curl.
- Browser
- Download file 7.63 kB
-
https://huggingface.co/spaces/ipetrousov/weedcrop_svm_classifier/resolve/main/app.py
- Command line
-
hf download hf://spaces/ipetrousov/weedcrop_svm_classifier/app.py
-
curl -L -o app.py https://huggingface.co/spaces/ipetrousov/weedcrop_svm_classifier/resolve/main/app.py
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) |