|
|
import numpy as np |
|
|
import cv2 |
|
|
import os |
|
|
import gradio as gr |
|
|
from skimage.feature import graycomatrix, graycoprops, local_binary_pattern |
|
|
from skimage.io import imread |
|
|
from skimage.color import rgb2gray |
|
|
from sklearn.model_selection import train_test_split |
|
|
from sklearn.svm import SVC |
|
|
from sklearn.neighbors import KNeighborsClassifier |
|
|
from sklearn.metrics import classification_report, confusion_matrix |
|
|
from sklearn.preprocessing import StandardScaler |
|
|
|
|
|
|
|
|
DATASET_PATH = "" |
|
|
CATEGORIES = ["stone", "brick", "wood"] |
|
|
|
|
|
def preprocess_image(image): |
|
|
"""Ensure the image is 2D grayscale and integer type.""" |
|
|
if image is None: |
|
|
print("❌ Warning: Received an empty image.") |
|
|
return None |
|
|
|
|
|
|
|
|
if len(image.shape) == 4: |
|
|
print(f"⚠️ Removing extra dimension: {image.shape}") |
|
|
image = np.squeeze(image, axis=0) |
|
|
|
|
|
|
|
|
if len(image.shape) == 3 and image.shape[2] == 4: |
|
|
print(f"⚠️ Converting RGBA to RGB: {image.shape}") |
|
|
image = cv2.cvtColor(image, cv2.COLOR_BGRA2BGR) |
|
|
|
|
|
|
|
|
if len(image.shape) == 3 and image.shape[2] == 3: |
|
|
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) |
|
|
|
|
|
|
|
|
return image.astype(np.uint8) |
|
|
|
|
|
def extract_glcm_features(image): |
|
|
"""Extract GLCM texture features from a grayscale image.""" |
|
|
gray = preprocess_image(image) |
|
|
if gray is None: |
|
|
return None |
|
|
|
|
|
glcm = graycomatrix(gray, distances=[1, 3, 5], angles=[0, np.pi/4, np.pi/2, 3*np.pi/4], |
|
|
levels=256, symmetric=True, normed=True) |
|
|
contrast = graycoprops(glcm, 'contrast').flatten() |
|
|
correlation = graycoprops(glcm, 'correlation').flatten() |
|
|
energy = graycoprops(glcm, 'energy').flatten() |
|
|
homogeneity = graycoprops(glcm, 'homogeneity').flatten() |
|
|
|
|
|
return np.hstack([contrast, correlation, energy, homogeneity]) |
|
|
|
|
|
def extract_lbp_features(image): |
|
|
"""Extract LBP texture features from a grayscale image.""" |
|
|
gray = preprocess_image(image) |
|
|
if gray is None: |
|
|
return None |
|
|
|
|
|
radius = 1 |
|
|
points = 8 * radius |
|
|
lbp = local_binary_pattern(gray, P=points, R=radius, method="uniform") |
|
|
|
|
|
|
|
|
n_bins = int(lbp.max() + 1) |
|
|
hist, _ = np.histogram(lbp.ravel(), bins=n_bins, range=(0, n_bins), density=True) |
|
|
|
|
|
return hist |
|
|
|
|
|
def augment_image(image): |
|
|
"""Perform multiple augmentation techniques on an image.""" |
|
|
augmented = [] |
|
|
|
|
|
|
|
|
augmented.append(cv2.flip(image, 1)) |
|
|
|
|
|
|
|
|
augmented.append(cv2.flip(image, 0)) |
|
|
|
|
|
|
|
|
augmented.append(cv2.rotate(image, cv2.ROTATE_90_CLOCKWISE)) |
|
|
|
|
|
|
|
|
augmented.append(cv2.rotate(image, cv2.ROTATE_90_COUNTERCLOCKWISE)) |
|
|
|
|
|
|
|
|
noise = np.random.normal(0, 10, image.shape).astype(np.uint8) |
|
|
noisy_image = cv2.add(image, noise) |
|
|
augmented.append(noisy_image) |
|
|
|
|
|
return augmented |
|
|
|
|
|
def load_dataset(): |
|
|
"""Load images and extract features while handling different image formats.""" |
|
|
features_glcm, labels_glcm = [], [] |
|
|
features_lbp, labels_lbp = [], [] |
|
|
original_images = [] |
|
|
|
|
|
for label, category in enumerate(CATEGORIES): |
|
|
category_path = os.path.join(DATASET_PATH, category) |
|
|
|
|
|
def is_image_file(file): |
|
|
return file.lower().endswith((".png", ".jpg", ".jpeg", ".bmp", ".tiff")) |
|
|
|
|
|
image_files = [f for f in os.listdir(category_path) if is_image_file(f)] |
|
|
|
|
|
for img_file in image_files: |
|
|
img_path = os.path.join(category_path, img_file) |
|
|
image = imread(img_path) |
|
|
|
|
|
if image is None: |
|
|
print(f"❌ Skipping: {img_path} (Failed to load)") |
|
|
continue |
|
|
|
|
|
image = preprocess_image(image) |
|
|
original_images.append(image) |
|
|
|
|
|
glcm_features = extract_glcm_features(image) |
|
|
lbp_features = extract_lbp_features(image) |
|
|
|
|
|
if glcm_features is not None and lbp_features is not None: |
|
|
features_glcm.append(glcm_features) |
|
|
labels_glcm.append(label) |
|
|
|
|
|
features_lbp.append(lbp_features) |
|
|
labels_lbp.append(label) |
|
|
|
|
|
max_len_glcm = max(len(x) for x in features_glcm) |
|
|
features_glcm = np.array([np.pad(x, (0, max_len_glcm - len(x)), mode='constant') for x in features_glcm]) |
|
|
|
|
|
max_len_lbp = max(len(x) for x in features_lbp) |
|
|
features_lbp = np.array([np.pad(x, (0, max_len_lbp - len(x)), mode='constant') for x in features_lbp]) |
|
|
|
|
|
return np.array(features_glcm), np.array(labels_glcm), np.array(features_lbp), np.array(labels_lbp), original_images |
|
|
|
|
|
|
|
|
X_glcm, y_glcm, X_lbp, y_lbp, original_images = load_dataset() |
|
|
|
|
|
scaler_lbp = StandardScaler() |
|
|
X_lbp = scaler_lbp.fit_transform(X_lbp) |
|
|
|
|
|
|
|
|
X_train_glcm, X_test_glcm, y_train_glcm, y_test_glcm, train_indices, test_indices = train_test_split( |
|
|
X_glcm, y_glcm, np.arange(len(y_glcm)), test_size=0.3, random_state=42 |
|
|
) |
|
|
|
|
|
X_train_lbp, X_test_lbp, y_train_lbp, y_test_lbp = train_test_split( |
|
|
X_lbp, y_lbp, test_size=0.3, random_state=42 |
|
|
) |
|
|
|
|
|
|
|
|
X_train_glcm_aug, y_train_glcm_aug = list(X_train_glcm), list(y_train_glcm) |
|
|
X_train_lbp_aug, y_train_lbp_aug = list(X_train_lbp), list(y_train_lbp) |
|
|
|
|
|
|
|
|
for idx, original_idx in enumerate(train_indices): |
|
|
image = original_images[original_idx] |
|
|
augmented_images = augment_image(image) |
|
|
|
|
|
for aug_img in augmented_images: |
|
|
glcm_features = extract_glcm_features(aug_img) |
|
|
lbp_features = extract_lbp_features(aug_img) |
|
|
|
|
|
if glcm_features is not None and lbp_features is not None: |
|
|
X_train_glcm_aug.append(glcm_features) |
|
|
y_train_glcm_aug.append(y_train_glcm[idx]) |
|
|
|
|
|
X_train_lbp_aug.append(lbp_features) |
|
|
y_train_lbp_aug.append(y_train_lbp[idx]) |
|
|
|
|
|
|
|
|
X_train_glcm = np.array(X_train_glcm_aug) |
|
|
y_train_glcm = np.array(y_train_glcm_aug) |
|
|
X_train_lbp = np.array(X_train_lbp_aug) |
|
|
y_train_lbp = np.array(y_train_lbp_aug) |
|
|
|
|
|
|
|
|
|
|
|
svm_glcm = SVC(kernel='linear') |
|
|
svm_glcm.fit(X_train_glcm, y_train_glcm) |
|
|
knn_glcm = KNeighborsClassifier(n_neighbors=3) |
|
|
knn_glcm.fit(X_train_glcm, y_train_glcm) |
|
|
|
|
|
svm_lbp = SVC(kernel='linear') |
|
|
svm_lbp.fit(X_train_lbp, y_train_lbp) |
|
|
knn_lbp = KNeighborsClassifier(n_neighbors=5, metric='manhattan') |
|
|
knn_lbp.fit(X_train_lbp, y_train_lbp) |
|
|
|
|
|
|
|
|
|
|
|
print("SVM GLCM Performance:\n", classification_report(y_test_glcm, svm_glcm.predict(X_test_glcm))) |
|
|
print("KNN GLCM Performance:\n", classification_report(y_test_glcm, knn_glcm.predict(X_test_glcm))) |
|
|
print("SVM LBP Performance:\n", classification_report(y_test_lbp, svm_lbp.predict(X_test_lbp))) |
|
|
print("KNN LBP Performance:\n", classification_report(y_test_lbp, knn_lbp.predict(X_test_lbp))) |
|
|
|
|
|
|
|
|
def classify_texture(image, algorithm): |
|
|
image = preprocess_image(image) |
|
|
|
|
|
if algorithm == "SVM (GLCM)": |
|
|
features = extract_glcm_features(image).reshape(1, -1) |
|
|
prediction = svm_glcm.predict(features) |
|
|
elif algorithm == "KNN (GLCM)": |
|
|
features = extract_glcm_features(image).reshape(1, -1) |
|
|
prediction = knn_glcm.predict(features) |
|
|
elif algorithm == "SVM (LBP)": |
|
|
features = extract_lbp_features(image).reshape(1, -1) |
|
|
features = scaler_lbp.transform(features) |
|
|
prediction = svm_lbp.predict(features) |
|
|
elif algorithm == "KNN (LBP)": |
|
|
features = extract_lbp_features(image).reshape(1, -1) |
|
|
features = scaler_lbp.transform(features) |
|
|
prediction = knn_lbp.predict(features) |
|
|
|
|
|
return CATEGORIES[prediction[0]] |
|
|
|
|
|
|
|
|
interface = gr.Interface( |
|
|
fn=classify_texture, |
|
|
inputs=[ |
|
|
gr.Image(type="numpy"), |
|
|
gr.Radio(["SVM (GLCM)", "KNN (GLCM)", "SVM (LBP)", "KNN (LBP)"], label="Select Algorithm") |
|
|
], |
|
|
outputs=gr.Label(label="Predicted Texture"), |
|
|
title="Texture Classification", |
|
|
description="Upload an image of a texture and choose an algorithm to classify it as stone, brick, or wood." |
|
|
) |
|
|
|
|
|
interface.launch() |
|
|
|
|
|
|