- read ome xml tiff

This commit is contained in:
Wim Pomp committed 2026-10-03 23:08:23 +02:00
1 parent 7f07f82fdb
commit 4815690621
3 files changed
+186 -163

No files matched your search

+1 -1
View File
@@ -33,7 +33,7 @@ libczirw-sys = { version = "0.5", optional = true }
ndarray = { version = "0.17", features = ["serde"] } ndarray = { version = "0.17", features = ["serde"] }
num = "0.4" num = "0.4"
numpy = { version = "0.29", optional = true } numpy = { version = "0.29", optional = true }
ome-metadata = "0.5" ome-metadata = "0.6"
ordered-float = { version = "5", optional = true } ordered-float = { version = "5", optional = true }
phf = { version = "0.14", features = ["macros"] } phf = { version = "0.14", features = ["macros"] }
postcard = { version = "1", features = ["use-std"], optional = true } postcard = { version = "1", features = ["use-std"], optional = true }
+184 -161
View File
@@ -156,47 +156,61 @@ impl Reader for TiffReader {
.unwrap_or(1) .unwrap_or(1)
== 2; == 2;
let imagej_bytes = reader let image_description = reader.current_ifd().get_tag(Tag::ImageDescription);
.current_ifd()
.get_tag(Tag::Unknown(50839))
.or_else(|_| reader.current_ifd().get_tag(Tag::ImageDescription))?;
let imagej_bytes = match imagej_bytes {
Value::Ascii(s) => s.into_bytes(),
other => other.into_u8_vec()?,
};
let metadata_str = String::from_utf8_lossy(&imagej_bytes);
let metadata_map = parse_imagej_metadata(&metadata_str);
let p_ndim = if n_samples > 1 { 3u8 } else { 2u8 }; let p_ndim = if n_samples > 1 { 3u8 } else { 2u8 };
let size_c = if p_ndim == 3 { let (size_c, size_t, size_z) = if let Ok(Value::Ascii(xml)) = image_description
n_samples && xml.starts_with("<?xml")
{
let ome = Ome::from_xml(xml)?;
(
ome.image[0].pixels.size_c as usize,
ome.image[0].pixels.size_t as usize,
ome.image[0].pixels.size_z as usize,
)
} else { } else {
metadata_map let imagej_bytes = reader
.get("channels") .current_ifd()
.get_tag(Tag::Unknown(50839))
.or_else(|_| reader.current_ifd().get_tag(Tag::ImageDescription))?;
let imagej_bytes = match imagej_bytes {
Value::Ascii(s) => s.into_bytes(),
other => other.into_u8_vec()?,
};
let metadata_str = String::from_utf8_lossy(&imagej_bytes);
let metadata_map = parse_imagej_metadata(&metadata_str);
let size_c = if p_ndim == 3 {
n_samples
} else {
metadata_map
.get("channels")
.and_then(|v| v.parse::<usize>().ok())
.unwrap_or(1)
};
let size_z = metadata_map
.get("slices")
.and_then(|v| v.parse::<usize>().ok()) .and_then(|v| v.parse::<usize>().ok())
.unwrap_or(1) .unwrap_or(1);
}; let size_t = metadata_map
let size_z = metadata_map .get("frames")
.get("slices") .and_then(|v| v.parse::<usize>().ok())
.and_then(|v| v.parse::<usize>().ok()) .unwrap_or(1);
.unwrap_or(1);
let size_t = metadata_map
.get("frames")
.and_then(|v| v.parse::<usize>().ok())
.unwrap_or(1);
let mult = if p_ndim == 3 { n_samples } else { 1 }; let mult = if p_ndim == 3 { n_samples } else { 1 };
let expected = size_c * size_z * size_t / mult; let expected = size_c * size_z * size_t / mult;
let mut n_ifds = 1; let mut n_ifds = 1;
while reader.more_images() { while reader.more_images() {
reader.next_directory()?; reader.next_directory()?;
n_ifds += 1; n_ifds += 1;
} }
let size_z = if n_ifds != expected && n_ifds > 0 { let size_z = if n_ifds != expected && n_ifds > 0 {
n_ifds / (size_c * size_t / mult).max(1) n_ifds / (size_c * size_t / mult).max(1)
} else { } else {
size_z size_z
};
(size_c, size_t, size_z)
}; };
drop(reader); drop(reader);
@@ -220,139 +234,146 @@ impl Reader for TiffReader {
fn metadata(&self) -> Result<Ome, Error> { fn metadata(&self) -> Result<Ome, Error> {
let mut reader = self.get_reader()?; let mut reader = self.get_reader()?;
let imagej_bytes = reader let image_description = reader.current_ifd().get_tag(Tag::ImageDescription);
.current_ifd() if let Ok(Value::Ascii(xml)) = image_description
.get_tag(Tag::Unknown(50839)) && xml.starts_with("<?xml")
.or_else(|_| reader.current_ifd().get_tag(Tag::ImageDescription))?; {
let imagej_bytes = match imagej_bytes { Ok(Ome::from_xml(xml)?)
Value::Ascii(s) => s.into_bytes(), } else {
other => other.into_u8_vec()?, let imagej_bytes = reader
}; .current_ifd()
let metadata_str = String::from_utf8_lossy(&imagej_bytes); .get_tag(Tag::Unknown(50839))
let metadata_map = parse_imagej_metadata(&metadata_str); .or_else(|_| reader.current_ifd().get_tag(Tag::ImageDescription))?;
let imagej_bytes = match imagej_bytes {
Value::Ascii(s) => s.into_bytes(),
other => other.into_u8_vec()?,
};
let metadata_str = String::from_utf8_lossy(&imagej_bytes);
let metadata_map = parse_imagej_metadata(&metadata_str);
let interval_t = metadata_map let interval_t = metadata_map
.get("interval") .get("interval")
.and_then(|v| v.parse::<f64>().ok()); .and_then(|v| v.parse::<f64>().ok());
let ru = reader let ru = reader
.current_ifd() .current_ifd()
.get_tag(Tag::ResolutionUnit) .get_tag(Tag::ResolutionUnit)
.ok() .ok()
.and_then(|i| i.into_u16().ok()) .and_then(|i| i.into_u16().ok())
.unwrap_or(2); .unwrap_or(2);
let pixel_size = reader let pixel_size = reader
.current_ifd() .current_ifd()
.get_tag(Tag::XResolution) .get_tag(Tag::XResolution)
.ok() .ok()
.and_then(|i| i.into_u64_vec().ok()) .and_then(|i| i.into_u64_vec().ok())
.and_then(|i| { .and_then(|i| {
i.first() i.first()
.and_then(|&n| i.get(1).map(|&d| (n as f64) / (d as f64))) .and_then(|&n| i.get(1).map(|&d| (n as f64) / (d as f64)))
}) })
.and_then(|i| { .and_then(|i| {
if i <= 0.0 { if i <= 0.0 {
None None
} else if ru == 3 { } else if ru == 3 {
Some(1e4 / i) Some(1e4 / i)
} else if ru == 2 { } else if ru == 2 {
Some(25400.0 / i) Some(25400.0 / i)
} else { } else {
Some(1.0 / i) Some(1.0 / i)
} }
}); });
let interval_z = metadata_map let interval_z = metadata_map
.get("spacing") .get("spacing")
.and_then(|v| v.parse::<f64>().ok()); .and_then(|v| v.parse::<f64>().ok());
let ome_pixel_type = match self.pixel_type { let ome_pixel_type = match self.pixel_type {
PixelType::I8 => ome::PixelType::Int8, PixelType::I8 => ome::PixelType::Int8,
PixelType::U8 => ome::PixelType::Uint8, PixelType::U8 => ome::PixelType::Uint8,
PixelType::I16 => ome::PixelType::Int16, PixelType::I16 => ome::PixelType::Int16,
PixelType::U16 => ome::PixelType::Uint16, PixelType::U16 => ome::PixelType::Uint16,
PixelType::I32 => ome::PixelType::Int32, PixelType::I32 => ome::PixelType::Int32,
PixelType::U32 => ome::PixelType::Uint32, PixelType::U32 => ome::PixelType::Uint32,
PixelType::F32 => ome::PixelType::Float, PixelType::F32 => ome::PixelType::Float,
PixelType::F64 => ome::PixelType::Double, PixelType::F64 => ome::PixelType::Double,
_ => ome::PixelType::Uint16, _ => ome::PixelType::Uint16,
}; };
let mut pixels = ome::Pixels { let mut pixels = ome::Pixels {
id: "Pixels:0".to_string(), id: "Pixels:0".to_string(),
size_x: self.shape.x as i32, size_x: self.shape.x as i32,
size_y: self.shape.y as i32, size_y: self.shape.y as i32,
size_z: self.shape.z as i32, size_z: self.shape.z as i32,
size_c: self.shape.c as i32, size_c: self.shape.c as i32,
size_t: self.shape.t as i32, size_t: self.shape.t as i32,
dimension_order: ome::PixelsDimensionOrderType::Xyczt, dimension_order: ome::PixelsDimensionOrderType::Xyczt,
r#type: ome_pixel_type, r#type: ome_pixel_type,
physical_size_x: pixel_size.map(|v| v as f32), physical_size_x: pixel_size.map(|v| v as f32),
physical_size_x_unit: ome::UnitsLength::um, physical_size_x_unit: ome::UnitsLength::um,
physical_size_y: pixel_size.map(|v| v as f32), physical_size_y: pixel_size.map(|v| v as f32),
physical_size_y_unit: ome::UnitsLength::um, physical_size_y_unit: ome::UnitsLength::um,
physical_size_z: interval_z.map(|v| v as f32), physical_size_z: interval_z.map(|v| v as f32),
physical_size_z_unit: ome::UnitsLength::um, physical_size_z_unit: ome::UnitsLength::um,
time_increment: interval_t.map(|v| v as f32), time_increment: interval_t.map(|v| v as f32),
time_increment_unit: ome::UnitsTime::s, time_increment_unit: ome::UnitsTime::s,
significant_bits: None, significant_bits: None,
interleaved: None, interleaved: None,
big_endian: None, big_endian: None,
channel: Vec::new(), channel: Vec::new(),
bin_data: Vec::new(), bin_data: Vec::new(),
tiff_data: Vec::new(), tiff_data: Vec::new(),
metadata_only: None, metadata_only: None,
plane: Vec::new(), plane: Vec::new(),
}; };
for c in 0..self.shape.c { for c in 0..self.shape.c {
pixels.channel.push(ome::Channel { pixels.channel.push(ome::Channel {
id: format!("Channel:{}", c), id: format!("Channel:{}", c),
name: Some(format!("Channel {}", c)), name: Some(format!("Channel {}", c)),
..Default::default() ..Default::default()
}); });
} }
for c in 0..self.shape.c { for c in 0..self.shape.c {
for z in 0..self.shape.z { for z in 0..self.shape.z {
for t in 0..self.shape.t { for t in 0..self.shape.t {
pixels.plane.push(ome::Plane { pixels.plane.push(ome::Plane {
the_c: Some(c as i32), the_c: Some(c as i32),
the_z: Some(z as i32), the_z: Some(z as i32),
the_t: Some(t as i32), the_t: Some(t as i32),
delta_t: interval_t.and_then(|v| { delta_t: interval_t.and_then(|v| {
if v > 0.0 { if v > 0.0 {
Some((t as f64 * v) as f32) Some((t as f64 * v) as f32)
} else { } else {
None None
} }
}), }),
..Default::default() ..Default::default()
}); });
}
} }
} }
}
let mut ome = Ome::default(); let mut ome = Ome::default();
ome.image.push(ome::Image { ome.image.push(ome::Image {
id: "Image:0".to_string(), id: "Image:0".to_string(),
name: self name: self
.path .path
.file_name() .file_name()
.map(|n| n.to_string_lossy().to_string()), .map(|n| n.to_string_lossy().to_string()),
pixels, pixels,
instrument_ref: None, instrument_ref: None,
objective_settings: None, objective_settings: None,
acquisition_date: None, acquisition_date: None,
description: None, description: None,
experimenter_ref: None, experimenter_ref: None,
experiment_ref: None, experiment_ref: None,
experimenter_group_ref: None, experimenter_group_ref: None,
imaging_environment: None, imaging_environment: None,
stage_label: None, stage_label: None,
roi_ref: Vec::new(), roi_ref: Vec::new(),
microbeam_manipulation_ref: Vec::new(), microbeam_manipulation_ref: Vec::new(),
annotation_ref: Vec::new(), annotation_ref: Vec::new(),
}); });
Ok(ome) Ok(ome)
}
} }
fn get_frame(&self, c: usize, z: usize, t: usize) -> Result<Frame, Error> { fn get_frame(&self, c: usize, z: usize, t: usize) -> Result<Frame, Error> {
@@ -521,6 +542,8 @@ mod tests {
metadata_e: "tiff/1xp53-01-AP1.tiff", metadata_e: "tiff/1xp53-01-AP1.tiff",
metadata_f: "tiff/test.tif", metadata_f: "tiff/test.tif",
metadata_g: "tiff/YTL1849A111_2023_05_04__14_46_19_cellnr_1_track.tif", metadata_g: "tiff/YTL1849A111_2023_05_04__14_46_19_cellnr_1_track.tif",
metadata_h: "tiff/Wapl-AID_C46_13_tagRFPt_MCP_well1_video1_1_max.tif" metadata_h: "tiff/Wapl-AID_C46_13_tagRFPt_MCP_well1_video1_1_max.tif",
metadata_i: "tiff/R1_NRF1_Gtf3a500nM_REAL_20260731_25pct_500ms_001_MMStack_PosR1_NRF1_Gtf3a500nM_REAL_20260731_25pct_500ms_001.ome.tif"
metadata_j: "tiff/20260922_Pos0_max.tif"
} }
} }