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
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
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