scirs2-ndimage 0.6.0

N-dimensional image processing module for SciRS2 (scirs2-ndimage)
Documentation
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
//! Comprehensive image analysis example demonstrating advanced features
//!
//! This example showcases the enhanced capabilities of scirs2-ndimage including:
//! - Advanced image quality assessment
//! - Texture analysis
//! - Multi-scale analysis
//! - Visualization and reporting
//! - Advanced filtering with wavelets and Gabor filters
//! - GPU backend device detection

use scirs2_core::ndarray::{Array1, Array2};
use scirs2_ndimage::{
    // Analysis functions
    analysis::{
        image_entropy, image_quality_assessment, image_sharpness, multi_scale_analysis,
        peak_signal_to_noise_ratio, structural_similarity_index, texture_analysis,
        MultiScaleConfig,
    },
    // Backend detection
    backend::{auto_backend, DeviceManager},
    // Error handling
    error::NdimageResult,
    // Advanced filters
    filters::{
        advanced::{gabor_filter, log_gabor_filter, steerable_filter, GaborParams},
        wavelets::{wavelet_denoise, WaveletFamily, WaveletFilter},
        BorderMode,
    },
    // Standard filters
    filters::{gaussian_filter, sobel},
    // Visualization functions
    visualization::{
        create_colormap, generate_report, plot_histogram, ColorMap, PlotConfig, ReportConfig,
        ReportFormat,
    },
};

#[allow(dead_code)]
fn main() -> NdimageResult<()> {
    println!("=== SciRS2 N-Dimensional Image Processing - Comprehensive Analysis Demo ===\n");

    // 1. Device Detection and Backend Capabilities
    demonstrate_device_detection()?;

    // 2. Create a synthetic test image
    let testimage = create_testimage();
    println!(
        "Created synthetic test image ({}×{})",
        testimage.nrows(),
        testimage.ncols()
    );

    // 3. Advanced Image Quality Assessment
    demonstrate_quality_assessment(&testimage)?;

    // 4. Texture Analysis
    demonstratetexture_analysis(&testimage)?;

    // 5. Advanced Filtering Techniques
    demonstrate_advanced_filtering(&testimage)?;

    // 6. Wavelet Analysis and Denoising
    demonstrate_wavelet_analysis(&testimage)?;

    // 7. Multi-scale Analysis
    demonstrate_multiscale_analysis(&testimage)?;

    // 8. Visualization and Reporting
    demonstrate_visualization(&testimage)?;

    // 9. Performance Comparison
    demonstrate_performance_comparison(&testimage)?;

    println!("\n=== Analysis Complete ===");
    println!("This example demonstrates the comprehensive image processing");
    println!("capabilities of SciRS2 including advanced analysis, visualization,");
    println!("and GPU backend support for scientific image processing workflows.");

    Ok(())
}

#[allow(dead_code)]
fn demonstrate_device_detection() -> NdimageResult<()> {
    println!("1. GPU Backend Device Detection");
    println!("-------------------------------");

    // Try to create device manager
    match scirs2_ndimage::backend::device_detection::get_device_manager() {
        Ok(manager_arc) => {
            let manager = manager_arc.lock().expect("Operation failed");

            // Check different backends
            let backends = [
                #[cfg(feature = "cuda")]
                scirs2_ndimage::backend::Backend::Cuda,
                #[cfg(feature = "opencl")]
                scirs2_ndimage::backend::Backend::OpenCL,
                #[cfg(all(target_os = "macos", feature = "metal"))]
                scirs2_ndimage::backend::Backend::Metal,
                scirs2_ndimage::backend::Backend::Cpu,
            ];

            for backend in &backends {
                let available = manager.is_backend_available(*backend);
                let device_count = manager.device_count(*backend);
                println!(
                    "  {:?}: {} (devices: {})",
                    backend,
                    if available {
                        "Available"
                    } else {
                        "Not Available"
                    },
                    device_count
                );

                if available && device_count > 0 {
                    if let Some(device_info) = manager.get_device_info(*backend, 0) {
                        println!("    Primary device: {}", device_info.name);
                        println!(
                            "    Total memory: {:.1} GB",
                            device_info.total_memory as f64 / (1024.0 * 1024.0 * 1024.0)
                        );
                        if let Some(compute_cap) = device_info.compute_capability {
                            println!(
                                "    Compute capability: {}.{}",
                                compute_cap.0, compute_cap.1
                            );
                        }
                    }
                }
            }

            // Try auto backend selection
            match auto_backend() {
                Ok(_executor) => println!("  Auto backend executor: Successfully created"),
                Err(e) => println!("  Auto backend executor: Failed ({})", e),
            }
        }
        Err(e) => {
            println!("  Device manager: Failed to initialize ({})", e);
            println!("  Note: This is expected in environments without GPU libraries");
        }
    }

    println!();
    Ok(())
}

#[allow(dead_code)]
fn create_testimage() -> Array2<f64> {
    let (height, width) = (128, 128);
    let mut image = Array2::zeros((height, width));

    // Create a complex test pattern with multiple features
    for i in 0..height {
        for j in 0..width {
            let x = j as f64 / width as f64;
            let y = i as f64 / height as f64;

            // Combine multiple patterns
            let mut value = 0.0;

            // Radial gradient
            let center_x = 0.5;
            let center_y = 0.5;
            let dist = ((x - center_x).powi(2) + (y - center_y).powi(2)).sqrt();
            value += 0.3 * (1.0 - dist);

            // Sine wave pattern
            value += 0.2 * (8.0 * std::f64::consts::PI * x).sin();

            // Checkerboard pattern
            if ((i / 16) + (j / 16)) % 2 == 0 {
                value += 0.3;
            }

            // Gaussian blob
            let blob_dist = ((x - 0.7).powi(2) + (y - 0.3).powi(2)).sqrt();
            value += 0.4 * (-blob_dist * blob_dist * 50.0).exp();

            // Add some noise
            value += 0.05 * scirs2_core::random::random::<f64>() - 0.025;

            image[[i, j]] = value.clamp(0.0, 1.0);
        }
    }

    image
}

#[allow(dead_code)]
fn demonstrate_quality_assessment(image: &Array2<f64>) -> NdimageResult<()> {
    println!("2. Advanced Image Quality Assessment");
    println!("------------------------------------");

    // Create a slightly modified version for comparison
    let noisyimage =
        image.mapv(|x| (x + 0.02 * scirs2_core::random::random::<f64>()).clamp(0.0, 1.0));

    // Compute comprehensive quality metrics
    let metrics = image_quality_assessment(&image.view(), &noisyimage.view())?;

    println!("  Quality Metrics (comparing original vs. noisy):");
    println!("    PSNR:           {:.2} dB", metrics.psnr);
    println!("    SSIM:           {:.4}", metrics.ssim);
    println!("    MSE:            {:.6}", metrics.mse);
    println!("    RMSE:           {:.6}", metrics.rmse);
    println!("    MAE:            {:.6}", metrics.mae);
    println!("    SNR:            {:.2} dB", metrics.snr);
    println!("    CNR:            {:.2}", metrics.cnr);
    println!("    Entropy:        {:.2} bits", metrics.entropy);
    println!("    Sharpness:      {:.6}", metrics.sharpness);
    println!("    Local Variance: {:.6}", metrics.local_variance);

    // Individual metric demonstrations
    let entropy = image_entropy(&image.view())?;
    let sharpness = image_sharpness(&image.view())?;
    let psnr = peak_signal_to_noise_ratio(&image.view(), &noisyimage.view())?;
    let ssim = structural_similarity_index(&image.view(), &noisyimage.view())?;

    println!("\n  Individual Metric Validation:");
    println!("    Image entropy:  {:.2} bits", entropy);
    println!("    Image sharpness:{:.6}", sharpness);
    println!("    PSNR validation:{:.2} dB", psnr);
    println!("    SSIM validation:{:.4}", ssim);

    println!();
    Ok(())
}

#[allow(dead_code)]
fn demonstratetexture_analysis(image: &Array2<f64>) -> NdimageResult<()> {
    println!("3. Comprehensive Texture Analysis");
    println!("----------------------------------");

    let texturemetrics = texture_analysis(&image.view(), Some(5))?;

    println!("  Texture Metrics:");
    println!("    GLCM Contrast:     {:.6}", texturemetrics.glcm_contrast);
    println!(
        "    GLCM Dissimilarity:{:.6}",
        texturemetrics.glcm_dissimilarity
    );
    println!(
        "    GLCM Homogeneity:  {:.6}",
        texturemetrics.glcm_homogeneity
    );
    println!("    GLCM Energy:       {:.6}", texturemetrics.glcm_energy);
    println!(
        "    GLCM Correlation:  {:.6}",
        texturemetrics.glcm_correlation
    );
    println!(
        "    LBP Uniformity:    {:.6}",
        texturemetrics.lbp_uniformity
    );
    println!("    Gabor Mean:        {:.6}", texturemetrics.gabor_mean);
    println!("    Gabor Std:         {:.6}", texturemetrics.gabor_std);
    println!(
        "    Fractal Dimension: {:.3}",
        texturemetrics.fractal_dimension
    );

    println!();
    Ok(())
}

#[allow(dead_code)]
fn demonstrate_advanced_filtering(image: &Array2<f64>) -> NdimageResult<()> {
    println!("4. Advanced Filtering Techniques");
    println!("--------------------------------");

    // Gabor filtering for texture analysis
    let gabor_params = GaborParams {
        wavelength: 8.0,
        orientation: std::f64::consts::PI / 4.0, // 45 degrees
        sigma_x: 3.0,
        sigma_y: 3.0,
        phase: 0.0,
        aspect_ratio: None,
    };

    let gabor_result = gabor_filter(
        &image.view(),
        &gabor_params,
        Some(15),
        Some(BorderMode::Reflect),
    )?;
    println!("  Gabor filter: Applied (kernel size: 15×15, orientation: 45°)");
    println!(
        "    Output range: [{:.3}, {:.3}]",
        gabor_result.iter().cloned().fold(f64::INFINITY, f64::min),
        gabor_result
            .iter()
            .cloned()
            .fold(f64::NEG_INFINITY, f64::max)
    );

    // Log-Gabor filtering (frequency domain)
    match log_gabor_filter(&image.view(), 0.1, 1.0, 0.0, std::f64::consts::PI / 6.0) {
        Ok(log_gabor_result) => {
            println!("  Log-Gabor filter: Applied (center freq: 0.1, bandwidth: 1.0 octave)");
            println!(
                "    Output range: [{:.3}, {:.3}]",
                log_gabor_result
                    .iter()
                    .cloned()
                    .fold(f64::INFINITY, f64::min),
                log_gabor_result
                    .iter()
                    .cloned()
                    .fold(f64::NEG_INFINITY, f64::max)
            );
        }
        Err(e) => println!("  Log-Gabor filter: Skipped ({})", e),
    }

    // Steerable filtering
    let steerable_result = steerable_filter(
        &image.view(),
        2,
        std::f64::consts::PI / 6.0,
        2.0,
        Some(BorderMode::Reflect),
    )?;
    println!("  Steerable filter: Applied (order: 2, orientation: 30°, sigma: 2.0)");
    println!(
        "    Output range: [{:.3}, {:.3}]",
        steerable_result
            .iter()
            .cloned()
            .fold(f64::INFINITY, f64::min),
        steerable_result
            .iter()
            .cloned()
            .fold(f64::NEG_INFINITY, f64::max)
    );

    // Standard filters for comparison
    let gaussian_result = gaussian_filter(image, 2.0, None, None)?;
    println!("  Gaussian filter: Applied (sigma: 2.0)");
    println!(
        "    Output range: [{:.3}, {:.3}]",
        gaussian_result
            .iter()
            .cloned()
            .fold(f64::INFINITY, f64::min),
        gaussian_result
            .iter()
            .cloned()
            .fold(f64::NEG_INFINITY, f64::max)
    );

    let sobel_result = sobel(image, 0, None)?;
    println!("  Sobel edge detection: Applied");
    println!(
        "    Output range: [{:.3}, {:.3}]",
        sobel_result.iter().cloned().fold(f64::INFINITY, f64::min),
        sobel_result
            .iter()
            .cloned()
            .fold(f64::NEG_INFINITY, f64::max)
    );

    println!();
    Ok(())
}

#[allow(dead_code)]
fn demonstrate_wavelet_analysis(image: &Array2<f64>) -> NdimageResult<()> {
    println!("5. Wavelet Analysis and Denoising");
    println!("----------------------------------");

    // Add noise for denoising demonstration
    let noisyimage =
        image.mapv(|x| (x + 0.05 * scirs2_core::random::random::<f64>()).clamp(0.0, 1.0));

    // Test different wavelet families
    let wavelets = [
        ("Haar", WaveletFamily::Haar),
        ("Daubechies-2", WaveletFamily::Daubechies(2)),
        ("Daubechies-4", WaveletFamily::Daubechies(4)),
    ];

    for (name, family) in &wavelets {
        match WaveletFilter::new(*family).and_then(|filter| {
            wavelet_denoise(&noisyimage.view(), &filter, 0.05, 3, BorderMode::Reflect)
        }) {
            Ok(denoised) => {
                let original_mse = noisyimage
                    .iter()
                    .zip(image.iter())
                    .map(|(&noisy, &orig)| (noisy - orig).powi(2))
                    .sum::<f64>()
                    / (image.len() as f64);

                let denoised_mse = denoised
                    .iter()
                    .zip(image.iter())
                    .map(|(&denoised_val, &orig)| (denoised_val - orig).powi(2))
                    .sum::<f64>()
                    / (image.len() as f64);

                println!("  {} wavelet denoising:", name);
                println!("    Noisy MSE:    {:.6}", original_mse);
                println!("    Denoised MSE: {:.6}", denoised_mse);
                println!(
                    "    Improvement:  {:.1}%",
                    (1.0 - denoised_mse / original_mse) * 100.0
                );
            }
            Err(e) => println!("  {} wavelet: Failed ({})", name, e),
        }
    }

    println!();
    Ok(())
}

#[allow(dead_code)]
fn demonstrate_multiscale_analysis(image: &Array2<f64>) -> NdimageResult<()> {
    println!("6. Multi-scale Analysis");
    println!("-----------------------");

    let config = MultiScaleConfig {
        num_scales: 4,
        scale_factor: 2.0,
        min_size: 16,
    };

    let multiscale_results = multi_scale_analysis(&image.view(), &config)?;

    println!(
        "  Multi-scale quality analysis ({} scales):",
        multiscale_results.len()
    );
    for (i, metrics) in multiscale_results.iter().enumerate() {
        let scale_size = image.nrows() / (2_usize.pow(i as u32));
        println!(
            "    Scale {}: {}×{} - Entropy: {:.2}, Sharpness: {:.4}",
            i + 1,
            scale_size,
            scale_size,
            metrics.entropy,
            metrics.sharpness
        );
    }

    println!();
    Ok(())
}

#[allow(dead_code)]
fn demonstrate_visualization(image: &Array2<f64>) -> NdimageResult<()> {
    println!("7. Visualization and Reporting");
    println!("------------------------------");

    // Create histogram data
    let flat_data: Array1<f64> = image.iter().cloned().collect();

    // Generate histogram plot
    let plot_config = PlotConfig {
        title: "Image Intensity Histogram".to_string(),
        xlabel: "Intensity".to_string(),
        ylabel: "Frequency".to_string(),
        num_bins: 50,
        format: ReportFormat::Text,
        ..PlotConfig::default()
    };

    match plot_histogram(&flat_data.view(), &plot_config) {
        Ok(histogram_plot) => {
            println!("  Histogram plot generated:");
            // Show just the first few lines
            for line in histogram_plot.lines().take(5) {
                println!("    {}", line);
            }
            println!("    ... (truncated)");
        }
        Err(e) => println!("  Histogram generation failed: {}", e),
    }

    // Generate comprehensive report
    let report_config = ReportConfig {
        title: "SciRS2 Image Analysis Report".to_string(),
        author: "Comprehensive Analysis Example".to_string(),
        format: ReportFormat::Markdown,
        ..ReportConfig::default()
    };

    match generate_report(&image.view(), None, None, &report_config) {
        Ok(report) => {
            println!("\n  Analysis report generated:");
            // Show first few lines of the report
            for line in report.lines().take(10) {
                println!("    {}", line);
            }
            println!("    ... (report continues)");
        }
        Err(e) => println!("  Report generation failed: {}", e),
    }

    // Demonstrate colormap creation
    let viridis_colors = create_colormap(ColorMap::Viridis, 5);
    println!("\n  Viridis colormap (5 colors):");
    for (i, color) in viridis_colors.iter().enumerate() {
        println!(
            "    Color {}: {} (R:{}, G:{}, B:{})",
            i,
            color.to_hex(),
            color.r,
            color.g,
            color.b
        );
    }

    println!();
    Ok(())
}

#[allow(dead_code)]
fn demonstrate_performance_comparison(image: &Array2<f64>) -> NdimageResult<()> {
    println!("8. Performance Characteristics");
    println!("------------------------------");

    use std::time::Instant;

    // Gaussian filter timing
    let start = Instant::now();
    let _gaussian = gaussian_filter(image, 2.0, None, None)?;
    let gaussian_time = start.elapsed();

    // Sobel filter timing
    let start = Instant::now();
    let _sobel = sobel(image, 0, None)?;
    let sobel_time = start.elapsed();

    // Gabor filter timing
    let gabor_params = GaborParams::default();
    let start = Instant::now();
    let _gabor = gabor_filter(&image.view(), &gabor_params, Some(15), None)?;
    let gabor_time = start.elapsed();

    println!(
        "  Processing times for {}×{} image:",
        image.nrows(),
        image.ncols()
    );
    println!("    Gaussian filter: {:?}", gaussian_time);
    println!("    Sobel filter:    {:?}", sobel_time);
    println!("    Gabor filter:    {:?}", gabor_time);

    // Memory usage estimation
    let image_size_mb = (image.len() * std::mem::size_of::<f64>()) as f64 / (1024.0 * 1024.0);
    println!("\n  Memory characteristics:");
    println!("    Input image:     {:.2} MB", image_size_mb);
    println!(
        "    Typical overhead:{:.2} MB (for intermediate arrays)",
        image_size_mb * 2.0
    );

    println!();
    Ok(())
}

// Include rand for the random number generation in the example