- metadata

This commit is contained in:
w.pomp
2026-07-13 13:41:02 +02:00
parent ff7cd562af
commit 0ce1187a43
+120 -102
View File
@@ -8,9 +8,7 @@ use ome_metadata::ome::{
impl Metadata for Ome {
fn get_instrument(&self) -> Option<&Instrument> {
let instrument_id = self.get_image()?.instrument_ref.as_ref()?.id.clone();
self.instrument
.iter()
.find(|i| i.id == instrument_id)
self.instrument.iter().find(|i| i.id == instrument_id)
}
fn get_image(&self) -> Option<&Image> {
@@ -42,23 +40,11 @@ pub trait Metadata {
.find(|o| o.id == objective_id)
}
fn get_tube_lens(&self) -> Option<Objective> {
Some(Objective {
manufacturer: None,
model: Some("Unknown".to_string()),
serial_number: None,
lot_number: None,
id: "TubeLens:1".to_string(),
correction: None,
immersion: None,
lens_na: None,
nominal_magnification: Some(1.0),
calibrated_magnification: None,
working_distance: None,
working_distance_unit: UnitsLength::um,
iris: None,
annotation_ref: vec![],
})
fn get_tube_lens(&self) -> Option<&Objective> {
self.get_instrument()?
.objective
.iter()
.find(|o| o.id.starts_with("Objective:Tubelens"))
}
/// shape of the data along cztyx axes
@@ -110,40 +96,46 @@ pub trait Metadata {
/// distance between planes in z-stack in nm
fn delta_z(&self) -> Result<Option<f64>, Error> {
if let Some(pixels) = self.get_pixels() {
if let Some(z) = pixels.physical_size_z {
return Ok(Some(
Ok(
if let Some(pixels) = self.get_pixels()
&& let Some(z) = pixels.physical_size_z
{
Some(
pixels
.physical_size_z_unit
.convert(&UnitsLength::nm, z as f64)?,
));
}
}
Ok(None)
)
} else {
None
},
)
}
/// time interval in seconds for time-lapse images
fn time_interval(&self) -> Result<Option<f64>, Error> {
if let Some(pixels) = self.get_pixels() {
if let Some(t) = pixels.plane.iter().map(|p| p.the_t).max() {
if t > 0 {
let plane_a = pixels.plane
.iter()
.find(|p| (p.the_c == 0) && (p.the_z == 0) && (p.the_t == 0));
let plane_b = pixels.plane
.iter()
.find(|p| (p.the_c == 0) && (p.the_z == 0) && (p.the_t == t));
if let (Some(a), Some(b)) = (plane_a, plane_b) {
if let (Some(a_t), Some(b_t)) = (a.delta_t, b.delta_t) {
return Ok(Some(
(b.delta_t_unit.convert(&UnitsTime::s, b_t as f64)?
- a.delta_t_unit.convert(&UnitsTime::s, a_t as f64)?)
.abs()
/ (t as f64),
));
}
}
}
if let Some(pixels) = self.get_pixels()
&& let Some(t) = pixels.plane.iter().filter_map(|p| p.the_t).max()
&& (t > 0)
{
let plane_a = pixels.plane.iter().find(|p| {
(p.the_c.is_none() || (p.the_c == Some(0)))
&& (p.the_z.is_none() || (p.the_z == Some(0)))
&& (p.the_t == Some(0))
});
let plane_b = pixels.plane.iter().find(|p| {
(p.the_c.is_none() || (p.the_c == Some(0)))
&& (p.the_z.is_none() || (p.the_z == Some(0)))
&& (p.the_t == Some(t))
});
if let (Some(a), Some(b)) = (plane_a, plane_b)
&& let (Some(a_t), Some(b_t)) = (a.delta_t, b.delta_t)
{
return Ok(Some(
(b.delta_t_unit.convert(&UnitsTime::s, b_t as f64)?
- a.delta_t_unit.convert(&UnitsTime::s, a_t as f64)?)
.abs()
/ (t as f64),
));
}
}
Ok(None)
@@ -152,17 +144,20 @@ pub trait Metadata {
/// exposure time for channel, z=0 and t=0
fn exposure_time(&self, channel: usize) -> Result<Option<f64>, Error> {
let c = channel as i32;
if let Some(pixels) = self.get_pixels() {
if let Some(p) = pixels.plane
.iter()
.find(|p| (p.the_c == c) && (p.the_z == 0) && (p.the_t == 0))
Ok(
if let Some(pixels) = self.get_pixels()
&& let Some(p) = pixels.plane.iter().find(|p| {
(p.the_c == Some(c))
&& (p.the_z.is_none() || (p.the_z == Some(0)))
&& (p.the_t.is_none() || (p.the_t == Some(0)))
})
&& let Some(t) = p.exposure_time
{
if let Some(t) = p.exposure_time {
return Ok(Some(p.exposure_time_unit.convert(&UnitsTime::s, t as f64)?));
}
}
}
Ok(None)
Some(p.exposure_time_unit.convert(&UnitsTime::s, t as f64)?)
} else {
None
},
)
}
fn binning(&self, channel: usize) -> Option<usize> {
@@ -184,35 +179,36 @@ pub trait Metadata {
}
fn laser_wavelengths(&self, channel: usize) -> Result<Option<f64>, Error> {
if let Some(pixels) = self.get_pixels() {
if let Some(channel) = pixels.channel.get(channel) {
if let Some(w) = channel.excitation_wavelength {
return Ok(Some(
channel
.excitation_wavelength_unit
.convert(&UnitsLength::nm, w as f64)?,
));
}
}
}
Ok(None)
Ok(
if let Some(pixels) = self.get_pixels()
&& let Some(channel) = pixels.channel.get(channel)
&& let Some(w) = channel.excitation_wavelength
{
Some(
channel
.excitation_wavelength_unit
.convert(&UnitsLength::nm, w as f64)?,
)
} else {
None
},
)
}
fn laser_powers(&self, channel: usize) -> Result<Option<f64>, Error> {
if let Some(pixels) = self.get_pixels() {
if let Some(channel) = pixels.channel.get(channel) {
if let Some(ls) = &channel.light_source_settings {
if let Some(a) = ls.attenuation {
return if (0. ..=1.).contains(&a) {
Ok(Some(1f64 - (a as f64)))
} else {
Err(Error::InvalidAttenuation(a.to_string()))
};
}
}
if let Some(pixels) = self.get_pixels()
&& let Some(channel) = pixels.channel.get(channel)
&& let Some(ls) = &channel.light_source_settings
&& let Some(a) = ls.attenuation
{
if (0. ..=1.).contains(&a) {
Ok(Some(1f64 - (a as f64)))
} else {
Err(Error::InvalidAttenuation(a.to_string()))
}
} else {
Ok(None)
}
Ok(None)
}
fn objective_name(&self) -> Option<String> {
@@ -249,19 +245,28 @@ pub trait Metadata {
}
fn gain(&self, channel: usize) -> Option<f64> {
if let Some(pixels) = self.get_pixels() {
Some(
*pixels
.channel
.get(channel)?
.detector_settings
.as_ref()?
.gain
.as_ref()? as f64,
)
} else {
None
}
self.get_pixels()
.and_then(|p| p.channel.get(channel))
.and_then(|c| c.detector_settings.as_ref())
.map(|ds| ds.id.as_str())
.and_then(|detector_id| {
self.get_instrument()
.and_then(|i| i.detector.iter().find(|d| d.id == detector_id))
.and_then(|d| d.amplification_gain)
.map(|g| g as f64)
})
}
fn is_zstack(&self) -> Result<bool, Error> {
self.get_pixels()
.map(|p| Ok(p.size_z > 1))
.unwrap_or_else(|| Err(Error::NoImageOrPixels))
}
fn is_time_lapse(&self) -> Result<bool, Error> {
self.get_pixels()
.map(|p| Ok(p.size_t > 1))
.unwrap_or_else(|| Err(Error::NoImageOrPixels))
}
fn summary(&self) -> Result<String, Error> {
@@ -274,10 +279,14 @@ pub trait Metadata {
if let Ok(Some(pixel_size)) = self.pixel_size() {
s.push_str(&format!("pixel size: {pixel_size:.2} nm\n"));
}
if let Ok(Some(delta_z)) = self.delta_z() {
if let Ok(Some(delta_z)) = self.delta_z()
&& self.is_zstack()?
{
s.push_str(&format!("z-interval: {delta_z:.2} nm\n"))
}
if let Ok(Some(time_interval)) = self.time_interval() {
if let Ok(Some(time_interval)) = self.time_interval()
&& self.is_time_lapse()?
{
s.push_str(&format!("time interval: {time_interval:.2} s\n"))
}
let exposure_time = (0..size_c)
@@ -288,12 +297,15 @@ pub trait Metadata {
.collect::<Vec<_>>();
if !exposure_time.is_empty() {
s.push_str(&format!(
"exposure time: {}\n",
exposure_time.into_iter().join(" | ")
"exposure time: {} s\n",
exposure_time
.iter()
.map(|e| format!("{:.2}", e))
.join(" | ")
));
}
if let Some(magnification) = self.magnification() {
s.push_str(&format!("magnification: {magnification}x\n"))
s.push_str(&format!("magnification: {magnification:.1}x\n"))
}
if let Some(objective_name) = self.objective_name() {
s.push_str(&format!("objective: {objective_name}\n"))
@@ -320,7 +332,10 @@ pub trait Metadata {
.flatten()
.collect::<Vec<_>>();
if !gain.is_empty() {
s.push_str(&format!("gain: {}\n", gain.into_iter().join(" ")));
s.push_str(&format!(
"gain: {}\n",
gain.into_iter().join(" | ")
));
}
let laser_wavelengths = (0..size_c)
.map(|c| self.laser_wavelengths(c))
@@ -342,8 +357,11 @@ pub trait Metadata {
.collect::<Vec<_>>();
if !laser_powers.is_empty() {
s.push_str(&format!(
"laser powers: {}\n",
laser_powers.into_iter().join(" | ")
"laser powers: {} %\n",
laser_powers
.into_iter()
.map(|p| format!("{:.3}", 100.0 * p))
.join(" | ")
));
}
let binning = (0..size_c)