|
17 | 17 | FuseBigStitcherDatasetIntoOMETiffCommand, |
18 | 18 | ) |
19 | 19 | from ij import IJ |
| 20 | +from java.io import File, FileInputStream, InputStreamReader |
| 21 | +from javax.xml.parsers import DocumentBuilderFactory |
| 22 | +from org.xml.sax import InputSource |
20 | 23 |
|
21 | 24 | from .. import pathtools |
22 | 25 | from ..log import LOG as log |
@@ -1666,3 +1669,93 @@ def fuse_dataset_bdvp( |
1666 | 1669 | "compress_temp_files", |
1667 | 1670 | False, |
1668 | 1671 | ) |
| 1672 | + |
| 1673 | + |
| 1674 | +def read_metadata_from_xml(xml_path): |
| 1675 | + """Extract metadata from a Zeiss Lightsheet microscopy XML file. |
| 1676 | +
|
| 1677 | + Parse the XML document to retrieve the number of channels, illuminations, |
| 1678 | + and timepoints from the experiment metadata. |
| 1679 | +
|
| 1680 | + Parameters |
| 1681 | + ---------- |
| 1682 | + xml_path : str |
| 1683 | + Path to the XML metadata file. |
| 1684 | +
|
| 1685 | + Returns |
| 1686 | + ------- |
| 1687 | + dict |
| 1688 | + A dictionary containing the following keys: |
| 1689 | + - 'channels_count': Number of channels in the dataset |
| 1690 | + - 'illuminations_count': Number of illumination directions |
| 1691 | + - 'timepoints_count': Number of timepoints in the dataset |
| 1692 | +
|
| 1693 | + Examples |
| 1694 | + -------- |
| 1695 | + >>> metadata = read_metadata_from_xml("/path/to/experiment.xml") |
| 1696 | + >>> print(metadata["channels_count"]) |
| 1697 | + ... 2 |
| 1698 | + >>> print(metadata["illuminations_count"]) |
| 1699 | + ... 4 |
| 1700 | + >>> print(metadata["timepoints_count"]) |
| 1701 | + ... 1 |
| 1702 | + """ |
| 1703 | + # Use our robust XML parsing function |
| 1704 | + dbf = DocumentBuilderFactory.newInstance() |
| 1705 | + db = dbf.newDocumentBuilder() |
| 1706 | + |
| 1707 | + # Initialize default values |
| 1708 | + nbr_chnl = 1 |
| 1709 | + nbr_ill = 1 |
| 1710 | + nbr_tp = 1 |
| 1711 | + |
| 1712 | + reader = None |
| 1713 | + try: |
| 1714 | + # This is needed to fix some issues with the micron symbol in the xml file |
| 1715 | + reader = InputStreamReader(FileInputStream(File(xml_path))) |
| 1716 | + dom = db.parse(InputSource(reader)) |
| 1717 | + |
| 1718 | + # Extract channel and illumination counts |
| 1719 | + nodeList = dom.getElementsByTagName("Attributes") |
| 1720 | + for i in range(nodeList.getLength()): |
| 1721 | + name_attr = nodeList.item(i).getAttributes().getNamedItem("name") |
| 1722 | + if name_attr is None: |
| 1723 | + continue |
| 1724 | + |
| 1725 | + node = name_attr.getNodeValue() |
| 1726 | + if node == "channel": |
| 1727 | + nbr_chnl = int( |
| 1728 | + nodeList.item(i).getElementsByTagName("Channel").getLength() |
| 1729 | + ) |
| 1730 | + if node == "illumination": |
| 1731 | + nbr_ill = int( |
| 1732 | + nodeList.item(i).getElementsByTagName("Illumination").getLength() |
| 1733 | + ) |
| 1734 | + |
| 1735 | + # Get timepoints |
| 1736 | + timepoints_node = dom.getElementsByTagName("Timepoints") |
| 1737 | + if timepoints_node.getLength() > 0: |
| 1738 | + last_nodes = timepoints_node.item(0).getElementsByTagName("last") |
| 1739 | + if last_nodes.getLength() > 0: |
| 1740 | + nbr_tp = int(last_nodes.item(0).getTextContent()) + 1 |
| 1741 | + except Exception as e: |
| 1742 | + # log.exception includes the traceback when available |
| 1743 | + try: |
| 1744 | + log.exception("Error extracting metadata from XML: %s", e) |
| 1745 | + except Exception: |
| 1746 | + log.error("Error extracting metadata from XML: %s", str(e)) |
| 1747 | + finally: |
| 1748 | + # Ensure the Java reader is closed to free resources |
| 1749 | + try: |
| 1750 | + if reader is not None: |
| 1751 | + reader.close() |
| 1752 | + except Exception: |
| 1753 | + pass |
| 1754 | + |
| 1755 | + xml_metadata = { |
| 1756 | + "channels_count": nbr_chnl, |
| 1757 | + "illuminations_count": nbr_ill, |
| 1758 | + "timepoints_count": nbr_tp, |
| 1759 | + } |
| 1760 | + |
| 1761 | + return xml_metadata |
0 commit comments