diff --git a/poetry.lock b/poetry.lock index 287530d6..8b95696a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -145,13 +145,13 @@ tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipyth [[package]] name = "imcf-fiji-mocks" -version = "0.6.0" +version = "0.7.0" description = "Mocks collection for Fiji-Python. Zero functional code." optional = false python-versions = ">=2.7" files = [ - {file = "imcf_fiji_mocks-0.6.0-py2.py3-none-any.whl", hash = "sha256:77396edd71133b8a7b2c3c4257d176c1e695f6b158e73af80a4441658988f332"}, - {file = "imcf_fiji_mocks-0.6.0.tar.gz", hash = "sha256:cc48debf0d9eb26d932ea29364370caa6dc46fe26e2e992080ba1cf3a087b204"}, + {file = "imcf_fiji_mocks-0.7.0-py2.py3-none-any.whl", hash = "sha256:643c3d4cc916d1573f1f3490885e37822c11c40c09b6a1e06c2fc561c8aeb20e"}, + {file = "imcf_fiji_mocks-0.7.0.tar.gz", hash = "sha256:65eab1974629ef72bbe7b8d76e81e067d5fb55b65aab8fb7e0f24755e0f7ac6b"}, ] [[package]] @@ -520,4 +520,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = ">=3.10" -content-hash = "a626402e812f99ff5599089b35498cd1146ced3c5e952f4cf0caf81e473f42b2" +content-hash = "010a8923c68fb6e367da60506e1937f570cd37633e42c44ba312012be4aa2172" diff --git a/pyproject.toml b/pyproject.toml index ea76ce15..8d534db5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,7 @@ version = "0.0.0" # - or: python = ">=3.10" [tool.poetry.dependencies] -imcf-fiji-mocks = ">=0.6.0" +imcf-fiji-mocks = ">=0.7.0" python = ">=2.7" python-micrometa = "^15.2.2" sjlogging = ">=0.5.2" diff --git a/src/imcflibs/imagej/projections.py b/src/imcflibs/imagej/projections.py index 1a6d0501..4379d6a3 100644 --- a/src/imcflibs/imagej/projections.py +++ b/src/imcflibs/imagej/projections.py @@ -1,13 +1,18 @@ -"""Functions for creating Z projections.""" +"""Functions for creating projections.""" from ij.plugin import ZProjector # pylint: disable-msg=E0401 from .bioformats import export_using_orig_name # pylint: disable-msg=E0401 from ..log import LOG as log +from net.imagej.axis import Axes +from net.imagej.ops import Ops +from ij import ImagePlus, IJ +from net.imagej import Dataset + def average(imp): - """Create an average intensity projection. + """Create an average intensity Z projection. Parameters ---------- @@ -23,13 +28,13 @@ def average(imp): log.warn("ImagePlus is not a z-stack, not creating a projection!") return imp - log.debug("Creating average projection...") + log.debug("Creating average Z projection...") proj = ZProjector.run(imp, "avg") return proj def maximum(imp): - """Create a maximum intensity projection. + """Create a maximum intensity Z projection. Parameters ---------- @@ -45,7 +50,7 @@ def maximum(imp): log.warn("ImagePlus is not a z-stack, not creating a projection!") return imp - log.debug("Creating maximum intensity projection...") + log.debug("Creating maximum intensity Z projection...") proj = ZProjector.run(imp, "max") return proj @@ -100,3 +105,77 @@ def create_and_save(imp, projections, path, filename, export_format): proj.close() return True + + +def project_stack(imp, projected_dimension, projection_type, ops, ds, cs): + """Project along a defined axis using the given projection type. + + Parameters + ---------- + imp : ImagePlus + The input image to be projected. + projected_dimension : str + The dimension along which to project the data. Must be one of {"X", "Y", "Z", + "TIME", "CHANNEL"}. + projection_type : str + The type of projection to perform. Must be one of {"Max", "Mean", "Median", + "Min", "StdDev", "Sum"}. + ops : OpService + The service used to access image processing operations. Use e.g. from script + parameter: `#@ OpService ops` + ds : DatasetService + The service used to create new datasets. Use e.g. from script parameter: + `#@ DatasetService ds` + cs : ConvertService + The service used to convert between formats. Use e.g. from script parameter: + `#@ ConvertService cs` + + Returns + ------- + ImagePlus + The resulting projected image as an ImagePlus object. + + Raises + ------ + Exception + If the specified dimension is not found or if the dimension has only one frame. + """ + bit_depth = imp.getBitDepth() + data = cs.convert(imp, Dataset) + # Select which dimension to project + dim = data.dimensionIndex(getattr(Axes, projected_dimension)) + if dim == -1: + raise Exception("%s dimension not found." % projected_dimension) + if data.dimension(dim) < 2: + raise Exception("%s dimension has only one frame." % projected_dimension) + + # Write the output dimensions + new_dimensions = [ + data.dimension(d) for d in range(0, data.numDimensions()) if d != dim + ] + + # Create the output image + projected = ops.create().img(new_dimensions) + + # Create the op and run it + proj_op = ops.op(getattr(Ops.Stats, projection_type), data) + ops.transform().project(projected, data, proj_op, dim) + + # Create the output Dataset and convert to ImagePlus + output = ds.create(projected) + output_imp = cs.convert(output, ImagePlus) + output_imp = output_imp.duplicate() + output_imp.setTitle("%s %s projection" % (projected_dimension, projection_type)) + IJ.run(output_imp, "Enhance Contrast", "saturated=0.35") + + # Rescale bit depth if possible + if projection_type in ["Max", "Min", "Median"]: + IJ.run("Conversions...", " ") + if bit_depth in [8, 16]: + IJ.run(output_imp, str(bit_depth) + "-bit", "") + if bit_depth == 12: + IJ.run(output_imp, "16-bit", "") + + IJ.run("Conversions...", "scale") + + return output_imp