def build_heat_map(shape, coords): """Build a heat map for an image based on point placement within it. The heat map is scaled at the level of ``shape``, generally assuming that ``coords`` have been scaled from a larger size. In other words, the heat map will show the density at the level of its pixels and can be further rescaled/resized to show density at different resolutions. Args: shape: Shape of image that contains ``coords``. coords: Array of coordinates of points. The array should have shape (n, m), where n = number of coordinate sets, and m = number of coordinate dimensions. Returns: :obj:`np.ndaarry`: An image of shape ``shape`` with values corresponding to the number of point occurrences at each pixel. """ if coords is not None and len(coords) > 0: # get counts of points at the same coordinate as a measure of density coords_unique, coords_count = np.unique(coords, return_counts=True, axis=0) coordsi = libmag.coords_for_indexing(coords_unique) dtype = libmag.dtype_within_range(0, np.amax(coords_count), True, False) heat_map = np.zeros(shape, dtype=dtype) heat_map[tuple(coordsi)] = coords_count else: # generate an array with small int type if no coords are available heat_map = np.zeros(shape, dtype=np.uint8) return heat_map
def _markers_from_blobs(roi, blobs): # use blobs as seeds by converting blobs into marker image markers = np.zeros(roi.shape, dtype=np.uint8) coords = libmag.coords_for_indexing(blobs[:, :3].astype(int)) markers[tuple(coords)] = 1 markers = morphology.dilation(markers, morphology.ball(1)) markers = measure.label(markers) return markers
def get_label_ids_from_position(coord, labels_img, scaling=None, rounding=False, return_coord_scaled=False): """Get the atlas label IDs for the given coordinates. Args: coord: Coordinates of experiment image in (z, y, x) order. Can be an [n, 3] array of coordinates. labels_img: The registered image whose intensity values correspond to label IDs. scaling: Scaling factor for the labels image size compared with the experiment image; defaults to None. rounding: True to round coordinates after scaling, which should be used rounding to reverse coordinates that were previously scaled inversely to avoid size degredation with repeated scaling. Typically rounding is False (default) so that coordinates fall evenly to their lowest integer, without exceeding max bounds. return_coord_scaled: True to return the array of scaled coordinates; defaults to False. Returns: An array of label IDs corresponding to ``coord``, or a scalar of one ID if only one coordinate is given. If ``return_coord_scaled`` is True, also returns a Numpy array of the same shape as ``coord`` scaled based on ``scaling``. """ libmag.printv("getting label IDs from coordinates using scaling", scaling) coord_scaled = coord if scaling is not None: # scale coordinates to atlas image size coord_scaled = np.multiply(coord, scaling) if rounding: # round when extra precision is necessary, such as during reverse # scaling, which requires clipping so coordinates don't exceed labels # image shape coord_scaled = np.around(coord_scaled).astype(np.int) coord_scaled = np.clip(coord_scaled, None, np.subtract(labels_img.shape, 1)) else: # typically don't round to stay within bounds coord_scaled = coord_scaled.astype(np.int) # index blob coordinates into labels image by int array indexing to # get the corresponding label IDs coordsi = libmag.coords_for_indexing(coord_scaled) label_ids = labels_img[tuple(coordsi)][0] if return_coord_scaled: return label_ids, coord_scaled return label_ids
def get_label_ids_from_position(coord_scaled, labels_img): """Get the atlas label IDs for the given coordinates. Args: coord_scaled (:class:`numpy.ndarray`): 2D array of coordinates in ``[[z,y,x], ...]`` format, or a single row as a 1D array. labels_img (:class:`numpy.ndarray`): Labeled image from which to extract labels at coordinates in ``coord_scaled``. Returns: :class:`numpy.ndarray`: An array of label IDs corresponding to ``coord``, or a scalar of one ID if only one coordinate is given. """ # index blob coordinates into labels image by int array indexing to # get the corresponding label IDs coordsi = libmag.coords_for_indexing(coord_scaled) label_ids = labels_img[tuple(coordsi)][0] return label_ids
def colocalize_blobs(roi, blobs, thresh=None): """Co-localize blobs from different channels based on surrounding intensities. Thresholds for detection are first identified in each channel by taking the blobs in the given channel, finding the surrounding intensities, and taking a low (5th) percentile. Then for each channel, the surrounding intensities of blobs in that channel are compared with the thresholds in the other channels. Blobs exceeding any given threshold are considered to co-localize in that channel. Args: roi (:obj:`np.ndarray`): Region of interest as a 3D+channel array. blobs (:obj:`np.ndarray`): Blobs as a 2D array in the format ``[[z, y, x, radius, confirmation, truth, channel...], ...]``. thresh (int, float, str): Threshold percentile of intensities from pixels surrounding each blob in the given channel. Use "min" to instead take the mininimum average intensity of all blobs in the channel. Defaults to None to use "min". Returns: :obj:`np.ndarray`: 2D Numpy array of same length as ``blobs`` with a column for each channel where 1 indicates that the corresponding blob has signal is present in the given channels at the blob's location, and 0 indicates insufficient signal. """ if blobs is None or roi is None or len(roi.shape) < 4: return None if thresh is None: thresh = "min" print("Colocalizing blobs based on image intensity across channels") threshs = [] selem = morphology.ball(2) # find only blobs in ROI since blobs list may include blobs from immediate # surrounds, but ROI is not available for them blobs_roi, blobs_roi_mask = detector.get_blobs_in_roi( blobs, (0, 0, 0), roi.shape[:3], reverse=False) blobs_chl = detector.get_blobs_channel(blobs_roi) blobs_range_chls = [] # get labeled masks of blobs for each channel and threshold intensities mask_roi = np.ones(roi.shape[:3], dtype=int) mask_roi_chls = [] for chl in range(roi.shape[3]): # label a mask with blob indices surrounding each blob blobs_chl_mask = np.isin(blobs_chl, chl) blobs_range = np.where(blobs_chl_mask)[0] blobs_range_chls.append(blobs_range) mask = np.copy(mask_roi) * -1 mask[tuple(libmag.coords_for_indexing( blobs_roi[blobs_chl_mask, :3].astype(int)))] = blobs_range mask = morphology.dilation(mask, selem=selem) mask_roi_chls.append(mask) if thresh == "min": # set minimum average surrounding intensity of all blobs as thresh threshs.append( None if len(blobs_range) == 0 else np.amin([ np.mean(roi[mask == b, chl]) for b in blobs_range])) else: # set a percentile of intensities surrounding all blobs in channel # as threshold for that channel, or the whole ROI if no blobs mask_blobs = mask >= 0 roi_mask = roi if np.sum(mask_blobs) < 1 else roi[mask_blobs, chl] threshs.append(np.percentile(roi_mask, thresh)) channels = np.unique(detector.get_blobs_channel(blobs_roi)).astype(int) colocs_roi = np.zeros((blobs_roi.shape[0], roi.shape[3]), dtype=np.uint8) for chl in channels: # get labeled mask of blobs in the given channel mask = mask_roi_chls[chl] blobs_range = blobs_range_chls[chl] for chl_other in channels: if threshs[chl_other] is None: continue for blobi in blobs_range: # find surrounding intensity of blob in another channel mask_blob = mask == blobi blob_avg = np.mean(roi[mask_blob, chl_other]) if config.verbose: print(blobi, detector.get_blob_channel(blobs_roi[blobi]), blobs_roi[blobi, :3], blob_avg, threshs[chl_other]) if blob_avg >= threshs[chl_other]: # intensities in another channel around blob's position # is above that channel's threshold colocs_roi[blobi, chl_other] = 1 # create array for all blobs including those outside ROI colocs = np.zeros((blobs.shape[0], roi.shape[3]), dtype=np.uint8) colocs[blobs_roi_mask] = colocs_roi if config.verbose: for i, (blob, coloc) in enumerate(zip(blobs_roi, colocs)): print(i, detector.get_blob_channel(blob), blob[:3], coloc) return colocs