1use scirs2_core::ndarray::ArrayStatCompat;
78
79pub mod colormap;
81pub mod plotting;
82pub mod reports;
83pub mod statistical;
84pub mod types;
85
86pub use types::{ColorMap, ColorSchemes, PlotConfig, ReportConfig, ReportFormat, RgbColor};
88
89pub use colormap::{
91 apply_colormap_to_array, autumn_colormap, cool_colormap, create_colormap,
92 get_colormap_function, gray_colormap, hot_colormap, inferno_colormap, jet_colormap,
93 plasma_colormap, spring_colormap, summer_colormap, viridis_colormap, winter_colormap,
94};
95
96pub use plotting::{
98 plot_contour, plot_gradient, plot_heatmap, plot_histogram, plot_profile, plot_surface,
99 visualize_gradient,
100};
101
102pub use reports::{
104 add_basic_statistics, add_image_info, add_quality_metrics, add_texture_metrics, generate_report,
105};
106
107pub use statistical::{
109 calculate_dataset_statistics, create_image_montage, plot_correlation_matrix,
110 plot_statistical_comparison,
111};
112
113pub use statistical::create_image_montage as createimage_montage;
116
117pub use reports::add_image_info as addimage_info;
119
120pub use reports::add_quality_metrics as add_qualitymetrics;
122
123pub use reports::add_texture_metrics as addtexturemetrics;
125
126pub mod export {
129 use super::reports::generate_report;
130 use super::types::{ReportConfig, ReportFormat};
131 use crate::analysis::{ImageQualityMetrics, TextureMetrics};
132 use crate::error::{NdimageError, NdimageResult};
133 use scirs2_core::ndarray::ArrayView2;
134 use scirs2_core::numeric::{Float, FromPrimitive, ToPrimitive};
135 use std::fmt::Debug;
136 use std::fs;
137 use std::path::Path;
138
139 #[derive(Debug, Clone)]
141 pub struct ExportConfig {
142 pub output_path: String,
144 pub quality: Option<u8>,
146 pub dpi: Option<u32>,
148 pub include_metadata: bool,
150 }
151
152 impl Default for ExportConfig {
153 fn default() -> Self {
154 Self {
155 output_path: "output.html".to_string(),
156 quality: Some(95),
157 dpi: Some(300),
158 include_metadata: true,
159 }
160 }
161 }
162
163 pub fn save_plot(content: &str, config: &ExportConfig) -> NdimageResult<()> {
165 let path = Path::new(&config.output_path);
166
167 if let Some(parent) = path.parent() {
169 fs::create_dir_all(parent).map_err(|e| {
170 NdimageError::ComputationError(format!("Failed to create directory: {}", e))
171 })?;
172 }
173
174 let mut output_content = content.to_string();
176 if config.include_metadata {
177 let timestamp = std::time::SystemTime::now()
178 .duration_since(std::time::UNIX_EPOCH)
179 .unwrap_or_default()
180 .as_secs();
181
182 let metadata = format!(
183 "\n<!-- Generated by scirs2-ndimage visualization module at {} -->\n",
184 timestamp
185 );
186
187 if content.contains("</html>") {
188 output_content = content.replace("</html>", &format!("{}</html>", metadata));
189 } else if content.contains("# ") {
190 output_content = format!(
191 "{}\n{}",
192 content,
193 metadata.replace("<!--", "").replace("-->", "")
194 );
195 } else {
196 output_content = format!(
197 "{}\n{}",
198 content,
199 metadata.replace("<!--", "").replace("-->", "")
200 );
201 }
202 }
203
204 fs::write(path, output_content)
205 .map_err(|e| NdimageError::ComputationError(format!("Failed to write file: {}", e)))?;
206
207 Ok(())
208 }
209
210 pub fn export_analysis_report<T>(
212 image: &ArrayView2<T>,
213 qualitymetrics: Option<&ImageQualityMetrics<T>>,
214 texturemetrics: Option<&TextureMetrics<T>>,
215 output_path: &str,
216 format: ReportFormat,
217 ) -> NdimageResult<()>
218 where
219 T: Float + FromPrimitive + ToPrimitive + Debug + Clone,
220 {
221 let config = ReportConfig {
222 title: "Comprehensive Image Analysis Report".to_string(),
223 author: "SciRS2 NDImage".to_string(),
224 format,
225 ..ReportConfig::default()
226 };
227
228 let report = generate_report(image, qualitymetrics, texturemetrics, &config)?;
229
230 let export_config = ExportConfig {
231 output_path: output_path.to_string(),
232 ..ExportConfig::default()
233 };
234
235 save_plot(&report, &export_config)?;
236 Ok(())
237 }
238}
239
240pub mod advanced {
243 use super::plotting::plot_heatmap;
244 use super::types::{ColorMap, PlotConfig, ReportFormat};
245 use crate::error::{NdimageError, NdimageResult};
246 use scirs2_core::ndarray::{ArrayStatCompat, ArrayView2};
247 use scirs2_core::numeric::{Float, FromPrimitive, ToPrimitive, Zero};
248 use std::fmt::{Debug, Write};
249
250 pub fn create_interactive_visualization<T>(
252 image: &ArrayView2<T>,
253 title: &str,
254 ) -> NdimageResult<String>
255 where
256 T: Float + FromPrimitive + ToPrimitive + Debug + Clone,
257 {
258 let mut html = String::new();
259
260 writeln!(&mut html, "<!DOCTYPE html>")?;
261 writeln!(&mut html, "<html><head>")?;
262 writeln!(&mut html, "<title>{}</title>", title)?;
263 writeln!(&mut html, "<style>")?;
264 writeln!(
265 &mut html,
266 r#"
267 body {{ font-family: Arial, sans-serif; margin: 20px; }}
268 .visualization-container {{ display: flex; flex-wrap: wrap; gap: 20px; }}
269 .plot-panel {{ border: 1px solid #ccc; padding: 15px; border-radius: 5px; min-width: 300px; }}
270 .plot-title {{ font-weight: bold; margin-bottom: 10px; color: #333; }}
271 .controls {{ margin-bottom: 15px; }}
272 .control-group {{ margin-bottom: 10px; }}
273 label {{ display: inline-block; width: 100px; }}
274 select, input {{ margin-left: 10px; }}
275 .stats-grid {{ display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }}
276 .stat-item {{ background: #f5f5f5; padding: 8px; border-radius: 3px; }}
277 .heatmap-container {{ position: relative; width: 400px; height: 300px; }}
278 .colorbar {{ width: 20px; height: 300px; background: linear-gradient(to top, blue, cyan, yellow, red); }}
279 "#
280 )?;
281 writeln!(&mut html, "</style>")?;
282 writeln!(&mut html, "<script>")?;
283 writeln!(
284 &mut html,
285 r#"
286 function updateVisualization() {{
287 const colormap = document.getElementById('colormap').value;
288 const plotType = document.getElementById('plotType').value;
289 // Update visualization based on controls
290 console.log('Updating visualization:', colormap, plotType);
291 }}
292
293 function exportView() {{
294 const content = document.getElementById('main-content').innerHTML;
295 const blob = new Blob([content], {{ type: 'text/html' }});
296 const url = URL.createObjectURL(blob);
297 const a = document.createElement('a');
298 a.href = url;
299 a.download = 'visualization.html';
300 a.click();
301 URL.revokeObjectURL(url);
302 }}
303 "#
304 )?;
305 writeln!(&mut html, "</script>")?;
306 writeln!(&mut html, "</head><body>")?;
307
308 writeln!(&mut html, "<h1>{}</h1>", title)?;
309
310 writeln!(&mut html, "<div class='controls'>")?;
312 writeln!(&mut html, "<div class='control-group'>")?;
313 writeln!(&mut html, "<label>Color Map:</label>")?;
314 writeln!(
315 &mut html,
316 r#"<select id="colormap" onchange="updateVisualization()">"#
317 )?;
318 writeln!(&mut html, "<option value='viridis'>Viridis</option>")?;
319 writeln!(&mut html, "<option value='plasma'>Plasma</option>")?;
320 writeln!(&mut html, "<option value='jet'>Jet</option>")?;
321 writeln!(&mut html, "<option value='hot'>Hot</option>")?;
322 writeln!(&mut html, "</select>")?;
323 writeln!(&mut html, "</div>")?;
324
325 writeln!(&mut html, "<div class='control-group'>")?;
326 writeln!(&mut html, "<label>Plot Type:</label>")?;
327 writeln!(
328 &mut html,
329 r#"<select id="plotType" onchange="updateVisualization()">"#
330 )?;
331 writeln!(&mut html, "<option value='heatmap'>Heatmap</option>")?;
332 writeln!(&mut html, "<option value='contour'>Contour</option>")?;
333 writeln!(&mut html, "<option value='surface'>3D Surface</option>")?;
334 writeln!(&mut html, "</select>")?;
335 writeln!(&mut html, "</div>")?;
336
337 writeln!(
338 &mut html,
339 r#"<button onclick="exportView()">Export View</button>"#
340 )?;
341 writeln!(&mut html, "</div>")?;
342
343 writeln!(
344 &mut html,
345 "<div id='main-content' class='visualization-container'>"
346 )?;
347
348 let (height, width) = image.dim();
350 let mean = image.mean_or(T::zero());
351 let min_val = image.iter().cloned().fold(T::infinity(), T::min);
352 let max_val = image.iter().cloned().fold(T::neg_infinity(), T::max);
353
354 writeln!(&mut html, "<div class='plot-panel'>")?;
355 writeln!(&mut html, "<div class='plot-title'>Image Statistics</div>")?;
356 writeln!(&mut html, "<div class='stats-grid'>")?;
357 writeln!(&mut html, "<div class='stat-item'>Width: {}</div>", width)?;
358 writeln!(&mut html, "<div class='stat-item'>Height: {}</div>", height)?;
359 writeln!(
360 &mut html,
361 "<div class='stat-item'>Mean: {:.4}</div>",
362 mean.to_f64().unwrap_or(0.0)
363 )?;
364 writeln!(
365 &mut html,
366 "<div class='stat-item'>Min: {:.4}</div>",
367 min_val.to_f64().unwrap_or(0.0)
368 )?;
369 writeln!(
370 &mut html,
371 "<div class='stat-item'>Max: {:.4}</div>",
372 max_val.to_f64().unwrap_or(0.0)
373 )?;
374 writeln!(
375 &mut html,
376 "<div class='stat-item'>Pixels: {}</div>",
377 width * height
378 )?;
379 writeln!(&mut html, "</div>")?;
380 writeln!(&mut html, "</div>")?;
381
382 writeln!(&mut html, "<div class='plot-panel'>")?;
384 writeln!(&mut html, "<div class='plot-title'>Heatmap View</div>")?;
385 writeln!(&mut html, "<div class='heatmap-container'>")?;
386
387 let config = PlotConfig {
389 format: ReportFormat::Html,
390 colormap: ColorMap::Viridis,
391 title: "Interactive Heatmap".to_string(),
392 ..PlotConfig::default()
393 };
394
395 let heatmap = plot_heatmap(image, &config)?;
396 writeln!(&mut html, "{}", heatmap)?;
397
398 writeln!(&mut html, "</div>")?;
399 writeln!(&mut html, "</div>")?;
400
401 writeln!(&mut html, "</div>")?;
402 writeln!(&mut html, "</body></html>")?;
403
404 Ok(html)
405 }
406
407 pub fn create_comparison_view<T>(
409 images: &[(&str, ArrayView2<T>)],
410 title: &str,
411 ) -> NdimageResult<String>
412 where
413 T: Float + FromPrimitive + ToPrimitive + Debug + Clone,
414 {
415 if images.is_empty() {
416 return Err(NdimageError::InvalidInput(
417 "No images provided for comparison".into(),
418 ));
419 }
420
421 let mut html = String::new();
422
423 writeln!(&mut html, "<!DOCTYPE html>")?;
424 writeln!(&mut html, "<html><head>")?;
425 writeln!(&mut html, "<title>{}</title>", title)?;
426 writeln!(&mut html, "<style>")?;
427 writeln!(
428 &mut html,
429 r#"
430 body {{ font-family: Arial, sans-serif; margin: 20px; }}
431 .comparison-grid {{ display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; }}
432 .image-panel {{ border: 1px solid #ccc; padding: 15px; border-radius: 5px; }}
433 .image-title {{ font-weight: bold; margin-bottom: 10px; color: #333; text-align: center; }}
434 .image-stats {{ background: #f9f9f9; padding: 10px; margin-top: 10px; border-radius: 3px; }}
435 .stat-row {{ display: flex; justify-content: space-between; margin-bottom: 5px; }}
436 .difference-highlight {{ background: #ffe6e6; }}
437 "#
438 )?;
439 writeln!(&mut html, "</style>")?;
440 writeln!(&mut html, "</head><body>")?;
441
442 writeln!(&mut html, "<h1>{}</h1>", title)?;
443 writeln!(&mut html, "<div class='comparison-grid'>")?;
444
445 for (name, image) in images {
446 writeln!(&mut html, "<div class='image-panel'>")?;
447 writeln!(&mut html, "<div class='image-title'>{}</div>", name)?;
448
449 let config = PlotConfig {
451 format: ReportFormat::Html,
452 colormap: ColorMap::Viridis,
453 title: name.to_string(),
454 width: 250,
455 height: 200,
456 ..PlotConfig::default()
457 };
458
459 let heatmap = plot_heatmap(image, &config)?;
460 writeln!(&mut html, "{}", heatmap)?;
461
462 let (height, width) = image.dim();
464 let mean = image.mean_or(T::zero());
465 let min_val = image.iter().cloned().fold(T::infinity(), T::min);
466 let max_val = image.iter().cloned().fold(T::neg_infinity(), T::max);
467
468 writeln!(&mut html, "<div class='image-stats'>")?;
469 writeln!(
470 &mut html,
471 "<div class='stat-row'><span>Dimensions:</span><span>{}×{}</span></div>",
472 height, width
473 )?;
474 writeln!(
475 &mut html,
476 "<div class='stat-row'><span>Mean:</span><span>{:.4}</span></div>",
477 mean.to_f64().unwrap_or(0.0)
478 )?;
479 writeln!(
480 &mut html,
481 "<div class='stat-row'><span>Range:</span><span>[{:.3}, {:.3}]</span></div>",
482 min_val.to_f64().unwrap_or(0.0),
483 max_val.to_f64().unwrap_or(0.0)
484 )?;
485 writeln!(&mut html, "</div>")?;
486
487 writeln!(&mut html, "</div>")?;
488 }
489
490 writeln!(&mut html, "</div>")?;
491 writeln!(&mut html, "</body></html>")?;
492
493 Ok(html)
494 }
495}
496
497#[cfg(test)]
498mod tests {
499 use super::*;
500 use scirs2_core::ndarray::{Array1, Array2};
501
502 #[test]
503 fn test_module_exports() {
504 let _config = PlotConfig::new();
506 let _colormap = ColorMap::Viridis;
507 let _format = ReportFormat::Html;
508 let _color = RgbColor::new(255, 0, 0);
509 }
510
511 #[test]
512 fn test_backward_compatibility_aliases() {
513 let img1 = Array2::<f64>::zeros((5, 5));
514 let img2 = Array2::<f64>::ones((5, 5));
515 let images = vec![img1.view(), img2.view()];
516 let config = PlotConfig::new().with_format(ReportFormat::Text);
517
518 let result = createimage_montage(&images, 2, &config);
520 assert!(result.is_ok());
521 }
522
523 #[test]
524 fn test_statistical_functions() {
525 let data1 = Array1::from_vec(vec![1.0, 2.0, 3.0, 4.0, 5.0]);
526 let data2 = Array1::from_vec(vec![2.0, 4.0, 6.0, 8.0, 10.0]);
527
528 let datasets = vec![("Test A", data1.view()), ("Test B", data2.view())];
529
530 let config = PlotConfig::new()
531 .with_format(ReportFormat::Text)
532 .with_title("Statistical Test");
533
534 let result = plot_statistical_comparison(&datasets, &config);
535 assert!(result.is_ok());
536 }
537
538 #[test]
539 fn test_plotting_functions() {
540 let data = Array1::from_vec(vec![1.0, 2.0, 3.0, 4.0, 5.0]);
541 let config = PlotConfig::new()
542 .with_format(ReportFormat::Text)
543 .with_title("Histogram Test");
544
545 let result = plot_histogram(&data.view(), &config);
546 assert!(result.is_ok());
547 }
548
549 #[test]
550 fn test_colormap_functions() {
551 let colors = create_colormap(ColorMap::Viridis, 10);
552 assert_eq!(colors.len(), 10);
553
554 let color = viridis_colormap(0.5);
555 assert!(color.r > 0 || color.g > 0 || color.b > 0);
556 }
557
558 #[test]
559 fn test_report_generation() {
560 let image = Array2::<f64>::ones((10, 10));
561 let config = ReportConfig::new()
562 .with_format(ReportFormat::Text)
563 .minimal();
564
565 let result = generate_report(&image.view(), None, None, &config);
566 assert!(result.is_ok());
567 }
568}