diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..001456e --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,42 @@ +[build-system] +requires = ["setuptools>=61.0"] +build-backend = "setuptools.build_meta" + +[project] +name = "ploc" +version = "0.1.0" +description = "Localizing particles in 3D." +readme = "README.md" +requires-python = ">=3.12" +license = { text = "GPL-3.0-or-later" } +authors = [{ name = "Martin Laasmaa", email = "martin@sysbio.ioc.ee" }] + +dependencies = [ + "numpy", + "scipy", + "matplotlib", + "tifffile", + "czifile", + "roifile", + "xmltodict", + "imagecodecs", + "xlsxwriter", + "openpyxl", +] + +[project.optional-dependencies] +dev = ["pytest", "black", "flake8"] + +[project.scripts] +ploc-gui = "ploc.ploc_app:main" + +[tool.setuptools] +package-dir = { "" = "src" } + +[tool.setuptools.packages.find] +where = ["ploc"] + + +[tool.black] +line-length = 140 +target-version = ['py310'] diff --git a/src/ploc/__init__.py b/src/ploc/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/ploc/io/image.py b/src/ploc/io/image.py new file mode 100644 index 0000000..1e0eba8 --- /dev/null +++ b/src/ploc/io/image.py @@ -0,0 +1,19 @@ +from pathlib import Path +from ploc.io.read_zeiss import ZeissData + + +class Image: + def __init__(self, filename: Path): + self.filename = filename + if filename.suffix == ".czi": + self._read_czi() + elif filename.suffix == ".tif": + self._read_tif() + else: + raise ValueError(f"Unsupported file format: {filename.suffix}") + + def _read_czi(self): + pass + + def _read_tif(self): + pass diff --git a/src/ploc/io/read_zeiss.py b/src/ploc/io/read_zeiss.py new file mode 100644 index 0000000..9994ee6 --- /dev/null +++ b/src/ploc/io/read_zeiss.py @@ -0,0 +1,93 @@ +import numpy as np + +import czifile as czi +import xml.etree.ElementTree as ET + + +class ZeissData: + + def __init__(self, filename): + + self.filename = filename + self._initialize() + + def _initialize(self): + czi_obj = czi.CziFile(self.filename) + axes = czi_obj.axes + tmp = czi_obj.asarray() + self.axes = "" + # print(tmp.shape) + for i, size in enumerate(tmp.shape): + if size > 1: + self.axes += axes[i] + + self.image = np.squeeze(tmp) + self.shape = self.image.shape + self.ndim = len(self.shape) + self.origin = np.array([[0, i1] for i1 in self.shape]) + + self.metadata = czi_obj.metadata() + root = ET.fromstring(self.metadata) + for d in root.findall("./Metadata/Scaling/Items/Distance"): + el = d.attrib["Id"] + if el in self.axes: + setattr(self, "p" + el.lower(), float(d[0].text) * 1e6) + + # print(self.axes) + # print(self.shape) + + # for k, v in kwargs.items(): + # if k in ImageJ.allowed_kwargs: + # setattr(self, k, v) + # else: + # print(k, "is not allowed keyword argument!") + + def get_channle_info(self, channelID): + # print(self.metadata) + root = ET.fromstring(self.metadata) + # for child in root: + # print(child.tag, child.attrib) + # print(type(root)) + + na = None + refractive_index = None + for _child in root.findall("./Metadata/Information/Instrument/Objectives/Objective"): + for _ch in _child: + if _ch.tag == "LensNA": + na = _ch.text + + if _ch.tag == "ImmersionRefractiveIndex": + refractive_index = _ch.text + # print(_child.tag, _child.attrib, _child[1].tag, type(_child.tag), type(_child.attrib)) + + em, ex = None, None + for _child in root.findall("./Metadata/DisplaySetting/Channels"): + + for _ch in _child.findall("./Channel"): + if _ch.attrib["Id"] == f"Channel:{channelID}": + for _c in _ch: + if _c.tag == "DyeMaxEmission": + em = _c.text + if _c.tag == "DyeMaxExcitation": + ex = _c.text + if ex is not None and em is not None: + break + + # print(_c.tag) + # print(_ch.tag, _ch.attrib["Id"], _ch.text) + + # print() + # print(_child.tag, _child.attrib, _child[1].tag, type(_child.tag), type(_child.attrib)) + + info = { + "MainEmissionWavelength": float(em), + "MainExcitationWavelength": float(ex), + "ObjectiveNA": float(na), + "RefractiveIndex": float(refractive_index), + } + return info + + def get_stack(self, channleID): + spacings = dict(X=self.px * 1e-6, Y=self.py * 1e-6, Z=self.pz * 1e-6) + + return self.image[channleID], spacings, self.get_channle_info(channleID) diff --git a/src/ploc/ploc_app.py b/src/ploc/ploc_app.py new file mode 100644 index 0000000..2f1f123 --- /dev/null +++ b/src/ploc/ploc_app.py @@ -0,0 +1,14 @@ +import argparse +from ploc.io.image import Image + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("image", type=str, help="Image file to process") + args = parser.parse_args() + + print("Hello World") + + +if __name__ == "__main__": + main()