File size: 2,697 Bytes
1ea7ba6 | 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 | """
Hand-crafted feature extraction: LBP (texture) + GLCM (texture) + HSV histogram (color).
Used by the texture branch and color branch of SpiceFusionNet.
"""
import numpy as np
import cv2
try:
from skimage.feature import local_binary_pattern, graycomatrix, graycoprops
_SKIMAGE = True
except ImportError:
_SKIMAGE = False
import config
def _require_skimage():
if not _SKIMAGE:
raise ImportError(
"scikit-image is required for texture features.\n"
"Install: pip install scikit-image"
)
def extract_lbp(img_rgb: np.ndarray) -> np.ndarray:
"""Local Binary Pattern histogram — 10-d vector."""
_require_skimage()
gray = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2GRAY)
lbp = local_binary_pattern(gray, config.LBP_P, config.LBP_R, method="uniform")
n_bins = config.LBP_P + 2
hist, _ = np.histogram(lbp.ravel(), bins=n_bins, range=(0, n_bins), density=True)
return hist.astype(np.float32)
def extract_glcm(img_rgb: np.ndarray) -> np.ndarray:
"""GLCM texture features — 48-d vector (6 props × 2 dist × 4 angles)."""
_require_skimage()
gray = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2GRAY)
gray = (gray // (256 // config.GLCM_LEVELS)).astype(np.uint8)
gray = np.clip(gray, 0, config.GLCM_LEVELS - 1)
angles_rad = [np.deg2rad(a) for a in config.GLCM_ANGLES_DEG]
glcm = graycomatrix(
gray,
distances=config.GLCM_DISTANCES,
angles=angles_rad,
levels=config.GLCM_LEVELS,
symmetric=True,
normed=True,
)
props = ["contrast", "dissimilarity", "homogeneity", "energy", "correlation", "ASM"]
feats = []
for p in props:
feats.extend(graycoprops(glcm, p).ravel())
return np.array(feats, dtype=np.float32)
def extract_texture(img_rgb: np.ndarray) -> np.ndarray:
"""Concatenate LBP + GLCM → 58-d texture vector."""
return np.concatenate([extract_lbp(img_rgb), extract_glcm(img_rgb)])
def extract_hsv(img_rgb: np.ndarray) -> np.ndarray:
"""HSV histogram — 100-d color vector (H=36 + S=32 + V=32)."""
hsv = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2HSV)
h = np.histogram(hsv[:, :, 0], bins=config.HSV_H_BINS, range=(0, 180), density=True)[0]
s = np.histogram(hsv[:, :, 1], bins=config.HSV_S_BINS, range=(0, 256), density=True)[0]
v = np.histogram(hsv[:, :, 2], bins=config.HSV_V_BINS, range=(0, 256), density=True)[0]
return np.concatenate([h, s, v]).astype(np.float32)
def extract_all(img_rgb: np.ndarray):
"""Returns (texture: 58-d, color: 100-d)."""
return extract_texture(img_rgb), extract_hsv(img_rgb)
|