Mentions légales du service

Skip to content
Snippets Groups Projects
unit.py 6.6 KiB
Newer Older
DEBREUVE Eric's avatar
DEBREUVE Eric committed
# Copyright CNRS/Inria/UNS
# Contributor(s): Eric Debreuve (since 2019), Morgane Nadal (2020)
#
# eric.debreuve@cnrs.fr
#
# This software is governed by the CeCILL  license under French law and
# abiding by the rules of distribution of free software.  You can  use,
# modify and/ or redistribute the software under the terms of the CeCILL
# license as circulated by CEA, CNRS and INRIA at the following URL
# "http://www.cecill.info".
#
# As a counterpart to the access to the source code and  rights to copy,
# modify and redistribute granted by the license, users are provided only
# with a limited warranty  and the software's author,  the holder of the
# economic rights,  and the successive licensors  have only  limited
# liability.
#
# In this respect, the user's attention is drawn to the risks associated
# with loading,  using,  modifying and/or developing or reproducing the
# software by the user in light of its specific status of free software,
# that may mean  that it is complicated to manipulate,  and  that  also
# therefore means  that it is reserved for developers  and  experienced
# professionals having in-depth computer knowledge. Users are therefore
# encouraged to load and test the software's suitability as regards their
# requirements in conditions enabling the security of their systems and/or
# data to be ensured and,  more generally, to use and operate it in the
# same conditions as regards security.
#
# The fact that you are presently reading this means that you have had
# knowledge of the CeCILL license and that you accept its terms.

import math as mt_
import re as re_
from pathlib import Path as path_t
from typing import Any, Sequence, Union
DEBREUVE Eric's avatar
DEBREUVE Eric committed

import numpy as nmpy
from logger_36 import LOGGER
DEBREUVE Eric's avatar
DEBREUVE Eric committed
from PIL import Image
from PIL.ExifTags import TAGS

from brick.type.base import array_t
DEBREUVE Eric's avatar
DEBREUVE Eric committed


def FindVoxelDimensionInMicron(
    data_path: path_t, voxel_size_in_micron: list = None
DEBREUVE Eric's avatar
DEBREUVE Eric committed
) -> array_t:
    """
    Find Voxel dimension in micron from the image metadata.
    """
    #
    if voxel_size_in_micron is not None:
        LOGGER.info(f"VOXEL DIM: [X Y Z] = {voxel_size_in_micron} microns")
        return nmpy.array(voxel_size_in_micron)
DEBREUVE Eric's avatar
DEBREUVE Eric committed

    else:  # TODO if not found, try to make something more general
        LOGGER.warning(
            "Warning: The size of a voxel is not specified in the parameters."
        )
DEBREUVE Eric's avatar
DEBREUVE Eric committed

        try:
            # Find the voxels dimensions in micron in the metadata.
            # /!\ Very specific to one type of microscope metadata !

            with Image.open(data_path) as img:
                print(img.size)
DEBREUVE Eric's avatar
DEBREUVE Eric committed
                # Use the exif tags into the image metadata
                meta_dict = {TAGS.get(key, "missing"): img.tag[key] for key in img.tag}
                # TODO: replace meta_dict processing below with meta_dict_2 processing... one day
                meta_dict_2 = {}
                for tag_id in img.tag:
                    value = img.tag[tag_id]
                    if isinstance(value, bytes):
                        value = value.decode("utf8")
                        value = value.replace("\x00", "")
                        if "\n" in value:
                            sub_dictionary = {}
                            for line in value.split("\n"):
                                if "=" in line:
                                    sub_key, sub_value = line.split("=")
                                    sub_dictionary[sub_key.strip()] = ConvertedValue(
                                        sub_value.strip()
                                    )
                                else:
                                    sub_dictionary[
                                        line.strip()
                                    ] = "Not a Key=Value entry"
                            value = sub_dictionary
                        else:
                            value = ConvertedValue(value)
                    elif isinstance(value, Sequence) and (value.__len__() == 1):
                        value = ConvertedValue(value[0])
                    else:
                        value = ConvertedValue(value)
                    meta_dict_2[TAGS.get(tag_id, tag_id)] = value
DEBREUVE Eric's avatar
DEBREUVE Eric committed

            # Decode the tags text
            metadata = meta_dict["missing"].decode("utf8")
            metadata = metadata.replace("\x00", "")

            # LOGGER.info(metadata)

DEBREUVE Eric's avatar
DEBREUVE Eric committed
            # Initialize the list of voxel size in str
            voxel_size = []

            for axe in "XYZ":
                pattern = (
                    "Voxel" + axe + ".+\= (\d.+E.\d.)"
                )  # Regular expression found in metadata
                voxel_size.append(re_.findall(pattern, metadata)[0])

            voxel_size = nmpy.array(list(map(float, voxel_size)))
DEBREUVE Eric's avatar
DEBREUVE Eric committed
            # Conversion meters in micron
            voxel_size_micron = 1.0e6 * voxel_size

            print("VOXEL DIM: [X Y Z] =", voxel_size_micron, "micron.")

            return voxel_size_micron

        except Exception as exception:
            raise ValueError(
                f"{exception}: /!\ Unable to find the voxel dimensions in micron. Specify it in the parameters."
            )


def ConvertedValue(value: Any) -> Union[Any, bool, int, float]:
    """"""
    output = value

    if isinstance(output, str):
        no_successful_conversion = True

        if no_successful_conversion:
            try:
                output = int(output)
                no_successful_conversion = False
            except ValueError:
                pass

        if no_successful_conversion:
            try:
                output = float(output)
                no_successful_conversion = False
            except ValueError:
                pass

        if no_successful_conversion:
            lower_cased = output.lower()
            if lower_cased == "false":
                output = False
            elif lower_cased == "true":
                output = True

    return output


DEBREUVE Eric's avatar
DEBREUVE Eric committed
def ToPixel(
    micron: float,
    voxel_size_micron: array_t,
    dimension: tuple = (0,),
    decimals: int = None,
) -> int:
    """
    Dimension correspond to the axis (X,Y,Z) = (0,1,2). Can be used for distance, area and volumes.
    """
    # Conversion of micron into pixels.
    return round(
        micron / (mt_.prod(voxel_size_micron[axis] for axis in dimension)), decimals
    )


def ToMicron(
    pixel: float,
    voxel_size_micron: Sequence,
    dimension: tuple = (0,),
    decimals: int = None,
DEBREUVE Eric's avatar
DEBREUVE Eric committed
) -> float:
    """
    Dimension correspond to the axis (X,Y,Z) = (0,1,2). Can be used for distance, area and volumes.
    """
    # Conversion of pixels into microns
    return round(
        pixel * (mt_.prod(voxel_size_micron[axis] for axis in dimension)), decimals
    )