pub mod fractal_analysis;
pub mod image_utils;
pub mod patch_extraction;
pub mod shape_descriptors;
pub mod sift_features;
pub mod simd_accelerated;
pub mod surf_features;
pub mod wavelet_features;
pub mod zernike_moments;
use crate::*;
use scirs2_core::ndarray::{Array2, Array3, ArrayView2, ArrayView3};
pub use fractal_analysis::{FractalDimensionExtractor, FractalMethod};
pub use image_utils::{
compute_entropy_fallback, compute_image_statistics, compute_kurtosis_fallback,
compute_skewness_fallback, extract_patches_fallback, normalize_image, rgb_to_grayscale,
simd_image,
};
pub use patch_extraction::{PatchExtractor, SamplingStrategy};
pub use shape_descriptors::ShapeDescriptorExtractor;
pub use sift_features::{SIFTExtractor, SIFTKeypoint};
pub use simd_accelerated::*;
pub use surf_features::{SURFExtractor, SURFKeypoint};
pub use wavelet_features::{FeatureConfig, WaveletFeatureExtractor, WaveletType};
pub use zernike_moments::ZernikeMomentsExtractor;
pub fn extract_patches_2d(
image: &ArrayView2<Float>,
patch_size: (usize, usize),
max_patches: Option<usize>,
random_state: Option<u64>,
) -> SklResult<Array3<Float>> {
let extractor = PatchExtractor::new()
.patch_size(patch_size)
.max_patches(max_patches)
.random_state(random_state);
extractor.extract(image)
}
pub fn reconstruct_from_patches_2d(
patches: &ArrayView3<Float>,
image_size: (usize, usize),
) -> SklResult<Array2<Float>> {
crate::image::patch_extraction::reconstruct_from_patches_2d(patches, image_size)
}
pub struct ImageFeaturePipeline {
pub sift_extractor: Option<SIFTExtractor>,
pub surf_extractor: Option<SURFExtractor>,
pub wavelet_extractor: Option<WaveletFeatureExtractor>,
pub shape_extractor: Option<ShapeDescriptorExtractor>,
pub fractal_extractor: Option<FractalDimensionExtractor>,
pub zernike_extractor: Option<ZernikeMomentsExtractor>,
}
impl ImageFeaturePipeline {
pub fn builder() -> ImageFeaturePipelineBuilder {
ImageFeaturePipelineBuilder::default()
}
pub fn extract_all_features(&self, image: &ArrayView2<Float>) -> SklResult<Array1<Float>> {
let mut all_features = Vec::new();
if let Some(ref extractor) = self.sift_extractor {
let keypoints = extractor.detect_keypoints(image)?;
if !keypoints.is_empty() {
let descriptors = extractor.extract_descriptors(image, &keypoints)?;
for row in descriptors.rows() {
all_features
.extend_from_slice(row.as_slice().expect("operation should succeed"));
}
}
}
if let Some(ref extractor) = self.surf_extractor {
let keypoints = extractor.detect_keypoints(image)?;
if !keypoints.is_empty() {
let descriptors = extractor.extract_descriptors(image, &keypoints)?;
for row in descriptors.rows() {
all_features
.extend_from_slice(row.as_slice().expect("operation should succeed"));
}
}
}
if let Some(ref extractor) = self.wavelet_extractor {
let features = extractor.extract_features(image)?;
all_features.extend_from_slice(features.as_slice().expect("operation should succeed"));
}
if let Some(ref extractor) = self.shape_extractor {
let features = extractor.extract_features(image)?;
all_features.extend_from_slice(features.as_slice().expect("operation should succeed"));
}
if let Some(ref extractor) = self.fractal_extractor {
let dimension = extractor.compute_fractal_dimension(image)?;
all_features.push(dimension);
}
if let Some(ref extractor) = self.zernike_extractor {
let moments = extractor.extract_features(image)?;
all_features.extend_from_slice(moments.as_slice().expect("operation should succeed"));
}
if all_features.is_empty() {
return Err(SklearsError::InvalidInput(
"No feature extractors enabled in pipeline".to_string(),
));
}
Ok(Array1::from_vec(all_features))
}
}
#[derive(Default)]
pub struct ImageFeaturePipelineBuilder {
enable_sift: bool,
enable_surf: bool,
enable_wavelet: bool,
enable_shape_descriptors: bool,
enable_fractal: bool,
enable_zernike: bool,
}
impl ImageFeaturePipelineBuilder {
pub fn enable_sift(mut self, enable: bool) -> Self {
self.enable_sift = enable;
self
}
pub fn enable_surf(mut self, enable: bool) -> Self {
self.enable_surf = enable;
self
}
pub fn enable_wavelet(mut self, enable: bool) -> Self {
self.enable_wavelet = enable;
self
}
pub fn enable_shape_descriptors(mut self, enable: bool) -> Self {
self.enable_shape_descriptors = enable;
self
}
pub fn enable_fractal(mut self, enable: bool) -> Self {
self.enable_fractal = enable;
self
}
pub fn enable_zernike(mut self, enable: bool) -> Self {
self.enable_zernike = enable;
self
}
pub fn build(self) -> ImageFeaturePipeline {
ImageFeaturePipeline {
sift_extractor: if self.enable_sift {
Some(SIFTExtractor::default())
} else {
None
},
surf_extractor: if self.enable_surf {
Some(SURFExtractor::default())
} else {
None
},
wavelet_extractor: if self.enable_wavelet {
Some(WaveletFeatureExtractor::default())
} else {
None
},
shape_extractor: if self.enable_shape_descriptors {
Some(ShapeDescriptorExtractor::default())
} else {
None
},
fractal_extractor: if self.enable_fractal {
Some(FractalDimensionExtractor::default())
} else {
None
},
zernike_extractor: if self.enable_zernike {
Some(ZernikeMomentsExtractor::default())
} else {
None
},
}
}
}
#[allow(non_snake_case)]
#[cfg(test)]
mod tests {
use super::*;
use scirs2_core::ndarray::Array2;
#[test]
fn test_extract_patches_2d() {
let image = Array2::from_shape_vec((10, 10), (0..100).map(|x| x as f64).collect())
.expect("operation should succeed");
let patches = extract_patches_2d(&image.view(), (3, 3), Some(5), None)
.expect("operation should succeed");
assert_eq!(patches.shape()[1], 3);
assert_eq!(patches.shape()[2], 3);
assert!(patches.shape()[0] <= 5);
}
#[test]
fn test_reconstruct_from_patches_2d() {
let image = Array2::from_shape_vec((6, 6), (0..36).map(|x| x as f64).collect())
.expect("operation should succeed");
let patches = extract_patches_2d(&image.view(), (3, 3), None, None)
.expect("operation should succeed");
let reconstructed =
reconstruct_from_patches_2d(&patches.view(), (6, 6)).expect("operation should succeed");
assert_eq!(reconstructed.shape(), &[6, 6]);
}
#[test]
fn test_image_feature_pipeline() {
let pipeline = ImageFeaturePipeline::builder()
.enable_shape_descriptors(true)
.enable_fractal(true)
.build();
let image = Array2::from_shape_vec((32, 32), (0..1024).map(|x| x as f64).collect())
.expect("operation should succeed");
let features = pipeline
.extract_all_features(&image.view())
.expect("operation should succeed");
assert!(!features.is_empty());
}
#[test]
fn test_empty_pipeline() {
let pipeline = ImageFeaturePipeline::builder().build();
let image = Array2::from_shape_vec((10, 10), (0..100).map(|x| x as f64).collect())
.expect("operation should succeed");
let result = pipeline.extract_all_features(&image.view());
assert!(result.is_err());
}
}