use std::sync::RwLock;
use ndarray as nd;
use ndarray_rand::{
rand_distr::{Poisson, Uniform},
RandomExt,
};
#[cfg(feature = "plots")]
use plotters::style::RGBColor;
use rand::Rng;
use rustronomy_fits as rsf;
use rustronomy_watershed::prelude::*;
const RF_SIZE: (usize, usize) = (1000, 1000);
static CGPS_DATA: RwLock<Option<nd::Array3<f64>>> = RwLock::new(None);
static SMOOTH_CGPS_DATA: RwLock<Option<nd::Array3<f64>>> = RwLock::new(None);
fn get_root_path() -> std::path::PathBuf {
const DATA_ENV: &str = "WSRS_DATA_PATH";
let root_path =
std::env::var(DATA_ENV).expect(&format!("enviroment variable ${DATA_ENV} not set"));
std::path::Path::new(&root_path)
.canonicalize()
.expect(&format!("could not canonicalize path found in ${DATA_ENV} env. variable"))
}
static CMAP: RwLock<Vec<(u8, u8, u8)>> = RwLock::new(Vec::new());
#[inline]
#[cfg(feature = "plots")]
fn cmap(count: usize, _min: usize, _max: usize) -> Result<RGBColor, Box<dyn std::error::Error>> {
let lock = CMAP.read()?;
if let Some(c) = lock.get(count) {
Ok(RGBColor(c.0, c.1, c.2))
} else {
drop(lock);
let mut lock = CMAP.write()?;
if lock.is_empty() {
lock.push((0, 0, 0))
};
let mut rng = rand::thread_rng();
let c = (rng.gen_range(25..u8::MAX), rng.gen_range(25..u8::MAX), rng.gen_range(25..u8::MAX));
lock.push(c.clone());
Ok(RGBColor(c.0, c.1, c.2))
}
}
fn open_cgps() {
let mut lock = CGPS_DATA.write().unwrap();
if lock.is_some() {
return;
}
let path = &get_root_path().join("full_cube.fits");
let mut fits_file = rsf::Fits::open(std::path::Path::new(path)).unwrap();
let (header, data) = fits_file.remove_hdu(0).unwrap().to_parts();
print!("{header}");
let array = match data.unwrap() {
rsf::Extension::Image(img) => img.as_owned_f64_array().unwrap(),
_ => panic!(),
};
let array = array.into_dimensionality().unwrap();
lock.replace(array);
}
fn open_cgps_smooth() {
let mut lock = SMOOTH_CGPS_DATA.write().unwrap();
if lock.is_some() {
return;
}
let path = &get_root_path().join("full_cube_smoothed.fits");
let mut fits_file = rsf::Fits::open(std::path::Path::new(path)).unwrap();
let (header, data) = fits_file.remove_hdu(0).unwrap().to_parts();
print!("{header}");
let array = match data.unwrap() {
rsf::Extension::Image(img) => img.as_owned_f64_array().unwrap(),
_ => panic!(),
};
let array = array.into_dimensionality().unwrap();
lock.replace(array);
}
#[cfg(feature = "plots")]
#[test]
fn test_merging_uniform() {
let rf = nd::Array2::<u8>::random(RF_SIZE, Uniform::new(0, 254));
let root = get_root_path().join("figs/uniform_merging_test/");
if !root.exists() {
std::fs::create_dir(&root).unwrap();
}
let watershed = TransformBuilder::default()
.set_plot_folder(&root)
.set_plot_colour_map(cmap)
.enable_edge_correction()
.build_merging()
.unwrap();
let mins = &watershed.find_local_minima(rf.view());
rustronomy_watershed::plotting::plot_slice(
rf.view(),
&root.join("original.png"),
color_maps::viridis,
)
.unwrap();
watershed.transform_to_list(rf.view(), mins);
}
#[cfg(feature = "plots")]
#[test]
fn test_segmenting_uniform() {
let rf = nd::Array2::<u8>::random(RF_SIZE, Uniform::new(0, 254));
let root = get_root_path().join("figs/uniform_segmenting_test/");
if !root.exists() {
std::fs::create_dir(&root).unwrap();
}
let watershed = TransformBuilder::default()
.set_plot_folder(&root)
.set_plot_colour_map(cmap)
.build_segmenting()
.unwrap();
let mins = &watershed.find_local_minima(rf.view());
rustronomy_watershed::plotting::plot_slice(
rf.view(),
&root.join("original.png"),
color_maps::viridis,
)
.unwrap();
watershed.transform_to_list(rf.view(), mins);
}
#[cfg(feature = "plots")]
#[test]
fn test_merging_poisson() {
let rf = nd::Array2::<f64>::random(RF_SIZE, Poisson::new(0.85f64).unwrap());
let root = get_root_path().join("figs/poisson_merging_test/");
if !root.exists() {
std::fs::create_dir(&root).unwrap();
}
let watershed = TransformBuilder::default()
.set_plot_folder(&root)
.set_plot_colour_map(cmap)
.enable_edge_correction()
.build_merging()
.unwrap();
let rf = watershed.pre_processor(rf.view());
let mins = &watershed.find_local_minima(rf.view());
rustronomy_watershed::plotting::plot_slice(
rf.view(),
&root.join("original.png"),
color_maps::viridis,
)
.unwrap();
watershed.transform_to_list(rf.view(), mins);
}
#[cfg(feature = "plots")]
#[test]
fn test_segmenting_poisson() {
let rf = nd::Array2::<f64>::random(RF_SIZE, Poisson::new(0.85f64).unwrap());
let root = get_root_path().join("figs/poisson_segmenting_test/");
if !root.exists() {
std::fs::create_dir(&root).unwrap();
}
let watershed = TransformBuilder::default()
.set_plot_folder(&root)
.set_plot_colour_map(cmap)
.build_segmenting()
.unwrap();
let rf = watershed.pre_processor(rf.view());
let mins = &watershed.find_local_minima(rf.view());
rustronomy_watershed::plotting::plot_slice(
rf.view(),
&root.join("original.png"),
color_maps::viridis,
)
.unwrap();
watershed.transform_to_list(rf.view(), mins);
}
#[cfg(feature = "plots")]
#[test]
fn test_merging_real() {
println!("Loading reduced data cube");
let root = get_root_path();
let mut lock = CGPS_DATA.read().unwrap();
let data_cube = if lock.is_some() {
lock.as_ref().unwrap()
} else {
drop(lock);
open_cgps();
lock = CGPS_DATA.read().unwrap();
lock.as_ref().unwrap()
};
let img = data_cube.slice(nd::s![.., .., 120]);
let root = root.join("figs/real_merging_test/");
if !root.exists() {
std::fs::create_dir(&root).unwrap();
}
let watershed = TransformBuilder::default()
.set_plot_folder(&root)
.set_plot_colour_map(cmap)
.enable_edge_correction()
.build_merging()
.unwrap();
let img = watershed.pre_processor(img.view());
let mins = &watershed.find_local_minima(img.view());
rustronomy_watershed::plotting::plot_slice(
img.view(),
&root.join("original.png"),
color_maps::viridis,
)
.unwrap();
watershed.transform_to_list(img.view(), mins);
}
#[cfg(feature = "plots")]
#[test]
fn test_segmenting_real() {
println!("Loading reduced data cube");
let root = get_root_path();
let mut lock = CGPS_DATA.read().unwrap();
let data_cube = if lock.is_some() {
lock.as_ref().unwrap()
} else {
drop(lock);
open_cgps();
lock = CGPS_DATA.read().unwrap();
lock.as_ref().unwrap()
};
let img = data_cube.slice(nd::s![.., .., 120]);
let root = root.join("figs/real_segmenting_test/");
if !root.exists() {
std::fs::create_dir(&root).unwrap();
}
let watershed = TransformBuilder::default()
.set_plot_folder(&root)
.set_plot_colour_map(cmap)
.build_segmenting()
.unwrap();
let img = watershed.pre_processor(img.view());
let mins = &watershed.find_local_minima(img.view());
rustronomy_watershed::plotting::plot_slice(
img.view(),
&root.join("original.png"),
color_maps::viridis,
)
.unwrap();
watershed.transform_to_list(img.view(), mins);
}
#[cfg(feature = "plots")]
#[test]
fn test_merging_real_with_nan() {
println!("Loading reduced data cube");
let root = get_root_path();
let mut lock = CGPS_DATA.read().unwrap();
let data_cube = if lock.is_some() {
lock.as_ref().unwrap()
} else {
drop(lock);
open_cgps();
lock = CGPS_DATA.read().unwrap();
lock.as_ref().unwrap()
};
let img = data_cube.slice(nd::s![.., .., 0]);
let root = root.join("figs/NaNreal_merging_test/");
if !root.exists() {
std::fs::create_dir(&root).unwrap();
}
let watershed = TransformBuilder::default()
.set_plot_folder(&root)
.set_plot_colour_map(cmap)
.enable_edge_correction()
.build_merging()
.unwrap();
let img = watershed.pre_processor(img.view());
let mins = &watershed.find_local_minima(img.view());
rustronomy_watershed::plotting::plot_slice(
img.view(),
&root.join("original.png"),
color_maps::viridis,
)
.unwrap();
watershed.transform_to_list(img.view(), mins);
}
#[cfg(feature = "plots")]
#[test]
fn test_segmenting_real_with_nan() {
println!("Loading reduced data cube");
let root = get_root_path();
let mut lock = CGPS_DATA.read().unwrap();
let data_cube = if lock.is_some() {
lock.as_ref().unwrap()
} else {
drop(lock);
open_cgps();
lock = CGPS_DATA.read().unwrap();
lock.as_ref().unwrap()
};
let img = data_cube.slice(nd::s![.., .., 0]);
let root = root.join("figs/NaNreal_segmenting_test/");
if !root.exists() {
std::fs::create_dir(&root).unwrap();
}
let watershed = TransformBuilder::default()
.set_plot_folder(&root)
.set_plot_colour_map(cmap)
.build_segmenting()
.unwrap();
let img = watershed.pre_processor(img.view());
let mins = &watershed.find_local_minima(img.view());
rustronomy_watershed::plotting::plot_slice(
img.view(),
&root.join("original.png"),
color_maps::viridis,
)
.unwrap();
watershed.transform_to_list(img.view(), mins);
}
#[cfg(feature = "plots")]
#[test]
fn test_merging_gaussian() {
let root = get_root_path().join("figs/gauß_merging_test/");
if !root.exists() {
std::fs::create_dir(&root).unwrap();
}
let watershed = TransformBuilder::default()
.set_plot_folder(&root)
.set_plot_colour_map(cmap)
.enable_edge_correction()
.build_merging()
.unwrap();
let img = watershed.pre_processor(
{
println!("Loading gaussian data");
let hdu = rsf::Fits::open(&get_root_path().join("GAUß.fits")).unwrap().remove_hdu(0).unwrap();
match hdu.to_parts().1.unwrap() {
rsf::Extension::Image(img) => img.as_owned_f64_array().unwrap(),
_ => panic!("shitties"),
}
}
.into_dimensionality::<nd::Ix2>()
.unwrap()
.view(),
);
let mins = &watershed.find_local_minima(img.view());
rustronomy_watershed::plotting::plot_slice(
img.view(),
&root.join("original.png"),
color_maps::viridis,
)
.unwrap();
watershed.transform_to_list(img.view(), mins);
}
#[cfg(feature = "plots")]
#[test]
fn test_segmenting_gaussian() {
let root = get_root_path().join("figs/gauß_segmenting_test/");
if !root.exists() {
std::fs::create_dir(&root).unwrap();
}
let watershed = TransformBuilder::default()
.set_plot_folder(&root)
.set_plot_colour_map(cmap)
.build_segmenting()
.unwrap();
let img = watershed.pre_processor(
{
println!("Loading gaussian data");
let hdu = rsf::Fits::open(&get_root_path().join("GAUß.fits")).unwrap().remove_hdu(0).unwrap();
match hdu.to_parts().1.unwrap() {
rsf::Extension::Image(img) => img.as_owned_f64_array().unwrap(),
_ => panic!("shitties"),
}
}
.into_dimensionality::<nd::Ix2>()
.unwrap()
.view(),
);
let mins = &watershed.find_local_minima(img.view());
rustronomy_watershed::plotting::plot_slice(
img.view(),
&root.join("original.png"),
color_maps::viridis,
)
.unwrap();
watershed.transform_to_list(img.view(), mins);
}
#[cfg(feature = "plots")]
#[test]
fn test_merging_real_smoothed() {
println!("Loading reduced data cube");
let root = get_root_path();
let mut lock = SMOOTH_CGPS_DATA.read().unwrap();
let data_cube = if lock.is_some() {
lock.as_ref().unwrap()
} else {
drop(lock);
open_cgps_smooth();
lock = SMOOTH_CGPS_DATA.read().unwrap();
lock.as_ref().unwrap()
};
let img = data_cube.slice(nd::s![.., .., 120]);
let root = root.join("figs/real_smoothed_merging_test/");
if !root.exists() {
std::fs::create_dir(&root).unwrap();
}
let watershed = TransformBuilder::default()
.set_plot_folder(&root)
.set_plot_colour_map(cmap)
.enable_edge_correction()
.build_merging()
.unwrap();
let img = watershed.pre_processor(img.view());
let mins = &watershed.find_local_minima(img.view());
rustronomy_watershed::plotting::plot_slice(
img.view(),
&root.join("original.png"),
color_maps::viridis,
)
.unwrap();
watershed.transform_to_list(img.view(), mins);
}
#[cfg(feature = "plots")]
#[test]
fn test_segmenting_real_smoothed() {
println!("Loading reduced data cube");
let root = get_root_path();
let mut lock = SMOOTH_CGPS_DATA.read().unwrap();
let data_cube = if lock.is_some() {
lock.as_ref().unwrap()
} else {
drop(lock);
open_cgps_smooth();
lock = SMOOTH_CGPS_DATA.read().unwrap();
lock.as_ref().unwrap()
};
let img = data_cube.slice(nd::s![.., .., 120]);
let root = root.join("figs/real_smoothed_segmenting_test/");
if !root.exists() {
std::fs::create_dir(&root).unwrap();
}
let watershed = TransformBuilder::default()
.set_plot_folder(&root)
.set_plot_colour_map(cmap)
.build_segmenting()
.unwrap();
let img = watershed.pre_processor(img.view());
let mins = &watershed.find_local_minima(img.view());
rustronomy_watershed::plotting::plot_slice(
img.view(),
&root.join("original.png"),
color_maps::viridis,
)
.unwrap();
watershed.transform_to_list(img.view(), mins);
}