use scirs2_core::ndarray::ArrayStatCompat;
use scirs2_core::ndarray::ArrayView2;
use scirs2_core::numeric::{Float, FromPrimitive, ToPrimitive, Zero};
use std::fmt::{Debug, Write};
use crate::analysis::{ImageQualityMetrics, TextureMetrics};
use crate::error::{NdimageError, NdimageResult};
use crate::visualization::types::{ReportConfig, ReportFormat};
use statrs::statistics::Statistics;
#[allow(dead_code)]
pub fn generate_report<T>(
image: &ArrayView2<T>,
qualitymetrics: Option<&ImageQualityMetrics<T>>,
texturemetrics: Option<&TextureMetrics<T>>,
config: &ReportConfig,
) -> NdimageResult<String>
where
T: Float + FromPrimitive + ToPrimitive + Debug + Clone,
{
let mut report = String::new();
match config.format {
ReportFormat::Html => {
writeln!(&mut report, "<!DOCTYPE html>")?;
writeln!(&mut report, "<html><head><title>{}</title>", config.title)?;
writeln!(&mut report, "<style>")?;
writeln!(
&mut report,
"body {{ font-family: Arial, sans-serif; margin: 20px; }}"
)?;
writeln!(
&mut report,
"table {{ border-collapse: collapse; width: 100%; }}"
)?;
writeln!(
&mut report,
"th, td {{ border: 1px solid #ddd; padding: 8px; text-align: left; }}"
)?;
writeln!(&mut report, "th {{ background-color: #f2f2f2; }}")?;
writeln!(
&mut report,
".metric-value {{ font-weight: bold; color: #2E86AB; }}"
)?;
writeln!(&mut report, "</style></head><body>")?;
writeln!(&mut report, "<h1>{}</h1>", config.title)?;
writeln!(
&mut report,
"<p><em>Generated by {}</em></p>",
config.author
)?;
}
ReportFormat::Markdown => {
writeln!(&mut report, "# {}", config.title)?;
writeln!(&mut report)?;
writeln!(&mut report, "*Generated by {}*", config.author)?;
writeln!(&mut report)?;
}
ReportFormat::Text => {
writeln!(&mut report, "{}", config.title)?;
writeln!(&mut report, "{}", "=".repeat(config.title.len()))?;
writeln!(&mut report)?;
writeln!(&mut report, "Generated by: {}", config.author)?;
writeln!(&mut report)?;
}
}
let (height, width) = image.dim();
add_image_info(&mut report, width, height, config.format)?;
if config.include_statistics {
add_basic_statistics(&mut report, image, config.format)?;
}
if config.include_qualitymetrics {
if let Some(metrics) = qualitymetrics {
add_quality_metrics(&mut report, metrics, config.format)?;
}
}
if config.includetexture_analysis {
if let Some(metrics) = texturemetrics {
add_texture_metrics(&mut report, metrics, config.format)?;
}
}
if config.format == ReportFormat::Html {
writeln!(&mut report, "</body></html>")?;
}
Ok(report)
}
#[allow(dead_code)]
pub fn add_image_info(
report: &mut String,
width: usize,
height: usize,
format: ReportFormat,
) -> Result<(), std::fmt::Error> {
match format {
ReportFormat::Html => {
writeln!(report, "<h2>Image Information</h2>")?;
writeln!(report, "<table>")?;
writeln!(report, "<tr><th>Property</th><th>Value</th></tr>")?;
writeln!(
report,
"<tr><td>Width</td><td class='metric-value'>{}</td></tr>",
width
)?;
writeln!(
report,
"<tr><td>Height</td><td class='metric-value'>{}</td></tr>",
height
)?;
writeln!(
report,
"<tr><td>Total Pixels</td><td class='metric-value'>{}</td></tr>",
width * height
)?;
writeln!(
report,
"<tr><td>Aspect Ratio</td><td class='metric-value'>{:.3}</td></tr>",
width as f64 / height as f64
)?;
writeln!(report, "</table>")?;
}
ReportFormat::Markdown => {
writeln!(report, "## Image Information")?;
writeln!(report)?;
writeln!(report, "| Property | Value |")?;
writeln!(report, "|----------|-------|")?;
writeln!(report, "| Width | {} |", width)?;
writeln!(report, "| Height | {} |", height)?;
writeln!(report, "| Total Pixels | {} |", width * height)?;
writeln!(
report,
"| Aspect Ratio | {:.3} |",
width as f64 / height as f64
)?;
writeln!(report)?;
}
ReportFormat::Text => {
writeln!(report, "Image Information")?;
writeln!(report, "-----------------")?;
writeln!(report, "Width: {}", width)?;
writeln!(report, "Height: {}", height)?;
writeln!(report, "Total Pixels: {}", width * height)?;
writeln!(report, "Aspect Ratio: {:.3}", width as f64 / height as f64)?;
writeln!(report)?;
}
}
Ok(())
}
#[allow(dead_code)]
pub fn add_basic_statistics<T>(
report: &mut String,
image: &ArrayView2<T>,
format: ReportFormat,
) -> Result<(), std::fmt::Error>
where
T: Float + FromPrimitive + ToPrimitive + Debug + Clone,
{
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);
let variance = image
.mapv(|x| (x - mean) * (x - mean))
.mean()
.unwrap_or(T::zero());
let std_dev = variance.sqrt();
match format {
ReportFormat::Html => {
writeln!(report, "<h2>Basic Statistics</h2>")?;
writeln!(report, "<table>")?;
writeln!(report, "<tr><th>Statistic</th><th>Value</th></tr>")?;
writeln!(
report,
"<tr><td>Mean</td><td class='metric-value'>{:.6}</td></tr>",
mean.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"<tr><td>Standard Deviation</td><td class='metric-value'>{:.6}</td></tr>",
std_dev.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"<tr><td>Variance</td><td class='metric-value'>{:.6}</td></tr>",
variance.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"<tr><td>Minimum</td><td class='metric-value'>{:.6}</td></tr>",
min_val.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"<tr><td>Maximum</td><td class='metric-value'>{:.6}</td></tr>",
max_val.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"<tr><td>Range</td><td class='metric-value'>{:.6}</td></tr>",
(max_val - min_val).to_f64().unwrap_or(0.0)
)?;
writeln!(report, "</table>")?;
}
ReportFormat::Markdown => {
writeln!(report, "## Basic Statistics")?;
writeln!(report)?;
writeln!(report, "| Statistic | Value |")?;
writeln!(report, "|-----------|-------|")?;
writeln!(report, "| Mean | {:.6} |", mean.to_f64().unwrap_or(0.0))?;
writeln!(
report,
"| Standard Deviation | {:.6} |",
std_dev.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"| Variance | {:.6} |",
variance.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"| Minimum | {:.6} |",
min_val.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"| Maximum | {:.6} |",
max_val.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"| Range | {:.6} |",
(max_val - min_val).to_f64().unwrap_or(0.0)
)?;
writeln!(report)?;
}
ReportFormat::Text => {
writeln!(report, "Basic Statistics")?;
writeln!(report, "----------------")?;
writeln!(
report,
"Mean: {:.6}",
mean.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"Standard Deviation:{:.6}",
std_dev.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"Variance: {:.6}",
variance.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"Minimum: {:.6}",
min_val.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"Maximum: {:.6}",
max_val.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"Range: {:.6}",
(max_val - min_val).to_f64().unwrap_or(0.0)
)?;
writeln!(report)?;
}
}
Ok(())
}
#[allow(dead_code)]
pub fn add_quality_metrics<T>(
report: &mut String,
metrics: &ImageQualityMetrics<T>,
format: ReportFormat,
) -> Result<(), std::fmt::Error>
where
T: Float + ToPrimitive,
{
match format {
ReportFormat::Html => {
writeln!(report, "<h2>Quality Metrics</h2>")?;
writeln!(report, "<table>")?;
writeln!(report, "<tr><th>Metric</th><th>Value</th></tr>")?;
writeln!(
report,
"<tr><td>PSNR</td><td class='metric-value'>{:.3} dB</td></tr>",
metrics.psnr.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"<tr><td>SSIM</td><td class='metric-value'>{:.6}</td></tr>",
metrics.ssim.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"<tr><td>MSE</td><td class='metric-value'>{:.6}</td></tr>",
metrics.mse.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"<tr><td>SNR</td><td class='metric-value'>{:.3} dB</td></tr>",
metrics.snr.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"<tr><td>Entropy</td><td class='metric-value'>{:.3} bits</td></tr>",
metrics.entropy.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"<tr><td>Sharpness</td><td class='metric-value'>{:.6}</td></tr>",
metrics.sharpness.to_f64().unwrap_or(0.0)
)?;
writeln!(report, "</table>")?;
}
ReportFormat::Markdown => {
writeln!(report, "## Quality Metrics")?;
writeln!(report)?;
writeln!(report, "| Metric | Value |")?;
writeln!(report, "|--------|-------|")?;
writeln!(
report,
"| PSNR | {:.3} dB |",
metrics.psnr.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"| SSIM | {:.6} |",
metrics.ssim.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"| MSE | {:.6} |",
metrics.mse.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"| SNR | {:.3} dB |",
metrics.snr.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"| Entropy | {:.3} bits |",
metrics.entropy.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"| Sharpness | {:.6} |",
metrics.sharpness.to_f64().unwrap_or(0.0)
)?;
writeln!(report)?;
}
ReportFormat::Text => {
writeln!(report, "Quality Metrics")?;
writeln!(report, "---------------")?;
writeln!(
report,
"PSNR: {:.3} dB",
metrics.psnr.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"SSIM: {:.6}",
metrics.ssim.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"MSE: {:.6}",
metrics.mse.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"SNR: {:.3} dB",
metrics.snr.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"Entropy: {:.3} bits",
metrics.entropy.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"Sharpness: {:.6}",
metrics.sharpness.to_f64().unwrap_or(0.0)
)?;
writeln!(report)?;
}
}
Ok(())
}
#[allow(dead_code)]
pub fn add_texture_metrics<T>(
report: &mut String,
metrics: &TextureMetrics<T>,
format: ReportFormat,
) -> Result<(), std::fmt::Error>
where
T: Float + ToPrimitive,
{
match format {
ReportFormat::Html => {
writeln!(report, "<h2>Texture Analysis</h2>")?;
writeln!(report, "<table>")?;
writeln!(report, "<tr><th>Metric</th><th>Value</th></tr>")?;
writeln!(
report,
"<tr><td>GLCM Contrast</td><td class='metric-value'>{:.6}</td></tr>",
metrics.glcm_contrast.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"<tr><td>GLCM Homogeneity</td><td class='metric-value'>{:.6}</td></tr>",
metrics.glcm_homogeneity.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"<tr><td>GLCM Energy</td><td class='metric-value'>{:.6}</td></tr>",
metrics.glcm_energy.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"<tr><td>LBP Uniformity</td><td class='metric-value'>{:.6}</td></tr>",
metrics.lbp_uniformity.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"<tr><td>Gabor Mean</td><td class='metric-value'>{:.6}</td></tr>",
metrics.gabor_mean.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"<tr><td>Gabor Std</td><td class='metric-value'>{:.6}</td></tr>",
metrics.gabor_std.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"<tr><td>Fractal Dimension</td><td class='metric-value'>{:.3}</td></tr>",
metrics.fractal_dimension.to_f64().unwrap_or(0.0)
)?;
writeln!(report, "</table>")?;
}
ReportFormat::Markdown => {
writeln!(report, "## Texture Analysis")?;
writeln!(report)?;
writeln!(report, "| Metric | Value |")?;
writeln!(report, "|--------|-------|")?;
writeln!(
report,
"| GLCM Contrast | {:.6} |",
metrics.glcm_contrast.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"| GLCM Homogeneity | {:.6} |",
metrics.glcm_homogeneity.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"| GLCM Energy | {:.6} |",
metrics.glcm_energy.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"| LBP Uniformity | {:.6} |",
metrics.lbp_uniformity.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"| Gabor Mean | {:.6} |",
metrics.gabor_mean.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"| Gabor Std | {:.6} |",
metrics.gabor_std.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"| Fractal Dimension | {:.3} |",
metrics.fractal_dimension.to_f64().unwrap_or(0.0)
)?;
writeln!(report)?;
}
ReportFormat::Text => {
writeln!(report, "Texture Analysis")?;
writeln!(report, "----------------")?;
writeln!(
report,
"GLCM Contrast: {:.6}",
metrics.glcm_contrast.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"GLCM Homogeneity: {:.6}",
metrics.glcm_homogeneity.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"GLCM Energy: {:.6}",
metrics.glcm_energy.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"LBP Uniformity: {:.6}",
metrics.lbp_uniformity.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"Gabor Mean: {:.6}",
metrics.gabor_mean.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"Gabor Std: {:.6}",
metrics.gabor_std.to_f64().unwrap_or(0.0)
)?;
writeln!(
report,
"Fractal Dimension: {:.3}",
metrics.fractal_dimension.to_f64().unwrap_or(0.0)
)?;
writeln!(report)?;
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::visualization::types::{ReportConfig, ReportFormat};
use scirs2_core::ndarray::Array2;
#[test]
fn test_generate_basic_report() {
let image = Array2::<f64>::ones((10, 10));
let config = ReportConfig::new()
.with_format(ReportFormat::Text)
.with_header("Test Report", "Test Suite")
.minimal();
let result = generate_report(&image.view(), None, None, &config);
assert!(result.is_ok());
let report = result.expect("Operation failed");
assert!(report.contains("Test Report"));
assert!(report.contains("Test Suite"));
assert!(report.contains("Image Information"));
assert!(report.contains("Width: 10"));
assert!(report.contains("Height: 10"));
}
#[test]
fn test_generate_html_report() {
let image = Array2::<f64>::zeros((5, 5));
let config = ReportConfig::new()
.with_format(ReportFormat::Html)
.with_header("HTML Test", "HTML Suite")
.with_sections(true, false, false, false, false);
let result = generate_report(&image.view(), None, None, &config);
assert!(result.is_ok());
let report = result.expect("Operation failed");
assert!(report.contains("<!DOCTYPE html>"));
assert!(report.contains("<h1>HTML Test</h1>"));
assert!(report.contains("Basic Statistics"));
assert!(report.contains("</body></html>"));
}
#[test]
fn test_generate_markdown_report() {
let image = Array2::from_elem((3, 3), 2.5f64);
let config = ReportConfig::new()
.with_format(ReportFormat::Markdown)
.with_header("Markdown Test", "MD Suite")
.with_sections(true, false, false, false, false);
let result = generate_report(&image.view(), None, None, &config);
assert!(result.is_ok());
let report = result.expect("Operation failed");
assert!(report.contains("# Markdown Test"));
assert!(report.contains("*Generated by MD Suite*"));
assert!(report.contains("## Image Information"));
assert!(report.contains("## Basic Statistics"));
assert!(report.contains("| Mean | 2.500000 |"));
}
#[test]
fn test_add_image_info_all_formats() {
let mut html_report = String::new();
let mut md_report = String::new();
let mut text_report = String::new();
assert!(add_image_info(&mut html_report, 100, 200, ReportFormat::Html).is_ok());
assert!(html_report.contains("<h2>Image Information</h2>"));
assert!(html_report.contains("100"));
assert!(html_report.contains("200"));
assert!(html_report.contains("20000"));
assert!(add_image_info(&mut md_report, 100, 200, ReportFormat::Markdown).is_ok());
assert!(md_report.contains("## Image Information"));
assert!(md_report.contains("| Width | 100 |"));
assert!(add_image_info(&mut text_report, 100, 200, ReportFormat::Text).is_ok());
assert!(text_report.contains("Width: 100"));
assert!(text_report.contains("Height: 200"));
assert!(text_report.contains("Aspect Ratio: 0.500"));
}
#[test]
fn test_add_basic_statistics() {
let image =
Array2::from_shape_vec((2, 2), vec![1.0, 2.0, 3.0, 4.0]).expect("Operation failed");
let mut report = String::new();
let result = add_basic_statistics(&mut report, &image.view(), ReportFormat::Text);
assert!(result.is_ok());
assert!(report.contains("Basic Statistics"));
assert!(report.contains("Mean: 2.500000")); assert!(report.contains("Minimum: 1.000000"));
assert!(report.contains("Maximum: 4.000000"));
assert!(report.contains("Range: 3.000000"));
}
}