File size: 4,240 Bytes
b4123b8 dd1d7f5 b4123b8 dd1d7f5 b4123b8 dd1d7f5 b4123b8 dd1d7f5 b4123b8 dd1d7f5 b4123b8 dd1d7f5 b4123b8 dd1d7f5 b4123b8 dd1d7f5 b4123b8 dd1d7f5 b4123b8 dd1d7f5 b4123b8 dd1d7f5 b4123b8 dd1d7f5 b4123b8 dd1d7f5 b4123b8 dd1d7f5 b4123b8 dd1d7f5 b4123b8 dd1d7f5 b4123b8 4c1c4a7 b4123b8 dd1d7f5 b4123b8 dd1d7f5 b4123b8 dd1d7f5 |
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 |
"""
Minimal texture feature extraction.
"""
import numpy as np
import torch
import torch.nn.functional as F
from skimage.feature import local_binary_pattern, hog
from skimage import exposure
from scipy import ndimage
from typing import Dict, Tuple, Optional
import logging
logger = logging.getLogger(__name__)
class TextureExtractor:
"""Minimal texture extraction (LBP, HOG, Lacunarity only)."""
def __init__(self, lbp_points: int = 8, lbp_radius: int = 1,
hog_orientations: int = 9, hog_pixels_per_cell: Tuple[int, int] = (8, 8),
hog_cells_per_block: Tuple[int, int] = (2, 2), lacunarity_window: int = 15,
ehd_threshold: float = 0.3, angle_resolution: int = 45):
"""Initialize with defaults."""
self.lbp_points = lbp_points
self.lbp_radius = lbp_radius
self.hog_orientations = hog_orientations
self.hog_pixels_per_cell = hog_pixels_per_cell
self.hog_cells_per_block = hog_cells_per_block
self.lacunarity_window = lacunarity_window
def extract_lbp(self, gray_image: np.ndarray) -> np.ndarray:
"""Extract Local Binary Pattern."""
try:
lbp = local_binary_pattern(gray_image, self.lbp_points, self.lbp_radius, method='uniform')
return self._convert_to_uint8(lbp)
except Exception as e:
logger.error(f"LBP failed: {e}")
return np.zeros_like(gray_image, dtype=np.uint8)
def extract_hog(self, gray_image: np.ndarray) -> np.ndarray:
"""Extract HOG features."""
try:
_, vis = hog(gray_image, orientations=self.hog_orientations,
pixels_per_cell=self.hog_pixels_per_cell,
cells_per_block=self.hog_cells_per_block,
visualize=True, feature_vector=True)
return exposure.rescale_intensity(vis, out_range=(0, 255)).astype(np.uint8)
except Exception as e:
logger.error(f"HOG failed: {e}")
return np.zeros_like(gray_image, dtype=np.uint8)
def compute_local_lacunarity(self, gray_image: np.ndarray) -> np.ndarray:
"""Compute lacunarity."""
try:
arr = gray_image.astype(np.float32)
m1 = ndimage.uniform_filter(arr, size=self.lacunarity_window)
m2 = ndimage.uniform_filter(arr * arr, size=self.lacunarity_window)
var = m2 - m1 * m1
lac = var / (m1 * m1 + 1e-6) + 1
lac[m1 <= 1e-6] = 0
return self._convert_to_uint8(lac)
except Exception as e:
logger.error(f"Lacunarity failed: {e}")
return np.zeros_like(gray_image, dtype=np.uint8)
def extract_all_texture_features(self, gray_image: np.ndarray) -> Dict[str, np.ndarray]:
"""Extract LBP, HOG, and Lacunarity."""
return {
'lbp': self.extract_lbp(gray_image),
'hog': self.extract_hog(gray_image),
'lac2': self.compute_local_lacunarity(gray_image)
}
def _convert_to_uint8(self, arr: np.ndarray) -> np.ndarray:
"""Convert to uint8."""
arr = np.nan_to_num(arr, nan=0.0, posinf=0.0, neginf=0.0)
ptp = np.ptp(arr)
if ptp > 0:
normalized = (arr - arr.min()) / (ptp + 1e-6) * 255
else:
normalized = np.zeros_like(arr)
return np.clip(normalized, 0, 255).astype(np.uint8)
def compute_texture_statistics(self, features: Dict[str, np.ndarray],
mask: Optional[np.ndarray] = None) -> Dict[str, Dict[str, float]]:
"""Compute basic statistics."""
stats = {}
for feature_name, feature_data in features.items():
if mask is not None and mask.shape == feature_data.shape:
masked_data = np.where(mask > 0, feature_data, np.nan)
else:
masked_data = feature_data
valid_data = masked_data[~np.isnan(masked_data)]
if len(valid_data) > 0:
stats[feature_name] = {
'mean': float(np.mean(valid_data)),
'std': float(np.std(valid_data)),
}
return stats |