def detect_plate(image): """ Crops to plate and outputs mask (assumes it was taken on a dark background) :param image: :return: """ lab = rgb2lab(image) l = lab[:, :, 0] # Lum is the only channel we really care about for plate segmentation edges = canny(l, 30) filled = binary_fill_holes(edges) eroded = binary_erosion(filled, disk(10)) mask = binary_dilation(eroded, disk(10)) # This is the mask mask_area = np.count_nonzero(mask) plate_radius = np.sqrt(mask_area / np.pi) padded_plate_radius = plate_radius + 0.1 * plate_radius indices = np.argwhere(mask) center = np.mean(indices, axis=0) y_min = int(max(0, np.round(center[0] - padded_plate_radius))) y_max = int(min(image.shape[0], np.round(center[0] + padded_plate_radius))) x_min = int(max(0, np.round(center[1] - padded_plate_radius))) x_max = int(min(image.shape[1], np.round(center[1] + padded_plate_radius))) cropped_image = image[y_min:y_max, x_min:x_max, :] cropped_mask = mask[y_min:y_max, x_min:x_max] new_center = np.mean(np.argwhere(cropped_mask), axis=0) cropped_mask = binary_erosion(cropped_mask, structure=disk(50)) return cropped_image, cropped_mask, new_center
def encode_centro_telomeres(image_centro, image_telo, centro_offset=0.0, centro_factor=1.0, centro_min_size=36, centro_radius=10, telo_offset=0.0, telo_adapt_radius=49, telo_open_radius=4): """Find centromeres, telomeres, and their overlap. Parameters ---------- image_centro : array, shape (M, N) The grayscale channel for centromeres. image_telo : array, shape (M, N) The grayscale channel for telomeres. centro_offset : float, optional Offset Otsu's threshold by this amount (i.e. be less stringent about what image intensity constitutes a centromere) centro_factor : float, optional Offset Otsu's threshold by a multiplicative constant. centro_min_size : int, optional Remove objects smaller than this, as they would be too small to be a centromere. centro_radius : int, optional Consider anything within this radius to be "near" a centromere. telo_offset : float, optional Offset the telomere image threshold by this amount. telo_adapt_radius : int, optional Use this radius to threshold telomere image adaptively. telo_open_radius : int, optional Use this radius for a binary opening of thresholded telomeres (removes noise). Returns ------- encoded_regions : array of int, shape (M, N) A uint8 image with the following values: - 0: background - 1: telomeres - 2: centromeres - 3: centromere/telomere overlap """ centros = otsu(image_centro, centro_offset, centro_factor) centros = remove_small_objects(centros, centro_min_size) centro_strel = selem.disk(centro_radius) centros = nd.binary_dilation(centros, structure=centro_strel) telos = imfilter.threshold_adaptive(image_telo, telo_adapt_radius, offset=telo_offset) telo_strel = selem.disk(telo_open_radius) telos = nd.binary_opening(telos, structure=telo_strel) encoded_regions = 2 * centros.astype(np.uint8) + telos return encoded_regions
def CE_e(img, radius=None, stop_condition=40): ''' Contrast Enhancement of Medical X-Ray ImageUsing Morphological Operators with OptimalStructuring Element, https://arxiv.org/pdf/1905.08545.pdf :param img: 2D np array, image :param radius: int [1-N], radius of the structuring element used for morphology operations :param stop_condition: int, value to which Edge content (EC) difference is compared, if EC difference is smaller then 'stop_condition' current value of radius consider to be optimal (recommended: 10-100 depending on the problem) :return: 2D np array, Contrast enhanced image normalized in between values [0-1] ''' A = minmax(img) ECA = np.sum(gaussian_gradient_magnitude(A, 1)) prevEC = 0 # radius adapt to the image if radius is None: convMtx = [np.Inf] for r in range(1,15): # define SE as B B = selem.disk(r) # opening and closing operations defined in the paper Atop = A - opening(A, selem=B) Abot = closing(A, selem=B) - A Aenhanced = A + Atop - Abot Aenhanced = np.clip(Aenhanced, a_min=0, a_max=None) # Edge content calculations EC = np.sum(gaussian_gradient_magnitude(Aenhanced, 1)) # min max scaling processed image Aenhanced_normed = (Aenhanced - np.min(Aenhanced))/(np.max(Aenhanced)-np.min(Aenhanced)) # stopping condition conv = EC - prevEC convMtx.append(conv) if convMtx[-2]-convMtx[-1] < stop_condition: break prevEC = EC # pre-defined radius else: print("Radius =", radius) B = selem.disk(radius) Atop = A - opening(A, selem=B) Abot = closing(A, selem=B) - A Aenhanced = A + Atop - Abot Aenhanced = np.clip(Aenhanced, a_min=0, a_max=None) EC = np.sum(gaussian_gradient_magnitude(Aenhanced, 1)) Aenhanced_normed = (Aenhanced - np.min(Aenhanced)) / (np.max(Aenhanced) - np.min(Aenhanced)) print("EC =", EC) return Aenhanced_normed
def _define_kernel(shape, size, dtype): """Build a kernel to apply a filter on images. Parameters ---------- shape : str Shape of the kernel used to compute the filter ('diamond', 'disk', 'rectangle' or 'square'). size : int, Tuple(int) or List(int) The size of the kernel: - For the rectangle we expect two values (width, height). - For the square one value (width). - For the disk and the diamond one value (radius). dtype : type Dtype used for the kernel (the same as the image). Returns ------- kernel : skimage.morphology.selem object Kernel to use with a skimage filter. """ # build the kernel if shape == "diamond": kernel = diamond(size, dtype=dtype) elif shape == "disk": kernel = disk(size, dtype=dtype) elif shape == "rectangle" and isinstance(size, tuple): kernel = rectangle(size[0], size[1], dtype=dtype) elif shape == "square": kernel = square(size, dtype=dtype) else: raise ValueError("Kernel definition is wrong.") return kernel
def get_centromere_neighbourhood(im, dilation_size=3, threshold=None, threshold_function=imfilter.threshold_otsu): """Obtain the locations near centromeres in an image. Parameters ---------- im : np.ndarray, shape (M, N) The input image, containing fluorescently-labelled centromeres. dilation_size : int (optional, default 3) Size in pixels of neighbourhood around actual centromere locations. threshold : float (optional, default None) Use this threshold instead of one computed by `threshold_function`. threshold_function : function, im -> int or im -> im (optional, default `skimage.filter.threshold_otsu`) Use this function to find a suitable threshold for the input image. Returns ------- centro : np.ndarray of bool, shape (M, N) The locations around centromeres marked as `True`. """ if threshold is None: threshold = threshold_function(im) centro = im > threshold strel = selem.disk(dilation_size) centro = nd.binary_dilation(centro, structure=strel) return centro
def find_valid_pixels(mDataTrain): # find valid pixels mThresh = 0.75 mm = mDataTrain > mThresh strel = selem.disk(5) mm = binary.binary_dilation(mm, strel) mm = morphology.remove_small_objects(mm, 1000, 4) pixValid = mm[mm > 0] return pixValid
def grabcut_binarization(bfly_rgb, bfly_bin): """Extract shape of the butterfly using OpenCV's grabcut. Greatly improves binarization of blue-colored butterflies. Arguments --------- bfly_rgb : (M, N, 3) ndarray Input RGB image of butterfly (ruler and tags cropped out) bfly_bin : (M, N) ndarray Binarizaed image of butterfly (ruler and tags cropped out) Expected to be binarized saturation channel of bfly_rgb. Returns ------- bfly_grabcut_bin : (M, N) ndarray Resulting binarized image of butterfly after segmentation by grabcut. """ # Dilation of image to capture butterfly region selem_arr = selem.disk(DILATION_SIZE) bfly_bin_dilated = binary_dilation(bfly_bin, selem_arr) bfly_bin_dilated_markers, _ = ndi.label( bfly_bin_dilated, ndi.generate_binary_structure(2, 1)) bfly_bin_dilated_regions = regionprops(bfly_bin_dilated_markers) bfly_bin_dilated_regions_sorted = sorted(bfly_bin_dilated_regions, key=lambda r: r.area, reverse=True) bfly_region = bfly_bin_dilated_regions_sorted[0] # Downscale image to improve grabcut speed bfly_rgb_rescale = rescale(bfly_rgb, GRABCUT_RESCALE_FACTOR) bfly_rgb_rescale = img_as_ubyte(bfly_rgb_rescale) # Determine grabcut highlight region using butterfly region (after rescaling) padding = 0 rect = (int(GRABCUT_RESCALE_FACTOR * bfly_region.bbox[1] - padding), int(GRABCUT_RESCALE_FACTOR * bfly_region.bbox[0] - padding), int(GRABCUT_RESCALE_FACTOR * bfly_region.bbox[3] + padding), int(GRABCUT_RESCALE_FACTOR * bfly_region.bbox[2] + padding)) # Grabcut mask = np.zeros(bfly_rgb_rescale.shape[:2], np.uint8) bgd_model = np.zeros((1, 65), np.float64) fgd_model = np.zeros((1, 65), np.float64) cv.grabCut(bfly_rgb_rescale, mask, rect, bgd_model, fgd_model, GRABCUT_ITERATIONS, cv.GC_INIT_WITH_RECT) mask2 = np.where((mask == 2) | (mask == 0), 0, 1).astype('uint8') bfly_grabcut_rescale = bfly_rgb_rescale * mask2[:, :, np.newaxis] # Rescale the image back up and get binary of result bfly_grabcut = rescale(bfly_grabcut_rescale, bfly_rgb.shape[0] / bfly_rgb_rescale.shape[0]) bfly_grabcut_bin = np.max(bfly_grabcut, axis=2) > 0 return bfly_grabcut_bin
def callback(self, image, info, jstate, seg_res_msg): # only process if moving slowly if np.linalg.norm(np.asarray(jstate.velocity)[2:9]) > 1e-1: rospy.logdebug("!!!To fast!!! {} | {}".format(np.linalg.norm(jstate.velocity), np.asarray(jstate.velocity)[2:9])) return cv_image = self.depth_scale*self.bridge.imgmsg_to_cv2(image, desired_encoding="32FC1").copy() old_shape = cv_image.shape cv_image = cv2.resize(cv_image, (240, 180), interpolation=cv2.INTER_NEAREST) # TODO: Define and use a central point where voxel size, max distance and viewfield are given as a rosparam and adapt along the image chain mask = np.bitwise_or(np.bitwise_and(self.value_to_replace-0.1 < cv_image, cv_image < self.value_to_replace+0.1), (cv_image < 0.1)) # get robot and invalid pixels mask = binary_dilation(mask, structure=disk(10)) # grow regions of robot pixels # mask = binary_dilation(mask, structure=disk(25)) # grow regions of robot pixels cv_image[mask] = np.nan # Replace extended robot pixels with nan # mask = cv_image < 0.1 # remove close pixels where filter fails # cv_image[mask] = np.nan # cv_rgb_image = self.bridge.imgmsg_to_cv2(rgb_image, desired_encoding="passthrough").copy() # cv_rgb_image = cv2.resize(cv_rgb_image, (240, 180), interpolation=cv2.INTER_NEAREST) cv_seg_image_cans = self.filter_seg_res(seg_res_msg, ["bottle", "cup"], np.asarray([255, 0, 0], dtype=np.uint8)) cv_seg_image_table = self.filter_seg_res(seg_res_msg, ["dining table"], np.asarray([0, 255, 0], dtype=np.uint8)) cv_seg_image = cv_seg_image_cans cv_seg_image[cv_seg_image_cans[:,:,0]==0] = cv_seg_image_table[cv_seg_image_cans[:,:,0]==0] # Table is in the background # Be more clever --> bigger objects must be farther in the background?! cv_seg_image = cv2.resize(cv_seg_image, (240, 180), interpolation=cv2.INTER_NEAREST) try: new_depth_img_msg = self.bridge.cv2_to_imgmsg(cv_image, "32FC1") new_depth_img_msg.header = image.header new_depth_img_msg.header.frame_id = "panda/panda_camera" new_seg_image_msg = self.bridge.cv2_to_imgmsg(cv_seg_image, "rgb8") new_seg_image_msg.header = seg_res_msg.header new_seg_image_msg.header.frame_id = "panda/panda_camera" new_info = deepcopy(info) new_info.header = info.header new_info.header.frame_id = "panda/panda_camera" new_info.height = 180 new_info.width = 240 # print("Old image size: {}, new: {}".format(old_shape, cv_image.shape)) new_info.K = np.asarray(new_info.K) new_info.K[0] = new_info.K[0] * 240.0/old_shape[1] # from https://answers.opencv.org/question/150551/how-does-resizing-an-image-affect-the-intrinsics/ new_info.K[4] = new_info.K[4] * 180.0/old_shape[0] new_info.K[2] = new_info.K[2] * 240.0/old_shape[1] new_info.K[5] = new_info.K[5] * 180.0/old_shape[0] new_info.P = np.asarray(new_info.P) new_info.P[0] = new_info.P[0] * 240.0/old_shape[1] new_info.P[2] = new_info.P[2] * 240.0/old_shape[1] new_info.P[5] = new_info.P[5] * 180.0/old_shape[0] new_info.P[6] = new_info.P[6] * 180.0/old_shape[0] self.image_pub.publish(new_depth_img_msg) rospy.logdebug("Image sent") self.rgb_image_pub.publish(new_seg_image_msg) rospy.logdebug("RGB Image sent") self.info_pub.publish(new_info) rospy.logdebug("Camera Info sent") except CvBridgeError as e: rospy.logerr(e)
def setUp(self): k = 5 arrname = '%03i' % k self.disk = selem.disk(k) fname_opening = os.path.join(data_dir, "disk-open-matlab-output.npz") self.expected_opening = np.load(fname_opening)[arrname] fname_closing = os.path.join(data_dir, "disk-close-matlab-output.npz") self.expected_closing = np.load(fname_closing)[arrname]
def create_mask(im_arr, erode=0): if im_arr.shape[2] == 3: im_arr = rgb2gray(im_arr) thresh = 0.05 inv_bin = np.invert(im_arr > thresh) all_labels = measure.label(inv_bin) # Select largest object and invert seg_arr = all_labels == 0 if erode > 0: strel = selem.disk(erode, dtype=np.bool) seg_arr = binary_erosion(seg_arr, selem=strel) elif erode < 0: strel = selem.disk(abs(erode), dtype=np.bool) seg_arr = binary_dilation(seg_arr, selem=strel) return seg_arr.astype(np.bool)
def generate_spot_background(spotmask, distance=3, annulus=5): """ compute an annulus around each spot to estimate background. Parameters ---------- spotmask : binary mask of spots distance : distance from the edge of segmented spot. annulus : width of the annulus Returns ------- spotbackgroundmask: binary mask of annuli around spots. TODO: 'comets' should be ignored, and this approach may not be robust to it. """ se_inner = selem.disk(distance, dtype=bool) se_outer = selem.disk(distance+annulus, dtype=bool) inner = binary_dilation(spotmask, se_inner) outer = binary_dilation(spotmask, se_outer) spot_background = np.bitwise_xor(inner, outer) return spot_background
def basic_pen_mask(image, pen_size_threshold, pen_mask_expansion): green_mask = np.bitwise_and( image[:, :, RGB_GREEN_CHANNEL] > image[:, :, RGB_GREEN_CHANNEL], image[:, :, RGB_GREEN_CHANNEL] - image[:, :, RGB_GREEN_CHANNEL] > MIN_COLOR_DIFFERENCE) blue_mask = np.bitwise_and( image[:, :, RGB_BLUE_CHANNEL] > image[:, :, RGB_GREEN_CHANNEL], image[:, :, RGB_BLUE_CHANNEL] - image[:, :, RGB_GREEN_CHANNEL] > MIN_COLOR_DIFFERENCE) masked_pen = np.bitwise_or(green_mask, blue_mask) new_mask_image = remove_small_objects(masked_pen, pen_size_threshold) return maximum(np.where(new_mask_image, 1, 0), disk(pen_mask_expansion)).astype(bool)
def features_opening_aubin(opening_sizes, rna_coord, mask_cyt): # get number of rna nb_rna = len(rna_coord) # apply opening operator and count the loss of rna features = [] for size in opening_sizes: s = disk(size, dtype=bool) mask_cyt_transformed = binary_opening(mask_cyt, selem=s) mask_rna = mask_cyt_transformed[rna_coord[:, 1], rna_coord[:, 2]] rna_after_opening = rna_coord[mask_rna] nb_rna_after_opening = len(rna_after_opening) diff_opening = (nb_rna - nb_rna_after_opening) / nb_rna features.append(diff_opening) return features
def create_mask(im_arr, erode=3): if im_arr.shape[2] == 3: im_arr = rgb2gray(im_arr) thresh = threshold_otsu(im_arr) inv_bin = np.invert(im_arr > thresh) all_labels = measure.label(inv_bin) # Select largest object and invert seg_arr = np.invert(all_labels == 1) if erode: strel = selem.disk(erode, dtype=np.bool) seg_arr = binary_erosion(seg_arr, selem=strel) return seg_arr.astype(np.bool)
def features_protrusion(rna_coord_out, mask_cyt, mask_nuc, mask_cyt_out): # get number of rna outside nucleus and cell area nb_rna_out = len(rna_coord_out) area_nuc = mask_nuc.sum() area_cyt_out = mask_cyt_out.sum() eps = stack.get_eps_float32() # case where we do not detect any rna outside the nucleus if nb_rna_out == 0: features = [0., np.log2(eps), 0.] return features # apply opening operator and count the loss of rna outside the nucleus features = [] for size in [30]: s = disk(size, dtype=bool) mask_cyt_transformed = binary_opening(mask_cyt, selem=s) mask_cyt_transformed[mask_nuc] = True new_area_cell_out = mask_cyt_transformed.sum() - area_nuc area_protrusion = area_cyt_out - new_area_cell_out if area_protrusion > 0: factor = nb_rna_out * area_protrusion / area_cyt_out mask_rna = mask_cyt_transformed[rna_coord_out[:, 1], rna_coord_out[:, 2]] rna_after_opening = rna_coord_out[mask_rna] nb_rna_protrusion = nb_rna_out - len(rna_after_opening) index_rna_opening = (nb_rna_protrusion + eps) / factor log2_index_rna_opening = np.log2(index_rna_opening) proportion_rna_opening = nb_rna_protrusion / nb_rna_out features += [ index_rna_opening, log2_index_rna_opening, proportion_rna_opening ] else: features += [0., np.log2(eps), 0.] return features
def generate_circle_map(game, sprites_group, rows, columns): """Map with circular sea in the center.""" ones = np.ones((rows, columns), dtype=np.uint8) ones[int(rows / 2), int(columns / 2)] = 0 # 0 pixel in the middle se = selem.disk(int(min([rows, columns]) / 2)) # radius of se R = 1/3 of min(row,col) ones = erosion(ones, selem=se) # expand (erode) the central 0 pixel to R land_bool_map = ones > 0 mountains_bool_map = MapGenerator.generate_mountains(rows, columns) coast_bool_map = feature.canny(255 * np.array(land_bool_map, dtype=np.uint8)) # make mountains only on land: mountains_bool_map = np.logical_and(mountains_bool_map, land_bool_map) number_map = np.zeros((rows, columns), dtype=np.uint8) number_map[land_bool_map] = TILE_TYPE_DICT['land'] number_map[coast_bool_map] = TILE_TYPE_DICT['sand'] number_map[mountains_bool_map] = TILE_TYPE_DICT['mountain'] return MapGenerator.generate_tiles(game, sprites_group, number_map)
def _define_kernel(shape, size, dtype): """Build a kernel to apply a filter on images. Parameters ---------- shape : str Shape of the kernel used to compute the filter (`diamond`, `disk`, `rectangle` or `square`). size : int, Tuple(int) or List(int) The size of the kernel: - For the rectangle we expect two values (`height`, `width`). - For the square one value (`width`). - For the disk and the diamond one value (`radius`). dtype : type Dtype used for the kernel (the same as the image). Returns ------- kernel : skimage.morphology.selem object Kernel to use with a skimage filter. """ # build the kernel if shape == "diamond": kernel = diamond(size, dtype=dtype) elif shape == "disk": kernel = disk(size, dtype=dtype) elif shape == "rectangle" and isinstance(size, (tuple, list)): kernel = rectangle(size[0], size[1], dtype=dtype) elif shape == "square": kernel = square(size, dtype=dtype) else: raise ValueError("Kernel definition is wrong. Shape of the kernel " "should be 'diamond', 'disk', 'rectangle' or " "'square'. Not {0}.".format(shape)) return kernel
def get_chromatin(im, background_diameter=51, opening_size=2, opening_iter=2, size_filter=256): """Find the chromatin in an unevenly illuminated image. Parameters ---------- im : np.ndarray, shape (M, N) The chromatin grayscale image. background_diameter : int, optional The diameter of the block size in which to find the background. (This is used by the scikit-image function `threshold_adaptive`.) (default: 51) opening_size : int, optional Perform a binary opening with a disk of this radius. (default: 2) opening_iter : int, optional Perform this many opening iterations. (default: 2) size_filter : int, optional After the morphological opening, filter out segments smaller than this size. (default: 256) Returns ------- chrs : np.ndarray, shape (M, N) A thresholded image of the chromatin regions. """ if im.ndim == 3 and im.shape[2] == 3: im = im[..., 0] fg = imfilter.threshold_adaptive(im, background_diameter) # on an unevenly lit image, `fg` will have all sorts of muck lying around, # in addition to the chromatin. Thankfully, the muck is noisy and full of # holes, whereas the chromatin is solid. An opening followed by a size # filtering removes it quite effectively. strel = selem.disk(opening_size) fg_open = nd.binary_opening(fg, strel, iterations=opening_iter) chrs = remove_small_objects(fg_open, size_filter) return chrs
def segment(self, img, plot=False): # print("Segmenting shape ", np.asarray(img).shape) new_mask = np.zeros(shape=color.rgb2ycbcr(img).shape) """ ##### HISTOGRAM BASED ####################################### img_transformed = color.rgb2ycbcr(img) new_mask[(img_transformed[:,:,1] > self.LOWER_THRESHOLD_R) & (img_transformed[:,:,1] < self.UPPER_THRESHOLD_R) & (img_transformed[:,:,2] > self.LOWER_THRESHOLD_B) &(img_transformed[:,:,2] < self.UPPER_THRESHOLD_B)] = 1 ##### UNCOMMENT THIS IF YOU WANT TO CHANGE THE STRATEGY ##### img_RGB = img.copy() img_HSV = color.rgb2hsv(img) new_mask[(img_HSV[:,:,0] >= 0) & (img_HSV[:,:,0] <= 50) & (img_HSV[:,:,1] >= 0.23) & (img_HSV[:,:,1] <= 0.68) & (img_RGB[:,:,0] > 95) & (img_RGB[:,:,1] > 40) & (img_RGB[:,:,2] > 20) & (img_RGB[:,:,0] > img_RGB[:,:,1]) & (img_RGB[:,:,0] > img_RGB[:,:,2]) & (np.abs((img_RGB[:,:,0] - img_RGB[:,:,1])) > 15)] = 1 img_RGB = img.copy() img_YCbCr = color.rgb2ycbcr(img) new_mask[(img_RGB[:,:,0] > 95) & (img_RGB[:,:,1] > 40) & (img_RGB[:,:,2] > 20) & (img_RGB[:,:,0] > img_RGB[:,:,1]) & (img_RGB[:,:,0] > img_RGB[:,:,2]) & (img_YCbCr[:,:,1] > 85) & (img_YCbCr[:,:,0] > 80) & (img_YCbCr[:,:,2] <= ((1.5862 * img_YCbCr[:,:,1]) + 20)) & (img_YCbCr[:,:,2] >= ((0.3448 * img_YCbCr[:,:,1]) + 76.2069)) & (img_YCbCr[:,:,2] >= ((-4.5652 * img_YCbCr[:,:,1]) + 234.5652)) & (img_YCbCr[:,:,2] <= ((-1.15 * img_YCbCr[:,:,1]) + 301.75)) & (img_YCbCr[:,:,2] <= ((-2.2857 * img_YCbCr[:,:,1]) + 432.85))] = 1 """ """ img_RGB = img.copy() img_LAB = color.rgb2lab(img) new_mask[ #(img_RGB[:,:,0] > 95) & #(img_LAB[:,:,0] >= 70) & #(img_LAB[:,:,0] <= 90) & (img_LAB[:,:,1] >= 9.5) & (img_LAB[:,:,1] <= 25) & (np.abs((img_LAB[:,:,1] - img_LAB[:,:,2])) < 10) #(img_LAB[:,:,2] >= 25) ] = 1 """ img_RGB = img.copy() img_LAB = color.rgb2lab(img) img_YCbCr = color.rgb2ycbcr(img) img_HSV = color.rgb2hsv(img) new_mask[ # YCbCr (img_YCbCr[:, :, 1] >= 77) & (img_YCbCr[:, :, 1] <= 127) & (img_YCbCr[:, :, 2] >= 133) & (img_YCbCr[:, :, 2] <= 173) & # RGB (img_RGB[:, :, 0] > 70) & (img_RGB[:, :, 1] > 40) & (img_RGB[:, :, 2] > 20) & (img_RGB[:, :, 0] > img_RGB[:, :, 1]) & (img_RGB[:, :, 0] > img_RGB[:, :, 2]) & #(np.abs((img_RGB[:,:,0] - img_RGB[:,:,1])) <= 15) & # HSV (img_HSV[:, :, 0] > 0.0275) & ## (img_HSV[:, :, 0] < 20) & (img_HSV[:, :, 1] > 0.20) & (img_HSV[:, :, 1] < 70) & ## (img_HSV[:, :, 2] >= 0) & ## # LAB (img_LAB[:, :, 1] < 60) & (img_LAB[:, :, 1] > 0) & (img_LAB[:, :, 2] > 0.99) & # PERSONAL ((img_LAB[:, :, 1] - (0.009 * img_LAB[:, :, 2]**2)) >= 0) & (np.abs( (img_LAB[:, :, 1] - img_LAB[:, :, 2])) < 12)] = 1 if self.dilate: structuring_elem = selem.disk(self.dilation_size) new_mask_dilated = dilation(color.rgb2gray(new_mask), structuring_elem) if plot: fig, ax = plt.subplots(1, 3, figsize=(10, 5)) ax[0].imshow(img) ax[1].imshow(new_mask) ax[2].imshow(new_mask_dilated, cmap=plt.get_cmap('gray')) plt.show() return color.rgb2gray(new_mask_dilated) if plot: fig, ax = plt.subplots(1, 2, figsize=(10, 5)) ax[0].imshow(img) ax[1].imshow(new_mask) plt.show() return color.rgb2gray(new_mask)
from skimage.feature import local_binary_pattern, greycomatrix, greycoprops from scipy.spatial import distance from sklearn.cluster import KMeans, MiniBatchKMeans from sklearn import metrics origin_dir = "./img/" dest_dir = "./results/" for file in list(os.scandir(origin_dir))[2:3]: input_img = img_as_float(imread(file.path)) #ESPAÇOS DE CORES E MEDIANA img = equalize_hist(input_img)[:, :, 0] # enhance Contrast img2 = equalize_hist(input_img)[:, :, 1] # enhance Contrast img_hsvS = rgb2hsv(input_img)[:, :, 1] img_median = median(img, selem=disk(7)) # SUPERPIXEL imgTeste = img_as_ubyte(input_img) segments_slic = slic(imgTeste, n_segments=4000, compactness=10, sigma=1) sp = mark_boundaries(imgTeste, segments_slic) figure("super") imshow(sp) # image_label_overlay = label2rgb(segments_slic, image=input_img) # figure("uper") # imshow(image_label_overlay) region = regionprops(segments_slic, intensity_image=None, cache=True) lista = []
def features_protrusion(rna_coord, cell_mask, nuc_mask, ndim, voxel_size_yx, check_input=True): """Compute protrusion related features. Parameters ---------- rna_coord : np.ndarray, np.int64 Coordinates of the detected RNAs with zyx or yx coordinates in the first 3 or 2 columns. cell_mask : np.ndarray, bool Surface of the cell with shape (y, x). nuc_mask : np.ndarray, bool Surface of the nucleus with shape (y, x). ndim : int Number of spatial dimensions to consider. voxel_size_yx : int or float Size of a voxel on the yx plan, in nanometer. check_input : bool Check input validity. Returns ------- index_rna_protrusion : float Number of RNAs detected in a protrusion and normalized by the expected number of RNAs under random distribution. proportion_rna_protrusion : float Proportion of RNAs detected in a protrusion. protrusion_area : float Protrusion area (in pixels). """ # TODO fin a better feature for the protrusion (idea: dilate region from # centroid and stop when a majority of new pixels do not belong to the # cell). # check parameters stack.check_parameter(check_input=bool) if check_input: stack.check_parameter(ndim=int, voxel_size_yx=(int, float)) if ndim not in [2, 3]: raise ValueError("'ndim' should be 2 or 3, not {0}.".format(ndim)) stack.check_array(rna_coord, ndim=2, dtype=np.int64) stack.check_array(cell_mask, ndim=2, dtype=bool) stack.check_array(nuc_mask, ndim=2, dtype=bool) # get number of rna and cell area nb_rna = len(rna_coord) cell_area = cell_mask.sum() # apply opening operator (3000 nanometers) and count the loss of RNAs size = int(3000 / voxel_size_yx) s = disk(size, dtype=bool) mask_cell_opened = binary_opening(cell_mask, selem=s) mask_cell_opened[nuc_mask] = True protrusion_area = cell_area - mask_cell_opened.sum() # case where we do not detect any if nb_rna == 0: features = (1., 0., protrusion_area) return features if protrusion_area > 0: expected_rna_protrusion = nb_rna * protrusion_area / cell_area mask_rna = mask_cell_opened[rna_coord[:, ndim - 2], rna_coord[:, ndim - 1]] rna_after_opening = rna_coord[mask_rna] nb_rna_protrusion = nb_rna - len(rna_after_opening) index_rna_protrusion = nb_rna_protrusion / expected_rna_protrusion proportion_rna_protrusion = nb_rna_protrusion / nb_rna features = (index_rna_protrusion, proportion_rna_protrusion, protrusion_area) else: features = (1., 0., 0.) return features
.. [1] http://en.wikipedia.org/wiki/Otsu's_method """ import matplotlib.pyplot as plt from skimage import data from skimage.morphology.selem import disk import skimage.filter.rank as rank from skimage.filter import threshold_otsu p8 = data.page() radius = 10 selem = disk(radius) loc_otsu = rank.otsu(p8, selem) t_glob_otsu = threshold_otsu(p8) glob_otsu = p8 >= t_glob_otsu plt.figure() plt.subplot(2, 2, 1) plt.imshow(p8, cmap=plt.cm.gray) plt.xlabel('original') plt.colorbar() plt.subplot(2, 2, 2) plt.imshow(loc_otsu, cmap=plt.cm.gray) plt.xlabel('local Otsu ($radius=%d$)' % radius) plt.colorbar()
def nuclei_seg(img, threshold=None ): # refine large blobs with regional thresholding ''' implementation of the H&E stained histology image nuclei segmentation method described in [Peikari et al., 2016] and [Peikari et al., 2017] :param img: np.array, [MxNx3] uint8, input RGB image :param threshold: array-like, [2] float, pre-computed thresholds :return: final_mask: np.array, [MxNx3] int16, segmentation mask th_1, th_2: float, thresholds references: [1] Peikari, M., Salama, S., Nofech‐Mozes, S., & Martel, A. L. (2017). Automatic cellularity assessment from post‐treated breast surgical specimens. Cytometry Part A, 91(11), 1078-1087. [2] Peikari, M., & Martel, A. L. (2016, March). Automatic cell detection and segmentation from H and E stained pathology slides using colorspace decorrelation stretching. In Medical Imaging 2016: Digital Pathology (Vol. 9791, p. 979114). International Society for Optics and Photonics. ''' im_nmzd = htk.preprocessing.color_normalization.reinhard( img, mean_ref, std_ref) img_ds = decorrstretch(im_nmzd) img_lab = htk.preprocessing.color_conversion.rgb_to_lab(img_ds) img_lab = 0.25 * img_lab[:, :, 0] + 0.5 * img_lab[:, :, 1] + 0.25 * img_lab[:, :, 2] if threshold is None: th = eng.multithresh(matlab.double(img_lab.tolist()), 2) th_1 = np.sort(th)[0][0] th_2 = np.sort(th)[0][1] else: th_1 = threshold[0] th_2 = threshold[1] mask = (img_lab < th_1) mask = binary_fill_holes(mask) mask = binary_opening(mask, selem.disk(3)) # correct for mis-filled holes mask_holes = (mask & (img_lab > th_2)) mask_holes = (htk.segmentation.label.area_open( label(mask_holes, connectivity=1), 40) > 0) mask_holes = (binary_dilation(mask_holes, selem.disk(2))) mask[mask_holes] = False mask_conn = label(mask, connectivity=1) # refine large blobs area = np.array([[_.label, _.area] for _ in regionprops(mask_conn)]) overlap_label = area[area[:, 1] > 300, 0] overlap_mask = np.isin(mask_conn, overlap_label) dt = distance_transform_edt(overlap_mask) local_maxi = peak_local_max(dt, labels=mask_conn, min_distance=4, indices=False) markers = label(local_maxi) overlap_seg = watershed(-dt, markers, mask=overlap_mask) mask_conn[overlap_seg > 0] = overlap_seg[overlap_seg > 0] + mask_conn.max() final_mask = htk.segmentation.label.condense(mask_conn) # refine large blobs by regional threshold area = np.array([[ _.label, _.area, ] for _ in regionprops(final_mask)]) overlap_label = area[area[:, 1] > 300, 0] overlap_mask = np.isin(final_mask, overlap_label) final_mask[overlap_mask] = 0 tmp_th = eng.multithresh(matlab.double([img_lab[overlap_mask].tolist()]), 2) th_3 = np.sort(tmp_th)[0][1] overlap_mask = overlap_mask & (img_lab < th_3) overlap_mask = binary_opening(overlap_mask, selem.disk(3)) dt = distance_transform_edt(overlap_mask) local_maxi = peak_local_max(dt, labels=overlap_mask, min_distance=4, indices=False) markers = label(local_maxi) overlap_seg = watershed(-dt, markers, mask=overlap_mask) final_mask[ overlap_seg > 0] = overlap_seg[overlap_seg > 0] + final_mask.max() # remove boundary regions for _1 in regionprops(final_mask): min_row, min_col, max_row, max_col = _1.bbox if min_row <= 0 or min_col <= 0 or max_row >= img.shape[ 0] or max_col >= img.shape[1]: final_mask[final_mask == _1.label] = 0 final_mask = htk.segmentation.label.condense(final_mask) final_mask = _label_fill_holes(final_mask) final_mask = htk.segmentation.label.area_open(final_mask, 40).astype('int16') return final_mask, th_1, th_2
def remove_segmented_nuc(image, nuc_mask, size_nuclei=2000): """Remove the nuclei we have already segmented in an image. 1) We start from the segmented nuclei with a light dilation. The missed nuclei and the background are set to 0 and removed from the original image. 2) We reconstruct the missing nuclei by small dilation. As we used the original image to set the maximum allowed value at each pixel, the background pixels remain unchanged. However, pixels from the missing nuclei are partially reconstructed by the dilation. The reconstructed image only differs from the original one where the nuclei have been missed. 3) We subtract the reconstructed image from the original one. 4) From the few missing nuclei kept and restored, we build a binary mask (dilation, small object removal). 5) We apply this mask to the original image to get the original pixel intensity of the missing nuclei. 6) We remove pixels with a too low intensity. Parameters ---------- image : np.ndarray, np.uint Original nuclei image with shape (y, x). nuc_mask : np.ndarray, Result of the segmentation (with instance differentiation or not). size_nuclei : int Threshold above which we detect a nuclei. Returns ------- image_without_nuc : np.ndarray Image with shape (y, x) and the same dtype of the original image. Nuclei previously detected in the mask are removed. """ # check parameters stack.check_array(image, ndim=2, dtype=[np.uint8, np.uint16]) stack.check_array(nuc_mask, ndim=2, dtype=bool) stack.check_parameter(size_nuclei=int) # store original dtype original_dtype = image.dtype # dilate the mask mask_dilated = stack.dilation_filter(image, "disk", 10) # remove the unsegmented nuclei from the original image diff = image.copy() diff[mask_dilated == 0] = 0 # reconstruct the missing nuclei by dilation s = disk(1).astype(original_dtype) image_reconstructed = reconstruction(diff, image, selem=s) image_reconstructed = image_reconstructed.astype(original_dtype) # substract the reconstructed image from the original one image_filtered = image.copy() image_filtered -= image_reconstructed # build the binary mask for the missing nuclei missing_mask = image_filtered > 0 missing_mask = clean_segmentation(missing_mask, small_object_size=size_nuclei, fill_holes=True) missing_mask = stack.dilation_filter(missing_mask, "disk", 20) # TODO improve the thresholds # get the original pixel intensity of the unsegmented nuclei unsegmented_nuclei = image.copy() unsegmented_nuclei[missing_mask == 0] = 0 if original_dtype == np.uint8: unsegmented_nuclei[unsegmented_nuclei < 40] = 0 else: unsegmented_nuclei[unsegmented_nuclei < 10000] = 0 return unsegmented_nuclei
def toy_voronoi_labels_affinities( width_, height_, radius, opening_radius, deform_sigma, deform_points, intensity_prob, noise_sigma ): # we will create bigger images such that we can crop out regions, # that look good offset = 2 * radius height = height_ + 2 * offset width = width_ + 2 * offset shape = (height, width) labels = np.zeros(shape, dtype=np.uint16) sketch = np.zeros_like(labels, dtype=float) # r is the distance between two center points, so we need to multiply by 2 centers = poisson_disc_samples(width=width, height=height, r=radius * 2) # append far points to centers to complete voronoi regions x_max = width + 1 y_max = height + 1 centers = centers + [(-1, -1), (-1, y_max), (x_max, -1), (x_max, y_max)] vor = Voronoi(centers) # create selem with provided radius to apply clsoing selem = disk(opening_radius) for i, region in enumerate(vor.regions): if -1 in region or len(region) == 0: continue polygon = [vor.vertices[i] for i in region] mask = polygon2mask(shape, polygon) # close polygon mask with provided selem and radius mask = binary_opening(mask, selem) # enumerate starts at 0, but 0 is background labels[mask] = i + 1 edt = distance_transform_edt(mask) edt = edt / edt.max() tmp_intensity = np.random.uniform(*intensity_prob) sketch[mask] = edt[mask] * tmp_intensity sketch = scipy.ndimage.gaussian_filter(sketch, radius / 4) [labels, sketch] = elasticdeform.deform_random_grid( [labels, sketch], sigma=deform_sigma, points=deform_points, # labels must be interpolated by nearest neighbor order=[0, 3], crop=( slice(offset, offset + height_ + 1), slice(offset, offset + width_ + 1) ) ) # labels = labels[offset:-offset + 1, offset:-offset + 1] # sketch = sketch[offset:-offset + 1, offset:-offset + 1] noise = sketch + np.random.normal(0, noise_sigma, size=sketch.shape) affinities = get_affinities(labels) return labels[:-1, :-1], sketch[:-1, :-1], noise[:-1, :-1], affinities
'segmentation&classification/corr/y_pred_corr_with_label.npy') # estimation for val clf_map_val = [] malignant_map_val = [] for i, j in zip(val_seg, y_pred_val_with_label): tmp_map = np.zeros(i.shape + (3, ), dtype='uint8') tmp_map[np.isin(i, j[j[:, 3] == 0, 2])] = [255, 0, 0] tmp_map[np.isin(i, j[j[:, 3] == 1, 2])] = [0, 0, 255] tmp_map[np.isin(i, j[j[:, 3] == 2, 2])] = [0, 255, 0] clf_map_val.append(tmp_map) malignant_map_val.append(np.isin(i, j[j[:, 3] == 0, 2])) clf_map_val = np.array(clf_map_val) malignant_map_val = np.array(malignant_map_val) malignant_map_val = np.array( [binary_dilation(j, selem.disk(11)) for j in malignant_map_val]) cellularity_val = np.array( [np.sum(j) / (j.shape[0] * j.shape[1]) for j in malignant_map_val]) if not os.path.exists('segmentation&classification/val/mapping'): os.mkdir('segmentation&classification/val/mapping') for i, j in enumerate(clf_map_val): skimage.io.imsave( 'segmentation&classification/val/mapping/{:03d}.tif'.format(i), j) # estimation for corr clf_map_corr = [] malignant_map_corr = [] for i, j in zip(corr_seg, y_pred_corr_with_label): tmp_map = np.zeros(i.shape + (3, ), dtype='uint8') tmp_map[np.isin(i, j[j[:, 3] == 0, 2])] = [255, 0, 0] tmp_map[np.isin(i, j[j[:, 3] == 1, 2])] = [0, 0, 255]
ndvi = sentinel2_data_cube.ndvi() scl = connection.load_collection("TERRASCOPE_S2_TOC_V2", bands=["SCL"]).filter_bbox(**bbox) classification = scl.band("SCL") #in openEO, 1 means mask (remove pixel) 0 means keep pixel SCL_MASK_VALUES = [0, 1, 3, 8, 9, 10, 11] #keep useful pixels, so set to 1 (remove) if smaller than threshold #first erode, then dilate: removes noise, in this case small areas marked as cloud? scl_mask = ~ ((classification == 0) | (classification == 1) | (classification == 3) | (classification == 8) | (classification == 9) | (classification == 10) | (classification == 11)) #scl_mask = ((classification == 3) | (classification == 8) | (classification == 9) ) #this is erosion: clouds (1's) get smaller scl_mask = scl_mask.apply_kernel(selem.disk(13)) #output of 2D convolution is non-binary, make it binary again, AND invert scl_mask = scl_mask < 0.01 #now do dilation scl_mask = scl_mask.apply_kernel(selem.disk(13)) scl_mask = scl_mask.add_dimension(name="bands",label="scl",type="bands") def test_mask(): scl_mask.filter_temporal("2018-05-06","2018-05-06").download("mask.tiff",format="GTIFF") masked = ndvi.mask(scl_mask) #I'm using pytest methods here: PyCharm allows these methods to be started individually, which is more convenient # otherwise my script would be doing multiple downloads on each test, or I would have to comment out these lines #takes a few seconds to download
tocc = 1.0 * (occgrid > 0.5) occgrid[occgrid > 0.5] = 0 print("Map loaded") else: print('Map not found') exit() plt.figure(1) plt.imshow(tocc + occgrid) plt.show() # Object Dilation dilation = 4.5 zmap = np.uint8(tocc > 0.5) disk = selem.disk(dilation) zmapd = binary_dilation(input=zmap, structure=disk) plt.figure(2) plt.imshow(zmapd) plt.show() # Get the rute origen = (8, 8) num1 = random.randint(30, 90) num2 = random.randint(30, 90) meta = (num1, num2) #if zmapd[meta[0]][meta[1]] != 0: # continue
def morph_grad(img): gr = gradient(img, disk(1)) return gr.astype(np.float32) / 256
def Volume_Label(): """ Creates binary labels from .stl files, that are sliced at height locations given by the position of the slices in the corresponding volume.nii file. It Saves at "filenamelabel" the binary labels of the selected anatomy. Returns: """ setDirVariables() ## Options # b_display = 0 # compare_Matlab_Python = 0 # Output size outSize = [128, 128, 128] templateSize = str(outSize[0]) + '_' + str(outSize[1]) + '_' + str( outSize[2]) # Sets InStlSet = 'stl' InDCMmSet = 'reshape_knee_' InDCMmSetdicom = 'knee' fileTemplate = InDCMmSet + templateSize anatomy = 'tibia' # 'femur', 'patella', 'fibula' position = 'prox' # 'dist', '', '' InSurfSet = 'ct_' + anatomy + '_' outSet = position + anatomy os.chdir(mainInputDataDirectoryLoc) fp = open('case.txt', 'r+') casePatient = fp.read() casePatient = int(casePatient) fp.close() print('Patient no. {}'.format(casePatient)) # Read excel file to get patients' codes xlsName = os.path.join(mainInputDataDirectoryLoc, 'Case_statistics.xlsx') # name = pandas.ExcelFile(xlsName) name = xlrd.open_workbook(xlsName) sheet = name.sheet_by_index(0) rows = sheet.nrows study = [sheet.cell_value(i, 0) for i in range(1, rows)] patientCode = study[casePatient - 1] ## Read volume nii mainPatientDirectory = 'Patient{:04d}'.format(casePatient) mainInputPatientDirectoryLoc = mainInputDataDirectoryLoc + '/preprocessedData/' + mainPatientDirectory + '/' mainInputPatientDirectoryNAS = mainInputDataDirectoryNAS + '/OriginalData/' + patientCode mainInputDicomDirectory = mainInputPatientDirectoryNAS + '/dicom/' if os.path.isdir(mainInputDicomDirectory + '/ct/'): mainInputDicomDirectory = mainInputDicomDirectory + '/ct/' + InDCMmSetdicom + '/' else: mainInputDicomDirectory = mainInputDicomDirectory + InDCMmSetdicom + '/' os.chdir(mainInputPatientDirectoryLoc) niiFilename = 'volumeCT_' + fileTemplate + '_{:04d}.nii'.format( casePatient) VolumeCT = loadNiiVolume(niiFilename, mainInputDicomDirectory) ## Normalize VolumeCT = normVolumeScan(VolumeCT) # Read stl and display mainInputStlDirectory = mainInputPatientDirectoryNAS + '/' + InStlSet + '/' os.chdir(mainInputStlDirectory) filename = InSurfSet + study[casePatient - 1] + '.stl' my_mesh1 = trimesh.load(filename) os.chdir(mainCodeDirectory) # Build binary volume of the reference surface corresponding to the CT volume VolumeSurf = VolumeCT VolumeSurf.volumeData = np.zeros(VolumeSurf.volumeData.shape, dtype=int) heights = [] for i in range(VolumeSurf.volumeDim[2]): heights.append( float(VolumeSurf.volumeOffset[2]) + i * VolumeSurf.voxelSize[2]) contours = mesh2vol(my_mesh1, heights, VolumeSurf.volumeOffset, VolumeSurf.voxelSize, VolumeSurf.volumeDim) indicesX = [] indicesY = [] for ip in range(VolumeSurf.volumeDim[2]): if contours[ip].shape[0] != 0: val = contours[ip][:, 0] - VolumeSurf.volumeOffset[0] val = val / (VolumeSurf.volumeDim[0] * VolumeSurf.voxelSize[0]) val = np.round(val * VolumeSurf.volumeDim[0], 0) valX = val.astype(int) val = contours[ip][:, 1] - VolumeSurf.volumeOffset[1] val = val / (VolumeSurf.volumeDim[1] * VolumeSurf.voxelSize[1]) val = np.round(val * VolumeSurf.volumeDim[0], 0) valY = val.astype(int) val_index = np.zeros((valX.shape[0], 2)) val_index[:, 0] = valX val_index[:, 1] = valY val_index = np.unique(val_index, axis=0).astype(int) indicesX.append(val_index[:, 0]) indicesY.append(val_index[:, 1]) for i, j in zip(valY, valX): VolumeSurf.volumeData[i - 1, j - 1, ip] = 1 else: indicesX.append([]) indicesY.append([]) VolumeSurfLabeled = VolumeSurf counter1 = 0 counter2 = 0 # fill in the image each contour for ip in range(VolumeSurfLabeled.volumeDim[2]): if contours[ip].shape[0] != 0: non_zero_start = np.count_nonzero(VolumeSurf.volumeData[:, :, ip]) ######## REGION FILL binaryImage = binary_fill_holes(VolumeSurf.volumeData[:, :, ip]) binaryImage = binaryImage > 1 / 255 ######### CLOSING kernel = np.ones((5, 5), np.uint8) binaryImage = binary_closing(binaryImage, kernel) ######### FILL HOLES AGAIN binaryImage = binary_fill_holes(binaryImage) non_zero_end = np.count_nonzero(binaryImage) ######### ALTERNATIVE PROCESSING FOR NON CLOSED CONTOURS if non_zero_end < non_zero_start * 4: strel = disk(2) binaryImage = binary_dilation(VolumeSurf.volumeData[:, :, ip], strel) binaryImage = binary_dilation(binaryImage, strel) binaryImage = binary_fill_holes(binaryImage) binaryImage = binary_erosion(binaryImage, strel) binaryImage = binary_erosion(binaryImage, strel) counter1 = counter1 + 1 non_zero_end2 = np.count_nonzero(binaryImage) ######### ALTERNATIVE PROCESSING FOR STILL-NON-CLOSED CONTOURS if non_zero_end2 < non_zero_start * 4: strel = disk(3) binaryImage = binary_dilation( VolumeSurf.volumeData[:, :, ip], strel) binaryImage = binary_dilation(binaryImage, strel) binaryImage = binary_fill_holes(binaryImage) binaryImage = binary_erosion(binaryImage, strel) binaryImage = binary_erosion(binaryImage, strel) counter2 = counter2 + 1 VolumeSurfLabeled.volumeData[:, :, ip] = binaryImage dMin = VolumeSurfLabeled.volumeData.min() D = VolumeSurfLabeled.volumeData + abs(dMin) D = D / D.max() * 255 print('Alternative processing no 1: {} \n Alternative proessing no 2: {}'. format(counter1, counter2)) ###### PLOT AND SCROLL ACROSS SLICES #if b_display == 1: # fig, ax = plt.subplots(1, 1) # tracker = IndexTracker(ax, D) # fig.canvas.mpl_connect('scroll_event', tracker.onscroll) # plt.show() # #if compare_Matlab_Python == 1: # name_dir = outSet + 'Label' # mainLabelDirectory = os.path.join(mainPatientDirectory, '{}'.format(name_dir)) # os.chdir(mainLabelDirectory) # mean_dice = dice_coeff(VolumeSurfLabeled.volumeData, outSet) # print(mean_dice) # Make nii file label volumeData = VolumeSurfLabeled.volumeData.astype(np.short) volumeData = np.transpose(volumeData, [1, 0, 2]) voxelSize = VolumeSurfLabeled.voxelSize volumeOffset = VolumeSurfLabeled.volumeOffset affine = np.eye(4) niiVolumeLabel = nib.Nifti1Image(volumeData, affine) niiVolumeLabel.header.set_slope_inter(VolumeSurfLabeled.rescaleSlope, VolumeSurfLabeled.rescaleIntercept) niiVolumeLabel.header.set_qform(affine, 1) niiVolumeLabel.header.set_zooms(voxelSize) niiVolumeLabel.header['qoffset_x'] = volumeOffset[0] niiVolumeLabel.header['qoffset_y'] = volumeOffset[1] niiVolumeLabel.header['qoffset_z'] = volumeOffset[2] os.chdir(mainInputPatientDirectoryLoc) # Save nii filenameLabel = 'volumeLabel_' + outSet + '_' + templateSize + '_{:04d}_py.nii'.format( casePatient) nib.nifti1.save(niiVolumeLabel, filenameLabel) os.chdir(mainCodeDirectory)
def features_foci(rna_coord_out, distance_cyt, distance_nuc, mask_cyt_out): # case where no mRNAs outside the nucleus are detected if len(rna_coord_out) == 0: features = [0.] * 35 * 2 features += [1., 0., 0.] * 4 features += [1., 0., 1., 0., 1., 0.] features += [1., 0., 1., 0., 1., 0.] return features features = [] for foci_radius in [50, 150, 250, 350, 450, 550, 650]: for min_foci_rna in [3, 4, 5, 6, 7]: clustered_spots = detection.cluster_spots( spots=rna_coord_out[:, :3], resolution_z=300, resolution_yx=103, radius=foci_radius, nb_min_spots=min_foci_rna) foci = detection.extract_foci(clustered_spots=clustered_spots) nb_foci = len(foci) nb_spots_in_foci = np.sum(foci[:, 3]) proportion_rna_foci = nb_spots_in_foci / len(rna_coord_out) features += [nb_foci, proportion_rna_foci] # case where no default foci are detected rna_coord_out_foci = rna_coord_out[rna_coord_out[:, 3] != -1, :] if len(rna_coord_out_foci) == 0: features += [1., 0., 0.] * 4 features += [1., 0., 1., 0., 1., 0.] features += [1., 0., 1., 0., 1., 0.] return features # get regular foci id l_id_foci = list(set(rna_coord_out_foci[:, 3])) # count mRNAs in successive 5 pixels foci neighbors nb_rna_out = len(rna_coord_out) cell_out_area = mask_cyt_out.sum() mask_foci_neighbor_cumulated = np.zeros_like(mask_cyt_out) eps = stack.get_eps_float32() # we count mRNAs in the neighbors 0-5 pixels around the foci, 5-10 pixels, # 10-15 pixels, and 15-20 pixels for radius in range(5, 21, 5): s = disk(radius).astype(bool) mask_foci_neighbor = np.zeros_like(mask_cyt_out) # for each foci, get a mask of its neighbor and merge them for i in l_id_foci: rna_foci_i = rna_coord_out_foci[rna_coord_out_foci[:, 3] == i, :3] foci = np.mean(rna_foci_i, axis=0) foci = np.round(foci).astype(np.int64) row, col = foci[1], foci[2] mask_neighbor = np.zeros_like(mask_cyt_out) min_row = max(row - radius, 0) min_row_s = min_row - (row - radius) max_row = min(row + radius + 1, mask_neighbor.shape[0]) max_row_s = s.shape[0] - ((row + radius + 1) - max_row) min_col = max(col - radius, 0) min_col_s = min_col - (col - radius) max_col = min(col + radius + 1, mask_neighbor.shape[1]) max_col_s = s.shape[1] - ((col + radius + 1) - max_col) new_s = s[min_row_s:max_row_s, min_col_s:max_col_s] mask_neighbor[min_row:max_row, min_col:max_col] = new_s mask_foci_neighbor |= mask_cyt_out & mask_neighbor # remove neighbor mask from previous radius mask_foci_neighbor[mask_foci_neighbor_cumulated] = False mask_foci_neighbor_cumulated |= mask_foci_neighbor # count mRNAs in such a region mask_rna = mask_foci_neighbor[rna_coord_out[:, 1], rna_coord_out[:, 2]] nb_rna_foci_neighbor = len(rna_coord_out[mask_rna]) area_foci_neighbor = mask_foci_neighbor.sum() factor = nb_rna_out * max(area_foci_neighbor, 1) / cell_out_area index_rna_foci_neighbor = (nb_rna_foci_neighbor + eps) / factor log2_index_rna_foci_neighbor = np.log2(index_rna_foci_neighbor) proportion_rna_foci_neighbor = nb_rna_foci_neighbor / nb_rna_out features += [ index_rna_foci_neighbor, log2_index_rna_foci_neighbor, proportion_rna_foci_neighbor ] # get foci coordinates foci_coord = [] for i in l_id_foci: rna_foci_i = rna_coord_out_foci[rna_coord_out_foci[:, 3] == i, :3] foci = np.mean(rna_foci_i, axis=0) foci = np.round(foci).astype(np.int64) foci_coord.append(foci.reshape(1, 3)) foci_coord = np.array(foci_coord, dtype=np.int64) foci_coord = np.squeeze(foci_coord, axis=1) foci_coord_2d = foci_coord[:, 1:3] # compute statistics from distance to cytoplasm distance_foci_cyt = distance_cyt[foci_coord_2d[:, 0], foci_coord_2d[:, 1]] factor = np.mean(distance_cyt[mask_cyt_out]) index_foci_mean_distance_cyt = (np.mean(distance_foci_cyt) + eps) / factor log2_index_foci_mean_distance_cyt = np.log2(index_foci_mean_distance_cyt) factor = np.median(distance_cyt[mask_cyt_out]) index_foci_med_distance_cyt = (np.median(distance_foci_cyt) + eps) / factor log2_index_foci_med_distance_cyt = np.log2(index_foci_med_distance_cyt) factor = np.std(distance_cyt[mask_cyt_out]) index_foci_std_distance_cyt = (np.std(distance_foci_cyt) + eps) / factor log2_index_foci_std_distance_cyt = np.log2(index_foci_std_distance_cyt) features += [ index_foci_mean_distance_cyt, log2_index_foci_mean_distance_cyt, index_foci_med_distance_cyt, log2_index_foci_med_distance_cyt, index_foci_std_distance_cyt, log2_index_foci_std_distance_cyt ] # compute statistics from distance to nucleus distance_foci_nuc = distance_nuc[foci_coord_2d[:, 0], foci_coord_2d[:, 1]] factor = np.mean(distance_nuc[mask_cyt_out]) index_foci_mean_distance_nuc = (np.mean(distance_foci_nuc) + eps) / factor log2_index_foci_mean_distance_nuc = np.log2(index_foci_mean_distance_nuc) factor = np.median(distance_nuc[mask_cyt_out]) index_foci_med_distance_nuc = (np.median(distance_foci_nuc) + eps) / factor log2_index_foci_med_distance_nuc = np.log2(index_foci_med_distance_nuc) factor = np.std(distance_nuc[mask_cyt_out]) index_foci_std_distance_nuc = (np.std(distance_foci_nuc) + eps) / factor log2_index_foci_std_distance_nuc = np.log2(index_foci_std_distance_nuc) features += [ index_foci_mean_distance_nuc, log2_index_foci_mean_distance_nuc, index_foci_med_distance_nuc, log2_index_foci_med_distance_nuc, index_foci_std_distance_nuc, log2_index_foci_std_distance_nuc ] return features
def Volume_Label(): setDirVariables() ## Options b_display = 0 cubic = 0 compare_Matlab_Python = 0 # Output size if cubic == 1: outSize = [200, 200, 131] else: outSize = [128, 128, 128] templateSize = str(outSize[0]) + '_' + str(outSize[1]) + '_' + str( outSize[2]) # Sets if cubic == 0: InDCMmSet = 'reshape_knee_' else: InDCMmSet = 'cubic_vox_knee_' InDCMmSetdicom = 'knee' fileTemplate = InDCMmSet + templateSize anatomy = 'tibia' # 'tibia' position = 'prox' # 'prox' InSurfSet = 'ct_' + anatomy + '_' outSet = position + anatomy os.chdir(mainInputDataDirectory) fp = open('case.txt', 'r+') casePatient = fp.read() casePatient = int(casePatient) casePatient = 3 fp.close() print('Patient no. {}'.format(casePatient)) xlsName = os.path.join(mainInputDataDirectory, 'Preop Data.xlsx') name = pandas.ExcelFile(xlsName) name = xlrd.open_workbook(xlsName) sheet = name.sheet_by_index(0) rows = sheet.nrows study = [sheet.cell_value(i, 0) for i in range(1, rows)] os.chdir(mainCodeDirectory) ## Read volume nii mainPatientDirectory = 'Patient{:03d}'.format(casePatient) mainPatientDirectory = mainInputDataDirectory + '/' + mainPatientDirectory + '/' mainInputDicomDirectory = mainPatientDirectory + '/' + InDCMmSetdicom + '/' os.chdir(mainPatientDirectory) niiFilename = 'volumeCT_' + fileTemplate + '_{:03d}.nii'.format( casePatient) VolumeCT = loadNiiVolume(niiFilename, mainInputDicomDirectory) ## Normalize VolumeCT = normVolumeScan(VolumeCT) # Read stl and display filename = InSurfSet + study[casePatient - 1] + '.stl' os.chdir(mainPatientDirectory) my_mesh1 = trimesh.load(filename) my_mesh = mesh.Mesh.from_file(filename) os.chdir(mainCodeDirectory) # Build binary volume of the reference surface corresponding to the CT volume VolumeSurf = VolumeCT VolumeSurf.volumeData = np.zeros(VolumeSurf.volumeData.shape, dtype=int) heights = [] for i in range(VolumeSurf.volumeDim[2]): heights.append( float(VolumeSurf.volumeOffset[2]) + i * VolumeSurf.voxelSize[2]) contours = mesh2vol(my_mesh1, heights, VolumeSurf.volumeOffset, VolumeSurf.voxelSize, VolumeSurf.volumeDim) indicesX = [] indicesY = [] for ip in range(VolumeSurf.volumeDim[2]): if contours[ip].shape[0] != 0: val = contours[ip][:, 0] - VolumeSurf.volumeOffset[0] val = val / (VolumeSurf.volumeDim[0] * VolumeSurf.voxelSize[0]) val = np.round(val * VolumeSurf.volumeDim[0], 0) valX = val.astype(int) val = contours[ip][:, 1] - VolumeSurf.volumeOffset[1] val = val / (VolumeSurf.volumeDim[1] * VolumeSurf.voxelSize[1]) val = np.round(val * VolumeSurf.volumeDim[0], 0) valY = val.astype(int) val_index = np.zeros((valX.shape[0], 2)) val_index[:, 0] = valX val_index[:, 1] = valY val_index = np.unique(val_index, axis=0).astype(int) indicesX.append(val_index[:, 0]) indicesY.append(val_index[:, 1]) for i, j in zip(valY, valX): VolumeSurf.volumeData[i - 1, j - 1, ip] = 1 else: indicesX.append([]) indicesY.append([]) VolumeSurfLabeled = VolumeSurf counter = 0 # fill in the image each contour for ip in range(VolumeSurfLabeled.volumeDim[2]): if contours[ip].shape[0] != 0: non_zero_start = np.count_nonzero(VolumeSurf.volumeData[:, :, ip]) ######## REGION FILL binaryImage = binary_fill_holes(VolumeSurf.volumeData[:, :, ip]) binaryImage = binaryImage > 1 / 255 ######### CLOSING kernel = np.ones((5, 5), np.uint8) binaryImage = binary_closing(binaryImage, kernel) ######### FILL HOLES AGAIN binaryImage = binary_fill_holes(binaryImage) non_zero_end = np.count_nonzero(binaryImage) ######### ALTERNATIVE PROCESSING FOR NON CLOSED CONTOURS if non_zero_end < non_zero_start * 4: strel = disk(2) binaryImage = binary_dilation(VolumeSurf.volumeData[:, :, ip], strel) binaryImage = binary_dilation(binaryImage, strel) binaryImage = binary_fill_holes(binaryImage) binaryImage = binary_erosion(binaryImage, strel) binaryImage = binary_erosion(binaryImage, strel) counter = counter + 1 VolumeSurfLabeled.volumeData[:, :, ip] = binaryImage dMin = VolumeSurfLabeled.volumeData.min() D = VolumeSurfLabeled.volumeData + abs(dMin) D = D / D.max() * 255 print(counter) ###### PLOT AND SCROLL ACROSS SLICES if b_display == 1: fig, ax = plt.subplots(1, 1) tracker = IndexTracker(ax, D) fig.canvas.mpl_connect('scroll_event', tracker.onscroll) plt.show() if compare_Matlab_Python == 1: name_dir = outSet + 'Label' mainLabelDirectory = os.path.join(mainPatientDirectory, '{}'.format(name_dir)) os.chdir(mainLabelDirectory) mean_dice = dice_coeff(VolumeSurfLabeled.volumeData, outSet) print(mean_dice) # Make nii file label volumeData = VolumeSurfLabeled.volumeData.astype(np.short) volumeData = np.transpose(volumeData, [1, 0, 2]) voxelSize = VolumeSurfLabeled.voxelSize volumeOffset = VolumeSurfLabeled.volumeOffset affine = np.eye(4) niiVolumeLabel = nib.Nifti1Image(volumeData, affine) niiVolumeLabel.header.set_slope_inter(VolumeSurfLabeled.rescaleSlope, VolumeSurfLabeled.rescaleIntercept) niiVolumeLabel.header.set_qform(affine, 1) niiVolumeLabel.header.set_zooms(voxelSize) niiVolumeLabel.header['qoffset_x'] = volumeOffset[0] niiVolumeLabel.header['qoffset_y'] = volumeOffset[1] niiVolumeLabel.header['qoffset_z'] = volumeOffset[2] os.chdir(mainPatientDirectory) # Save nii filenameLabel = 'volumeLabel_' + outSet + '_' + templateSize + '_{:03d}.nii'.format( casePatient) #nib.nifti1.save(niiVolumeLabel, filenameLabel) os.chdir(mainCodeDirectory)
def create_buffer_mask(og_mask, foreground_threshold=0, buffer_additional=0): ''' Function to add an equal-area buffer to regions in a binary image. Foreground threshold is used if input binary image had been resampled with bicubic or quadratic and thus has multiple greyscale levels. 127 would be a reasonable number for foreground_threshold in this case. Inputs: OG_mask binary mask with background = 0 foreground_threshold maximum value to treat as background (rest become foreground) buffer_additional additional pixels to buffer (+/-) to modify equal-area default ''' og_mask[og_mask > foreground_threshold] = 1 labeled = measure.label(og_mask, background=0, connectivity=2) if np.all(og_mask == 1): mask = np.full(labeled.shape, True) elif np.all(og_mask == 0): mask = np.full(labeled.shape, False) else: regions = measure.regionprops(labeled) mask = np.full(labeled.shape, False) copy = np.full(labeled.shape, False) for x, region in enumerate(regions): old_radius = region.equivalent_diameter / 2 old_area = np.pi * old_radius**2 new_area = 2 * old_area new_radius = np.sqrt(new_area / np.pi) buffer = new_radius - old_radius + buffer_additional coords = region.coords i = coords[:, 0] j = coords[:, 1] copy[i, j] = True dist = int(np.ceil(buffer)) bbox_coords = region.bbox #(min_row, min_col, max_row, max_col) if bbox_coords[0] - dist >= 0: bbox_i_min = bbox_coords[0] - dist else: bbox_i_min = 0 if bbox_coords[1] - dist >= 0: bbox_j_min = bbox_coords[1] - dist else: bbox_j_min = 0 if bbox_coords[2] + dist <= copy.shape[0]: bbox_i_max = bbox_coords[2] + dist else: bbox_i_max = copy.shape[0] if bbox_coords[3] + dist <= copy.shape[1]: bbox_j_max = bbox_coords[3] + dist else: bbox_j_max = copy.shape[1] #for i in range(0, int(np.ceil(buffer))): copy_x = copy[bbox_i_min:bbox_i_max, bbox_j_min:bbox_j_max] if dist > dilation_radius_step_sz * 2: dist_tmp = dilation_radius_step_sz dist_remain = dist # init while (dist_tmp > 0) & (dist_remain > 0): strelem = selem.disk(dist_tmp) copy_x = binary_dilation(copy_x, selem=strelem) dist_remain = dist_remain - dist_tmp # after this iter dist_tmp = np.min( [dist_remain - dist_tmp, dilation_radius_step_sz]) # for next iter else: # strelem = np.ones((dist*2+1,dist*2+1)) # box strelem = selem.disk(dist) # disk copy_x = binary_dilation(copy_x, selem=strelem) copy[bbox_i_min:bbox_i_max, bbox_j_min:bbox_j_max] = copy_x #bounds = find_boundaries(pekel_copy) #pekel_copy[bounds] = 1 mask = mask | copy mask[mask > 0] = 1 return mask
.. note: local threshold is much slower than global one. .. [1] http://en.wikipedia.org/wiki/Otsu's_method """ import matplotlib.pyplot as plt from skimage import data from skimage.morphology.selem import disk import skimage.filter.rank as rank from skimage.filter import threshold_otsu p8 = data.page() radius = 10 selem = disk(radius) loc_otsu = rank.otsu(p8, selem) t_glob_otsu = threshold_otsu(p8) glob_otsu = p8 >= t_glob_otsu plt.figure() plt.subplot(2, 2, 1) plt.imshow(p8, cmap=plt.cm.gray) plt.xlabel('original') plt.colorbar() plt.subplot(2, 2, 2) plt.imshow(loc_otsu, cmap=plt.cm.gray) plt.xlabel('local Otsu ($radius=%d$)' % radius) plt.colorbar() plt.subplot(2, 2, 3)