scirs2-ndimage 0.4.1

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
568
//! Visualization and Reporting Utilities for Image Processing Results
//!
//! This module provides comprehensive tools for creating visual representations of
//! image processing results, statistical plots, and comprehensive analysis reports.
//! Designed for scientific documentation and presentation of image analysis workflows.
//!
//! The module is organized into focused sub-modules for better maintainability:
//!
//! - [`types`] - Core data structures, enums, and configuration types
//! - [`colormap`] - Color map implementations for scientific visualization
//! - [`plotting`] - Basic plotting functions (histograms, profiles, heatmaps, etc.)
//! - [`reports`] - Comprehensive report generation system
//! - [`statistical`] - Statistical visualization and comparison functions
//!
//! # Examples
//!
//! ## Basic Plotting
//!
//! ```rust
//! use scirs2_core::ndarray::Array2;
//! use scirs2_ndimage::visualization::{PlotConfig, ColorMap, ReportFormat, plot_histogram};
//!
//! let data = Array2::from_shape_fn((100, 100), |(i, j)| {
//!     ((i as f64).sin() * (j as f64).cos()).abs()
//! });
//!
//! let config = PlotConfig::new()
//!     .with_colormap(ColorMap::Viridis)
//!     .with_format(ReportFormat::Html)
//!     .with_title("Sample Heatmap");
//!
//! let histogram = plot_histogram(&data.view().into_shape_with_order(10000)?.view(), &config)?;
//! # Ok::<(), Box<dyn std::error::Error>>(())
//! ```
//!
//! ## Report Generation
//!
//! ```rust
//! use scirs2_core::ndarray::Array2;
//! use scirs2_ndimage::visualization::{ReportConfig, ReportFormat, generate_report};
//!
//! let image = Array2::from_shape_fn((50, 50), |(i, j)| {
//!     (i + j) as f64 / 100.0
//! });
//!
//! let config = ReportConfig::new()
//!     .with_format(ReportFormat::Markdown)
//!     .with_header("Analysis Report", "SciRS2 NDImage")
//!     .with_all_sections();
//!
//! let report = generate_report(&image.view(), None, None, &config)?;
//! # Ok::<(), Box<dyn std::error::Error>>(())
//! ```
//!
//! ## Statistical Comparison
//!
//! ```rust
//! use scirs2_core::ndarray::Array1;
//! use scirs2_ndimage::visualization::{PlotConfig, ReportFormat, plot_statistical_comparison};
//!
//! let control = Array1::from_vec(vec![1.0, 2.0, 3.0, 4.0, 5.0]);
//! let treatment = Array1::from_vec(vec![2.0, 3.0, 4.0, 5.0, 6.0]);
//!
//! let datasets = vec![
//!     ("Control Group", control.view()),
//!     ("Treatment Group", treatment.view()),
//! ];
//!
//! let config = PlotConfig::new()
//!     .with_format(ReportFormat::Markdown)
//!     .with_title("Group Comparison");
//!
//! let comparison = plot_statistical_comparison(&datasets, &config)?;
//! # Ok::<(), Box<dyn std::error::Error>>(())
//! ```

use scirs2_core::ndarray::ArrayStatCompat;

// Sub-module declarations
pub mod colormap;
pub mod plotting;
pub mod reports;
pub mod statistical;
pub mod types;

// Re-export all core types for backward compatibility
pub use types::{ColorMap, ColorSchemes, PlotConfig, ReportConfig, ReportFormat, RgbColor};

// Re-export all colormap functions
pub use colormap::{
    apply_colormap_to_array, autumn_colormap, cool_colormap, create_colormap,
    get_colormap_function, gray_colormap, hot_colormap, inferno_colormap, jet_colormap,
    plasma_colormap, spring_colormap, summer_colormap, viridis_colormap, winter_colormap,
};

// Re-export all plotting functions
pub use plotting::{
    plot_contour, plot_gradient, plot_heatmap, plot_histogram, plot_profile, plot_surface,
    visualize_gradient,
};

// Re-export all report generation functions
pub use reports::{
    add_basic_statistics, add_image_info, add_quality_metrics, add_texture_metrics, generate_report,
};

// Re-export all statistical visualization functions
pub use statistical::{
    calculate_dataset_statistics, create_image_montage, plot_correlation_matrix,
    plot_statistical_comparison,
};

// Legacy aliases for backward compatibility
/// Legacy alias for `create_image_montage`
pub use statistical::create_image_montage as createimage_montage;

/// Legacy alias for `add_image_info`
pub use reports::add_image_info as addimage_info;

/// Legacy alias for `add_quality_metrics`
pub use reports::add_quality_metrics as add_qualitymetrics;

/// Legacy alias for `add_texture_metrics`
pub use reports::add_texture_metrics as addtexturemetrics;

// Export utilities module inline for compatibility
/// Export utilities for saving visualization output to files
pub mod export {
    use super::reports::generate_report;
    use super::types::{ReportConfig, ReportFormat};
    use crate::analysis::{ImageQualityMetrics, TextureMetrics};
    use crate::error::{NdimageError, NdimageResult};
    use scirs2_core::ndarray::ArrayView2;
    use scirs2_core::numeric::{Float, FromPrimitive, ToPrimitive};
    use std::fmt::Debug;
    use std::fs;
    use std::path::Path;

    /// Export configuration for file output
    #[derive(Debug, Clone)]
    pub struct ExportConfig {
        /// Output file path
        pub output_path: String,
        /// Image quality (for compressed formats)
        pub quality: Option<u8>,
        /// DPI for vector formats
        pub dpi: Option<u32>,
        /// Whether to include metadata
        pub include_metadata: bool,
    }

    impl Default for ExportConfig {
        fn default() -> Self {
            Self {
                output_path: "output.html".to_string(),
                quality: Some(95),
                dpi: Some(300),
                include_metadata: true,
            }
        }
    }

    /// Save a generated plot to file
    pub fn save_plot(content: &str, config: &ExportConfig) -> NdimageResult<()> {
        let path = Path::new(&config.output_path);

        // Create parent directories if they don't exist
        if let Some(parent) = path.parent() {
            fs::create_dir_all(parent).map_err(|e| {
                NdimageError::ComputationError(format!("Failed to create directory: {}", e))
            })?;
        }

        // Add metadata if requested
        let mut output_content = content.to_string();
        if config.include_metadata {
            let timestamp = std::time::SystemTime::now()
                .duration_since(std::time::UNIX_EPOCH)
                .unwrap_or_default()
                .as_secs();

            let metadata = format!(
                "\n<!-- Generated by scirs2-ndimage visualization module at {} -->\n",
                timestamp
            );

            if content.contains("</html>") {
                output_content = content.replace("</html>", &format!("{}</html>", metadata));
            } else if content.contains("# ") {
                output_content = format!(
                    "{}\n{}",
                    content,
                    metadata.replace("<!--", "").replace("-->", "")
                );
            } else {
                output_content = format!(
                    "{}\n{}",
                    content,
                    metadata.replace("<!--", "").replace("-->", "")
                );
            }
        }

        fs::write(path, output_content)
            .map_err(|e| NdimageError::ComputationError(format!("Failed to write file: {}", e)))?;

        Ok(())
    }

    /// Generate and save a comprehensive analysis report
    pub fn export_analysis_report<T>(
        image: &ArrayView2<T>,
        qualitymetrics: Option<&ImageQualityMetrics<T>>,
        texturemetrics: Option<&TextureMetrics<T>>,
        output_path: &str,
        format: ReportFormat,
    ) -> NdimageResult<()>
    where
        T: Float + FromPrimitive + ToPrimitive + Debug + Clone,
    {
        let config = ReportConfig {
            title: "Comprehensive Image Analysis Report".to_string(),
            author: "SciRS2 NDImage".to_string(),
            format,
            ..ReportConfig::default()
        };

        let report = generate_report(image, qualitymetrics, texturemetrics, &config)?;

        let export_config = ExportConfig {
            output_path: output_path.to_string(),
            ..ExportConfig::default()
        };

        save_plot(&report, &export_config)?;
        Ok(())
    }
}

// Advanced visualization utilities module inline for compatibility
/// Advanced visualization utilities
pub mod advanced {
    use super::plotting::plot_heatmap;
    use super::types::{ColorMap, PlotConfig, ReportFormat};
    use crate::error::{NdimageError, NdimageResult};
    use scirs2_core::ndarray::{ArrayStatCompat, ArrayView2};
    use scirs2_core::numeric::{Float, FromPrimitive, ToPrimitive, Zero};
    use std::fmt::{Debug, Write};

    /// Create an interactive HTML visualization with multiple views
    pub fn create_interactive_visualization<T>(
        image: &ArrayView2<T>,
        title: &str,
    ) -> NdimageResult<String>
    where
        T: Float + FromPrimitive + ToPrimitive + Debug + Clone,
    {
        let mut html = String::new();

        writeln!(&mut html, "<!DOCTYPE html>")?;
        writeln!(&mut html, "<html><head>")?;
        writeln!(&mut html, "<title>{}</title>", title)?;
        writeln!(&mut html, "<style>")?;
        writeln!(
            &mut html,
            r#"
            body {{ font-family: Arial, sans-serif; margin: 20px; }}
            .visualization-container {{ display: flex; flex-wrap: wrap; gap: 20px; }}
            .plot-panel {{ border: 1px solid #ccc; padding: 15px; border-radius: 5px; min-width: 300px; }}
            .plot-title {{ font-weight: bold; margin-bottom: 10px; color: #333; }}
            .controls {{ margin-bottom: 15px; }}
            .control-group {{ margin-bottom: 10px; }}
            label {{ display: inline-block; width: 100px; }}
            select, input {{ margin-left: 10px; }}
            .stats-grid {{ display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }}
            .stat-item {{ background: #f5f5f5; padding: 8px; border-radius: 3px; }}
            .heatmap-container {{ position: relative; width: 400px; height: 300px; }}
            .colorbar {{ width: 20px; height: 300px; background: linear-gradient(to top, blue, cyan, yellow, red); }}
        "#
        )?;
        writeln!(&mut html, "</style>")?;
        writeln!(&mut html, "<script>")?;
        writeln!(
            &mut html,
            r#"
            function updateVisualization() {{
                const colormap = document.getElementById('colormap').value;
                const plotType = document.getElementById('plotType').value;
                // Update visualization based on controls
                console.log('Updating visualization:', colormap, plotType);
            }}

            function exportView() {{
                const content = document.getElementById('main-content').innerHTML;
                const blob = new Blob([content], {{ type: 'text/html' }});
                const url = URL.createObjectURL(blob);
                const a = document.createElement('a');
                a.href = url;
                a.download = 'visualization.html';
                a.click();
                URL.revokeObjectURL(url);
            }}
        "#
        )?;
        writeln!(&mut html, "</script>")?;
        writeln!(&mut html, "</head><body>")?;

        writeln!(&mut html, "<h1>{}</h1>", title)?;

        // Controls panel
        writeln!(&mut html, "<div class='controls'>")?;
        writeln!(&mut html, "<div class='control-group'>")?;
        writeln!(&mut html, "<label>Color Map:</label>")?;
        writeln!(
            &mut html,
            r#"<select id="colormap" onchange="updateVisualization()">"#
        )?;
        writeln!(&mut html, "<option value='viridis'>Viridis</option>")?;
        writeln!(&mut html, "<option value='plasma'>Plasma</option>")?;
        writeln!(&mut html, "<option value='jet'>Jet</option>")?;
        writeln!(&mut html, "<option value='hot'>Hot</option>")?;
        writeln!(&mut html, "</select>")?;
        writeln!(&mut html, "</div>")?;

        writeln!(&mut html, "<div class='control-group'>")?;
        writeln!(&mut html, "<label>Plot Type:</label>")?;
        writeln!(
            &mut html,
            r#"<select id="plotType" onchange="updateVisualization()">"#
        )?;
        writeln!(&mut html, "<option value='heatmap'>Heatmap</option>")?;
        writeln!(&mut html, "<option value='contour'>Contour</option>")?;
        writeln!(&mut html, "<option value='surface'>3D Surface</option>")?;
        writeln!(&mut html, "</select>")?;
        writeln!(&mut html, "</div>")?;

        writeln!(
            &mut html,
            r#"<button onclick="exportView()">Export View</button>"#
        )?;
        writeln!(&mut html, "</div>")?;

        writeln!(
            &mut html,
            "<div id='main-content' class='visualization-container'>"
        )?;

        // Statistics panel
        let (height, width) = image.dim();
        let mean = image.mean_or(T::zero());
        let min_val = image.iter().cloned().fold(T::infinity(), T::min);
        let max_val = image.iter().cloned().fold(T::neg_infinity(), T::max);

        writeln!(&mut html, "<div class='plot-panel'>")?;
        writeln!(&mut html, "<div class='plot-title'>Image Statistics</div>")?;
        writeln!(&mut html, "<div class='stats-grid'>")?;
        writeln!(&mut html, "<div class='stat-item'>Width: {}</div>", width)?;
        writeln!(&mut html, "<div class='stat-item'>Height: {}</div>", height)?;
        writeln!(
            &mut html,
            "<div class='stat-item'>Mean: {:.4}</div>",
            mean.to_f64().unwrap_or(0.0)
        )?;
        writeln!(
            &mut html,
            "<div class='stat-item'>Min: {:.4}</div>",
            min_val.to_f64().unwrap_or(0.0)
        )?;
        writeln!(
            &mut html,
            "<div class='stat-item'>Max: {:.4}</div>",
            max_val.to_f64().unwrap_or(0.0)
        )?;
        writeln!(
            &mut html,
            "<div class='stat-item'>Pixels: {}</div>",
            width * height
        )?;
        writeln!(&mut html, "</div>")?;
        writeln!(&mut html, "</div>")?;

        // Heatmap panel
        writeln!(&mut html, "<div class='plot-panel'>")?;
        writeln!(&mut html, "<div class='plot-title'>Heatmap View</div>")?;
        writeln!(&mut html, "<div class='heatmap-container'>")?;

        // Generate a simplified heatmap representation
        let config = PlotConfig {
            format: ReportFormat::Html,
            colormap: ColorMap::Viridis,
            title: "Interactive Heatmap".to_string(),
            ..PlotConfig::default()
        };

        let heatmap = plot_heatmap(image, &config)?;
        writeln!(&mut html, "{}", heatmap)?;

        writeln!(&mut html, "</div>")?;
        writeln!(&mut html, "</div>")?;

        writeln!(&mut html, "</div>")?;
        writeln!(&mut html, "</body></html>")?;

        Ok(html)
    }

    /// Create a comparison visualization between multiple images
    pub fn create_comparison_view<T>(
        images: &[(&str, ArrayView2<T>)],
        title: &str,
    ) -> NdimageResult<String>
    where
        T: Float + FromPrimitive + ToPrimitive + Debug + Clone,
    {
        if images.is_empty() {
            return Err(NdimageError::InvalidInput(
                "No images provided for comparison".into(),
            ));
        }

        let mut html = String::new();

        writeln!(&mut html, "<!DOCTYPE html>")?;
        writeln!(&mut html, "<html><head>")?;
        writeln!(&mut html, "<title>{}</title>", title)?;
        writeln!(&mut html, "<style>")?;
        writeln!(
            &mut html,
            r#"
            body {{ font-family: Arial, sans-serif; margin: 20px; }}
            .comparison-grid {{ display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; }}
            .image-panel {{ border: 1px solid #ccc; padding: 15px; border-radius: 5px; }}
            .image-title {{ font-weight: bold; margin-bottom: 10px; color: #333; text-align: center; }}
            .image-stats {{ background: #f9f9f9; padding: 10px; margin-top: 10px; border-radius: 3px; }}
            .stat-row {{ display: flex; justify-content: space-between; margin-bottom: 5px; }}
            .difference-highlight {{ background: #ffe6e6; }}
        "#
        )?;
        writeln!(&mut html, "</style>")?;
        writeln!(&mut html, "</head><body>")?;

        writeln!(&mut html, "<h1>{}</h1>", title)?;
        writeln!(&mut html, "<div class='comparison-grid'>")?;

        for (name, image) in images {
            writeln!(&mut html, "<div class='image-panel'>")?;
            writeln!(&mut html, "<div class='image-title'>{}</div>", name)?;

            // Generate heatmap for this image
            let config = PlotConfig {
                format: ReportFormat::Html,
                colormap: ColorMap::Viridis,
                title: name.to_string(),
                width: 250,
                height: 200,
                ..PlotConfig::default()
            };

            let heatmap = plot_heatmap(image, &config)?;
            writeln!(&mut html, "{}", heatmap)?;

            // Add statistics
            let (height, width) = image.dim();
            let mean = image.mean_or(T::zero());
            let min_val = image.iter().cloned().fold(T::infinity(), T::min);
            let max_val = image.iter().cloned().fold(T::neg_infinity(), T::max);

            writeln!(&mut html, "<div class='image-stats'>")?;
            writeln!(
                &mut html,
                "<div class='stat-row'><span>Dimensions:</span><span>{}×{}</span></div>",
                height, width
            )?;
            writeln!(
                &mut html,
                "<div class='stat-row'><span>Mean:</span><span>{:.4}</span></div>",
                mean.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                &mut html,
                "<div class='stat-row'><span>Range:</span><span>[{:.3}, {:.3}]</span></div>",
                min_val.to_f64().unwrap_or(0.0),
                max_val.to_f64().unwrap_or(0.0)
            )?;
            writeln!(&mut html, "</div>")?;

            writeln!(&mut html, "</div>")?;
        }

        writeln!(&mut html, "</div>")?;
        writeln!(&mut html, "</body></html>")?;

        Ok(html)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use scirs2_core::ndarray::{Array1, Array2};

    #[test]
    fn test_module_exports() {
        // Test that all re-exports are available
        let _config = PlotConfig::new();
        let _colormap = ColorMap::Viridis;
        let _format = ReportFormat::Html;
        let _color = RgbColor::new(255, 0, 0);
    }

    #[test]
    fn test_backward_compatibility_aliases() {
        let img1 = Array2::<f64>::zeros((5, 5));
        let img2 = Array2::<f64>::ones((5, 5));
        let images = vec![img1.view(), img2.view()];
        let config = PlotConfig::new().with_format(ReportFormat::Text);

        // Test legacy alias
        let result = createimage_montage(&images, 2, &config);
        assert!(result.is_ok());
    }

    #[test]
    fn test_statistical_functions() {
        let data1 = Array1::from_vec(vec![1.0, 2.0, 3.0, 4.0, 5.0]);
        let data2 = Array1::from_vec(vec![2.0, 4.0, 6.0, 8.0, 10.0]);

        let datasets = vec![("Test A", data1.view()), ("Test B", data2.view())];

        let config = PlotConfig::new()
            .with_format(ReportFormat::Text)
            .with_title("Statistical Test");

        let result = plot_statistical_comparison(&datasets, &config);
        assert!(result.is_ok());
    }

    #[test]
    fn test_plotting_functions() {
        let data = Array1::from_vec(vec![1.0, 2.0, 3.0, 4.0, 5.0]);
        let config = PlotConfig::new()
            .with_format(ReportFormat::Text)
            .with_title("Histogram Test");

        let result = plot_histogram(&data.view(), &config);
        assert!(result.is_ok());
    }

    #[test]
    fn test_colormap_functions() {
        let colors = create_colormap(ColorMap::Viridis, 10);
        assert_eq!(colors.len(), 10);

        let color = viridis_colormap(0.5);
        assert!(color.r > 0 || color.g > 0 || color.b > 0);
    }

    #[test]
    fn test_report_generation() {
        let image = Array2::<f64>::ones((10, 10));
        let config = ReportConfig::new()
            .with_format(ReportFormat::Text)
            .minimal();

        let result = generate_report(&image.view(), None, None, &config);
        assert!(result.is_ok());
    }
}