Esempio n. 1
0
def profile_line(img, src=None, dst=None, linewidth=1, order=1, mode="constant", cval=0.0, constrain=True, **kargs):
    """Wrapper for sckit-image method of the same name to get a line_profile.

    Parameters:
        img(ImageArray): Image data to take line section of
        src, dst (2-tuple of int or float): start and end of line profile. If the co-ordinates
            are given as intergers then they are assumed to be pxiel co-ordinates, floats are
            assumed to be real-space co-ordinates using the embedded metadata.
        linewidth (int): the wideth of the profile to be taken.
        order (int 1-3): Order of interpolation used to find image data when not aligned to a point
        mode (str): How to handle data outside of the image.
        cval (float): The constant value to assume for data outside of the image is mode is "constant"
        constrain (bool): Ensure the src and dst are within the image (default True).

    Returns:
        A :py:class:`Stoner.Data` object containing the line profile data and the metadata from the image.
    """
    scale = img.get("MicronsPerPixel", 1.0)
    r, c = img.shape
    if src is None and dst is None:
        if "x" in kargs:
            src = (kargs["x"], 0)
            dst = (kargs["x"], r)
        if "y" in kargs:
            src = (0, kargs["y"])
            dst = (c, kargs["y"])
    if isinstance(src, float):
        src = (src, src)
    if isinstance(dst, float):
        dst = (dst, dst)
    dst = _scale(dst, scale)
    src = _scale(src, scale)
    if not istuple(src, int, int):
        raise ValueError("src co-ordinates are not a 2-tuple of ints.")
    if not istuple(dst, int, int):
        raise ValueError("dst co-ordinates are not a 2-tuple of ints.")

    if constrain:
        fix = lambda x, mx: int(round(sorted([0, x, mx])[1]))
        r, c = img.shape
        src = list(src)
        src = (fix(src[0], r), fix(src[1], c))
        dst = (fix(dst[0], r), fix(dst[1], c))

    result = measure.profile_line(img, src, dst, linewidth, order, mode, cval)
    points = measure.profile._line_profile_coordinates(src, dst, linewidth)[:, :, 0]
    ret = Data()
    ret.data = points.T
    ret.setas = "xy"
    ret &= np.sqrt(ret.x ** 2 + ret.y ** 2) * scale
    ret &= result
    ret.column_headers = ["X", "Y", "Distance", "Intensity"]
    ret.setas = "..xy"
    ret.metadata = img.metadata.copy()
    return ret
Esempio n. 2
0
def profile_line(img, src=None, dst=None, linewidth=1, order=1, mode="constant", cval=0.0, constrain=True, **kargs):
    """Wrapper for sckit-image method of the same name to get a line_profile.

    Parameters:
        img(ImageArray): Image data to take line section of
        src, dst (2-tuple of int or float): start and end of line profile. If the co-ordinates
            are given as intergers then they are assumed to be pxiel co-ordinates, floats are
            assumed to be real-space co-ordinates using the embedded metadata.
        linewidth (int): the wideth of the profile to be taken.
        order (int 1-3): Order of interpolation used to find image data when not aligned to a point
        mode (str): How to handle data outside of the image.
        cval (float): The constant value to assume for data outside of the image is mode is "constant"
        constrain (bool): Ensure the src and dst are within the image (default True).

    Returns:
        A :py:class:`Stoner.Data` object containing the line profile data and the metadata from the image.
    """
    scale = img.get("MicronsPerPixel", 1.0)
    r, c = img.shape
    if src is None and dst is None:
        if "x" in kargs:
            src = (kargs["x"], 0)
            dst = (kargs["x"], r)
        if "y" in kargs:
            src = (0, kargs["y"])
            dst = (c, kargs["y"])
    if isinstance(src, float):
        src = (src, src)
    if isinstance(dst, float):
        dst = (dst, dst)
    dst = _scale(dst, scale)
    src = _scale(src, scale)
    if not istuple(src, int, int):
        raise ValueError("src co-ordinates are not a 2-tuple of ints.")
    if not istuple(dst, int, int):
        raise ValueError("dst co-ordinates are not a 2-tuple of ints.")

    if constrain:
        fix = lambda x, mx: int(round(sorted([0, x, mx])[1]))
        r, c = img.shape
        src = list(src)
        src = (fix(src[0], r), fix(src[1], c))
        dst = (fix(dst[0], r), fix(dst[1], c))

    result = measure.profile_line(img, src, dst, linewidth, order, mode, cval)
    points = measure.profile._line_profile_coordinates(src, dst, linewidth)[:, :, 0]
    ret = Data()
    ret.data = points.T
    ret.setas = "xy"
    ret &= np.sqrt(ret.x ** 2 + ret.y ** 2) * scale
    ret &= result
    ret.column_headers = ["X", "Y", "Distance", "Intensity"]
    ret.setas = "..xy"
    ret.metadata = img.metadata.copy()
    return ret
Esempio n. 3
0
def profile_line(img,
                 src,
                 dst,
                 linewidth=1,
                 order=1,
                 mode='constant',
                 cval=0.0):
    """Wrapper for sckit-image method of the same name to get a line_profile.

    Parameters:
        img(ImageArray): Image data to take line section of
        src, dst (2-tuple of int or float): start and end of line profile. If the co-ordinates
            are given as intergers then they are assumed to be pxiel co-ordinates, floats are
            assumed to be real-space co-ordinates using the embedded metadata.
        linewidth (int): the wideth of the profile to be taken.
        order (int 1-3): Order of interpolation used to find image data when not aligned to a point
        mode (str): How to handle data outside of the image.
        cval (float): The constant value to assume for data outside of the image is mode is "constant"

    Returns:
        A :py:class:`Stoner.Data` object containing the line profile data and the metadata from the image.
    """
    scale = img.get("MicronsPerPixel", 1.0)
    if isinstance(src[0], float):
        src = (int(src[0] / scale), int(src[1] / scale))
    if isinstance(dst[0], float):
        dst = (int(dst[0] / scale), int(dst[1] / scale))

    result = measure.profile_line(img, src, dst, linewidth, order, mode, cval)
    points = measure.profile._line_profile_coordinates(src, dst,
                                                       linewidth)[:, :, 0]
    ret = Data()
    ret.data = points.T
    ret.setas = "xy"
    ret &= np.sqrt(ret.x**2 + ret.y**2) * scale
    ret &= result
    ret.column_headers = ["X", "Y", "Distance", "Intensity"]
    ret.setas = "..xy"
    ret.metadata = img.metadata.copy()
    return ret
Esempio n. 4
0
    def slice(self, key=None, values_only=False, output=None):  # pylint: disable=arguments-differ
        """Return a list of the metadata dictionaries for each item/file in the top level group

        Keyword Arguments:
            key(string or list of strings):
                if given then only return the item(s) requested from the metadata
            values_only(bool):
                if given amd *output* not set only return tuples of the dictionary values. Mostly useful
                when given a single key string
            output (str or type):
                Controls the output format from slice_metadata. Possible values are

                - "dict" or dict - return a list of dictionary subsets of the metadata from each image
                - "list" or list - return a list of values of each item pf the metadata
                - "array" or np.array - return a single array - like list above, but returns as a numpy array. This can create a 2D array from multiple keys
                - "Data" or Stoner.Data - returns the metadata in a Stoner.Data object where the column headers are the metadata keys.
                - "smart" - switch between *dict* and *list* depending whether there is one or more keys.

        Returns:
            ret(list of dict, tuple of values or :py:class:`Stoner.Data`):
                depending on *values_only* or (output* returns the sliced dictionaries or tuples/
                values of the items

        To do:
            this should probably be a func in baseFolder and should use have
            recursive options (build a dictionary of metadata values). And probably
            options to extract other parts of objects (first row or whatever).
        """
        if output is None:  # Sort out a definitive value of output
            output = "dict" if not values_only else "smart"
        if output not in [
                "dict",
                "list",
                "array",
                "Data",
                "smart",
                dict,
                list,
                _np_.ndarray,
                DataFile,
        ]:  # Check for good output value
            raise SyntaxError(
                "output of slice metadata must be either dict, list, or array not {}"
                .format(output))
        metadata = [
            k.metadata for k in self._folder
        ]  # this can take some time if it's loading in the contents of the folder
        if isinstance(key, string_types):  # Single key given
            key = metadata[0].__lookup__(key, multiple=True)
            key = [key] if not islike_list(key) else key
        # Expand all keys in case of multiple metadata matches
        newkey = []
        for k in key:
            newkey.extend(metadata[0].__lookup__(k, multiple=True))
        key = newkey
        if len(
                set(key) - set(self.common_keys)
        ):  # Is the key in the common keys? # TODO: implement __getitem__'s masked array logic?
            raise KeyError(
                "{} are missing from some items in the Folder.".format(
                    set(key) - set(self.common_keys)))
        results = []
        for i, met in enumerate(
                metadata):  # Assemble a list of dictionaries of values
            results.append({k: v for k, v in metadata[i].items() if k in key})
        if output in [
                "list", "array", "Data", list, _np_.ndarray, DataFile
        ] or (output == "smart" and len(results[0]) == 1):  # Reformat output
            cols = []
            for k in key:  # Expand the columns of data we're going to need if some values are not scalar
                if islike_list(metadata[0][k]):
                    for i, _ in enumerate(metadata[0][k]):
                        cols.append("{}_{}".format(k, i))
                else:
                    cols.append(k)

            for i, met in enumerate(results):  # For each object in the Folder
                results[i] = []
                for k in key:  # and for each key in out list
                    v = met[k]
                    if islike_list(
                            v
                    ):  # extend or append depending if the value is scalar. # TODO: This will blowup for values with more than 1 D!
                        results[i].extend(v)
                    else:
                        results[i].append(v)
                if output in ["aaray", "Data", _np_.ndarray,
                              DataFile]:  # Convert each row to an array
                    results[i] = _np_.array(results[i])
            if len(cols) == 1:  # single key
                results = [m[0] for m in results]
            if output in ["array", _np_.ndarray]:
                results = _np_.array(results)
            if output in ["Data", DataFile]:  # Build oour Data object
                from Stoner import Data

                tmp = Data()
                tmp.data = _np_.array(results)
                tmp.column_headers = cols
                results = tmp
        return results