def test_operations_on_closed_handle(self):
     osr = OpenSlide(file_path('boxes.tiff'))
     props = osr.properties
     associated = osr.associated_images
     osr.close()
     self.assertRaises(ArgumentError,
             lambda: osr.read_region((0, 0), 0, (100, 100)))
     self.assertRaises(ArgumentError, lambda: osr.close())
     self.assertRaises(ArgumentError, lambda: props['openslide.vendor'])
     self.assertRaises(ArgumentError, lambda: associated['label'])
示例#2
0
    def get_image_open(wsi_path):
        try:
            wsi_image = OpenSlide(wsi_path)
            level_used = 6
            rgb_image = np.array(
                wsi_image.read_region((0, 0), level_used,
                                      wsi_image.level_dimensions[level_used]))
            wsi_image.close()
        except OpenSlideUnsupportedFormatError:
            raise ValueError(
                'Exception: OpenSlideUnsupportedFormatError for %s' % wsi_path)

        # hsv -> 3 channel
        hsv = cv2.cvtColor(rgb_image, cv2.COLOR_BGR2HSV)
        lower_red = np.array([20, 20, 20])
        upper_red = np.array([200, 200, 200])
        # mask -> 1 channel
        mask = cv2.inRange(hsv, lower_red, upper_red)

        close_kernel = np.ones((20, 20), dtype=np.uint8)
        image_close = cv2.morphologyEx(np.array(mask), cv2.MORPH_CLOSE,
                                       close_kernel)
        open_kernel = np.ones((5, 5), dtype=np.uint8)
        image_open = cv2.morphologyEx(np.array(image_close), cv2.MORPH_OPEN,
                                      open_kernel)

        return image_open
示例#3
0
def v_slide(svs_path, bound_y, queue, tile_size, start_point):
    """
    """
    logger = logging.getLogger(__name__)
    AVG_THRESHOLD = 170
    LAYER = 0
    GREEN_CHANNEL_INDEX = 1
    try:
        svs_file = OpenSlide(svs_path)
        filename = os.path.basename(svs_path)

        x0 = start_point[0]
        y0 = start_point[1]

        pid = os.getpid()
        logger.debug(f'{pid}: start working...')
        logger.debug(f'worker {pid} working on {filename}...')
        while y0 < bound_y:
            img = svs_file.read_region((x0, y0), LAYER, (tile_size, tile_size))
            green_c_avg = np.average(np.array(img)[:, :, GREEN_CHANNEL_INDEX])
            if green_c_avg < AVG_THRESHOLD:
                img = np.array(img)
                img = img[:, :, 0:3]
                queue.put(img)

            y0 += tile_size

        logger.debug(f'{pid}: work finished.')
    finally:
        svs_file.close()
    def read_wsi_tumor(wsi_path, mask_path):
        """
            # =====================================================================================
            # read WSI image and resize
            # Due to memory constraint, we use down sampled (4th level, 1/32 resolution) image
            # ======================================================================================
        """
        try:
            wsi_image = OpenSlide(wsi_path)
            wsi_mask = OpenSlide(mask_path)

            level_used = wsi_image.level_count - 1

            rgb_image = np.array(wsi_image.read_region((0, 0), level_used,
                                                       wsi_image.level_dimensions[level_used]))

            mask_level = wsi_mask.level_count - 1
            mask_image = wsi_mask.read_region((0, 0), mask_level,
                                              wsi_image.level_dimensions[mask_level])
            resize_factor = float(1.0 / pow(2, level_used - mask_level))
            # print('resize_factor: %f' % resize_factor)
            mask_image = cv2.resize(np.array(mask_image), (0, 0), fx=resize_factor, fy=resize_factor)

            wsi_mask.close()
        except OpenSlideUnsupportedFormatError:
            print('Exception: OpenSlideUnsupportedFormatError')
            return None, None, None, None

        return wsi_image, rgb_image, mask_image, level_used
示例#5
0
def v_slide(slp, n_y, x, y, tile_size, stepsize, x0, outdir, std):
    # pid = os.getpid()
    # print('{}: start working'.format(pid))
    slide = OpenSlide(slp)
    imloc = []
    imlist = []
    y0 = 0
    target_x = x0 * stepsize
    image_x = target_x + x
    while y0 < n_y:
        target_y = y0 * stepsize
        image_y = target_y + y
        img = slide.read_region((image_x, image_y), 0, (tile_size, tile_size))
        wscore = bgcheck(img, tile_size)
        if wscore < 0.3:
            img = img.resize((tile_size, tile_size))
            # try:
            #     img = normalization(img, std)
            # except staintools.miscellaneous.exceptions.TissueMaskException:
            #     print("Empty tissue mask computed: region_x-{}-y-{}".format(image_x, image_y))
            #     y0 += 1
            #     continue
            # except:
            #     print('An error occurred: region_x-{}-y-{}'.format(image_x, image_y))
            #     y0 += 1
            #     continue
            img.save(outdir + "/region_x-{}-y-{}.png".format(image_x, image_y))
            strr = outdir + "/region_x-{}-y-{}.png".format(image_x, image_y)
            imloc.append([x0, y0, image_x, image_y, target_x, target_y, strr])
            # imlist.append(np.array(img)[:, :, :3])
        y0 += 1
    slide.close()
    return imloc, imlist
示例#6
0
    def read_wsi_tumor(wsi_path, mask_path):
        """
            # =====================================================================================
            # read WSI image and resize
            # Due to memory constraint, we use down sampled (4th level, 1/32 resolution) image
            # ======================================================================================
        """
        try:
            wsi_image = OpenSlide(wsi_path)
            wsi_mask = OpenSlide(mask_path)

            level_used = wsi_image.level_count - 1

            rgb_image = np.array(
                wsi_image.read_region((0, 0), level_used,
                                      wsi_image.level_dimensions[level_used]))

            mask_level = wsi_mask.level_count - 1
            tumor_gt_mask = wsi_mask.read_region(
                (0, 0), mask_level, wsi_image.level_dimensions[mask_level])
            resize_factor = float(1.0 / pow(2, level_used - mask_level))
            # print('resize_factor: %f' % resize_factor)
            tumor_gt_mask = cv2.resize(np.array(tumor_gt_mask), (0, 0),
                                       fx=resize_factor,
                                       fy=resize_factor)

            wsi_mask.close()
        except OpenSlideUnsupportedFormatError:
            print('Exception: OpenSlideUnsupportedFormatError')
            return None, None, None, None

        return wsi_image, rgb_image, wsi_mask, tumor_gt_mask, level_used
示例#7
0
    def __init__(self, filepath, num_of_imgs=0, resume_from=None):

        super(MaskTestset, self).__init__()

        self.filepath = filepath
        self.patch_size = patch_size

        if os.path.isdir(self.filepath):
            self.images_grid = []  # list ( n * 2 )
            self.imageIDX = []  # list ( n )
            self.image_size = []  # list ( ? )

            self.files = [
                f for f in sorted(os.listdir(self.filepath))
                if os.path.isfile(os.path.join(self.filepath, f))
            ]
            if resume_from is not None:
                self.files[:self.files.index(resume_from)] = []
            for i, file in enumerate(tqdm(self.files, desc="loading images")):
                if num_of_imgs != 0 and i == num_of_imgs:
                    break
                if file.endswith((".svs", ".tiff")):
                    self.mode = "WSI"
                    slide = OpenSlide(os.path.join(self.filepath, file))
                    patches_grid = self.sample_patches(slide.dimensions,
                                                       self.patch_size - 16)
                    self.images_grid.extend(patches_grid)
                    self.imageIDX.extend([i] * len(patches_grid))
                    self.image_size.append(slide.dimensions)
                    slide.close()
                elif file.endswith((".jpg", ".png")):
                    self.mode = "ROI"
                    img = io.imread(os.path.join(self.filepath,
                                                 file)).astype(np.uint8)
                    patches_grid = self.sample_patches(img.shape,
                                                       self.patch_size - 16)
                    self.images_grid.extend(patches_grid)
                    self.imageIDX.extend([i] * len(patches_grid))
                    self.image_size.append(img.shape)
                else:
                    raise FileNotFoundError("Invalid data directory.")

        elif (os.path.exists(self.filepath) and os.path.isfile(self.filepath)
              and self.filepath.endswith(("h5", "hdf5"))):
            self.mode = "patch"
            self.images = []
            f = h5py.File(self.filepath, 'r')
            for i, img in enumerate(f['x']):
                if num_of_imgs != 0 and i == num_of_imgs:
                    break
                self.images.append(img)

        else:
            raise FileNotFoundError("Invalid data directory.")

        self.transform = transforms.Compose([
            transforms.ToTensor(),
            transforms.Normalize(mean=[0.485, 0.456, 0.406],
                                 std=[0.229, 0.224, 0.225])
        ])
示例#8
0
def process_zip(path, project_name):
    proj_base_dir = os.environ.get('PROJECT_BASE_DIR', '/mnt/WORK/temp/')
    data = {'project_name': project_name, 'images': []}
    if proj_base_dir is None or not os.path.exists(path):
        return False, None

    project_dir = os.path.join(proj_base_dir, project_name)
    save_dir = os.path.join(project_dir, 'wsi')
    thumbnails = os.path.join(project_dir, 'thumbnails')
    os.makedirs(save_dir)
    os.makedirs(thumbnails)

    with zipfile.ZipFile(path, 'r') as zip_ref:
        zip_ref.extractall(save_dir)

    for wsi in os.listdir(save_dir):
        slide = OpenSlide(os.path.join(save_dir, wsi))

        fname = _save_thumbnails(slide, thumbnails, wsi)

        temp = {}
        temp['tiles'] = _extract_tiles(slide)
        temp['name'] = wsi
        temp['project_name'] = project_name
        temp['meta'] = {}
        temp['thumbnail'] = fname
        data['images'].append(temp)

        slide.close()
    return True, data
示例#9
0
def v_slide(slp, n_y, x, y, tile_size, stepsize, x0, outdir, level, dp, std):
    # pid = os.getpid()
    # print('{}: start working'.format(pid))
    slide = OpenSlide(slp)
    imloc = []
    y0 = 0
    target_x = x0 * stepsize
    image_x = (target_x + x)*(4**level)
    while y0 < n_y:
        target_y = y0 * stepsize
        image_y = (target_y + y)*(4**level)
        img = slide.read_region((image_x, image_y), level, (tile_size, tile_size))
        wscore = bgcheck(img, tile_size)
        if wscore < 0.7:
            img = img.resize((299, 299))
            img = normalization(img, std)
            if dp:
                img.save(outdir + "/region_x-{}-y-{}_{}.png".format(image_x, image_y, str(dp)))
                strr = outdir + "/region_x-{}-y-{}_{}.png".format(image_x, image_y, str(dp))
            else:
                img.save(outdir + "/region_x-{}-y-{}.png".format(image_x, image_y))
                strr = outdir + "/region_x-{}-y-{}.png".format(image_x, image_y)
            imloc.append([x0, y0, image_x, image_y, strr])
        y0 += 1
    slide.close()
    return imloc
示例#10
0
 def test_operations_on_closed_handle(self):
     osr = OpenSlide(file_path('boxes.tiff'))
     props = osr.properties
     associated = osr.associated_images
     osr.close()
     self.assertRaises(ArgumentError, lambda: osr.read_region((0, 0), 0, (100, 100)))
     self.assertRaises(ArgumentError, lambda: osr.close())
     self.assertRaises(ArgumentError, lambda: props['openslide.vendor'])
     self.assertRaises(ArgumentError, lambda: associated['label'])
def read_wsi_normal(wsi_normal_path, resolution_level=None):

    wsi_normal = OpenSlide(wsi_normal_path)
    if resolution_level is None:
        resolution_level = wsi_normal.level_count - 1
    wsi_normal_sample = wsi_normal.read_region(
        (0, 0), resolution_level,
        wsi_normal.level_dimensions[resolution_level])
    wsi_normal.close()
    return wsi_normal_sample
示例#12
0
def get_image_metadata():
    """
    Read slide and process
    :return:
    """
    p = Path(os.path.join(SLIDE_DIR, (CASE_ID + '.svs')))
    osr = OpenSlide(str(p))
    # props = osr.properties
    # props.__getitem__('openslide.level[0].width')
    # props.__getitem__('openslide.level[0].height')
    image_width = osr.dimensions[0]
    image_height = osr.dimensions[1]
    osr.close()
    return image_width, image_height
示例#13
0
def v_slide(params):
    """
    """

    paths = Paths()
    try:
        try:
            scn_file = OpenSlide(paths.slice_80)
        except OpenSlideUnsupportedFormatError:
            logging.error("OpenSlideUnsupportedFormatError!")
            return
        except OpenSlideError:
            logging.error("OpenSlideError!")
            return

        start_point = params["start_point"]
        x0 = start_point[0]
        y0 = start_point[1]
        bound_y = params["bound_y"]
        tile_path = params["tile_path"]
        save_tiles = params["save_tiles"]
        q = params["queue"]

        AVG_THRESHOLD = 170
        pid = os.getpid()
        data = {}
        while y0 < bound_y:
            img = scn_file.read_region((x0, y0), 0, (299, 299))
            green_c_avg = np.average(np.array(img)[:, :, 1])
            if green_c_avg < AVG_THRESHOLD:
                sufix = "_" + str(x0) + "_" + \
                        str(y0) + ".png"
                file_name = "scn80" + sufix
                img = np.array(img)
                img = img[:, :, 0:3]
                data['pred'] = img
                data['xlabel'] = np.array([x0])
                data['ylabel'] = np.array([y0])
                q.put(dict(data))
                if save_tiles:
                    img.save(os.path.join(tile_path, file_name))

            y0 += 150

        return pid
    finally:
        scn_file.close()
示例#14
0
def v_slide(slp,
            s_id,
            n_y,
            x,
            y,
            tile_size,
            step_size,
            out_dir,
            x0,
            std=np.asarray([])):
    # pid = os.getpid()
    # print('{}: start working'.format(pid))
    slide = OpenSlide(slp)
    imloc = []
    imlist = []
    y0 = 0
    target_x = x0 * step_size
    image_x = target_x + x
    while y0 < n_y:
        target_y = y0 * step_size
        image_y = target_y + y
        img = slide.read_region((image_x, image_y), 0, (tile_size, tile_size))
        wscore = bgcheck(img)
        # output png files if an output directory is given
        if wscore < 0.8:
            tile_name = s_id + '_tile_x_{}_y_{}.png'.format(
                str(image_x).zfill(6),
                str(image_y).zfill(6))
            if std.any():
                img = normalization(img, std)
            if out_dir:
                img.save(out_dir + '/' + tile_name)
        # append image data and descriptions to list
            imlist.append(np.asarray(img)[:, :, :3].astype(np.uint8))
            imloc.append([
                s_id,
                str(x0).zfill(3),
                str(y0).zfill(3),
                str(target_x).zfill(5),
                str(target_y).zfill(5), tile_name
            ])

        y0 += 1
    slide.close()

    return imloc, imlist
示例#15
0
def v_slide(slp, n_y, x, y, tile_size, stepsize, x0):
    # pid = os.getpid()
    # print('{}: start working'.format(pid))
    slide = OpenSlide(slp)
    imloc = []
    imlist = []
    y0 = 0
    target_x = x0 * stepsize
    image_x = target_x + x
    while y0 < n_y:
        target_y = y0 * stepsize
        image_y = target_y + y
        img = slide.read_region((image_x, image_y), 0, (tile_size, tile_size))
        wscore = bgcheck(img)
        if wscore < 0.5:
            imloc.append([x0, y0, target_x, target_y])
            imlist.append(np.array(img)[:, :, :3])
        y0 += 1
    slide.close()
    return imloc, imlist
def read_wsi_tumor(wsi_tumor_path, wsi_mask_path, resolution_level=None):

    try:

        wsi_tumor = OpenSlide(wsi_tumor_path)
        if resolution_level is None:
            resolution_level = wsi_tumor.level_count - 1
        wsi_tumor_sample = wsi_tumor.read_region(
            (0, 0), resolution_level,
            wsi_tumor.level_dimensions[resolution_level])
        wsi_tumor.close()

        wsi_tumor_mask = OpenSlide(wsi_mask_path)
        wsi_tumor_mask_sample = wsi_tumor_mask.read_region(
            (0, 0), resolution_level,
            wsi_tumor_mask.level_dimensions[resolution_level])
        wsi_tumor_mask.close()
    except Exception as e:
        return None, None

    return wsi_tumor_sample, wsi_tumor_mask_sample
def generate_thumbnail(image_filepath, thumbnail_filepath):
    '''
    Generates a thumbnail from the specified image.
    '''

    # open image with OpenSlide library
    image_file = OpenSlide(image_filepath)
    
    # extract image dimensions
    image_dims = image_file.dimensions
    
    # make thumbnail 100 times smaller
    thumb_dims = tuple( (x/100 for x in image_dims) )
    
    # create thumbnail
    thumb_file = image_file.get_thumbnail(thumb_dims)
    
    # save file with desired path, format
    thumb_file.save(thumbnail_filepath, "png")
    
    # cleanup
    image_file.close()
示例#18
0
def napari_get_reader(path):
    """A basic implementation of the napari_get_reader hook specification.

    Parameters
    ----------
    path : str or list of str
        Path to file, or list of paths.

    Returns
    -------
    function or None
        If the path is a recognized format, return a function that accepts the
        same path or list of paths, and returns a list of layer data tuples.
    """
    if isinstance(path, list):
        # Don't handle multiple paths
        return None

    if OpenSlide.detect_format(path) is None:
        return None

    try:
        slide = OpenSlide(path)
    except OpenSlideUnsupportedFormatError:
        return None

    description = slide.properties.get(PROPERTY_NAME_COMMENT)
    # Don't try to handle OME-TIFF
    # https://github.com/cgohlke/tifffile/blob/b346e3bd7de81de512a6715b01124c8f6d60a707/tifffile/tifffile.py#L5781
    if description and description[-4:] == "OME>":
        return None

    # Don't try to handle files that aren't multiscale.
    if slide.level_count == 1:
        return None

    slide.close()

    return reader_function
示例#19
0
def glue_to_one_picture_from_coord(url, regions, window=200, K=16, layer=0):
    block = int(np.sqrt(K))
    slide = OpenSlide(url)
    scale = slide.level_downsamples[2] / slide.level_downsamples[layer]
    image = np.full(
        (int(block * window * scale), int(block * window * scale), 3),
        255,
        dtype=np.uint8)
    for i, itr in enumerate(regions):
        r, c = divmod(i, block)
        ws = int(window * scale)
        patch = np.asarray(
            slide.read_region((int(itr[0] * slide.level_downsamples[2]),
                               int(itr[1] * slide.level_downsamples[2])),
                              layer, (ws, ws)))[:, :, :3]
        r0 = int(r * window * scale)
        r1 = r0 + ws
        c0 = int(c * window * scale)
        c1 = c0 + ws
        image[r0:r1, c0:c1, :] = patch
    slide.close()
    return image
示例#20
0
def glue_to_one_picture_from_coord(url, regions, window=200, K=16, layer=0):
    block = int(np.sqrt(K))
    slide = OpenSlide(url)
    scale = slide.level_downsamples[2] / slide.level_downsamples[layer]
    image = np.full((int(block * window * scale), int(block * window * scale), 3), 255, dtype=np.uint8)
    for i, itr in enumerate(regions):
        r, c = divmod(i, block)
        ws = int(window * scale)
        xs = int(itr[0] * slide.level_downsamples[2])
        ys = int(itr[1] * slide.level_downsamples[2])
        # patch = np.asarray(slide.read_region((xs, ys), layer, (ws, ws)))[:, :, :3]
        # 環境によっては以下の対応が必要
        wo = int(window * slide.level_downsamples[2])
        patch = np.asarray(slide.read_region((xs, ys), 0, (wo, wo)))[:, :, :3]
        # layer != 0 の場合はリサイズ
        if layer > 0:
            patch = cv2.resize(patch, (ws, ws))
        r0 = int(r * window * scale)
        r1 = r0 + ws
        c0 = int(c * window * scale)
        c1 = c0 + ws
        image[r0:r1, c0:c1, :] = patch
    slide.close()
    return image
示例#21
0
    def get_a_patch(self, idx):

        if self.mode == "WSI":
            image_file = os.path.join(self.filepath,
                                      self.files[self.imageIDX[idx]])
            slide = OpenSlide(image_file)
            x, y = self.images_grid[idx]
            patch = np.asarray(
                slide.read_region((x, y), level=0,
                                  size=tuple(self.patch_size)).convert('RGB'))
            slide.close()
            return patch, self.imageIDX[idx]

        elif self.mode == "ROI":
            image_file = os.path.join(self.filepath,
                                      self.files[self.imageIDX[idx]])
            image = io.imread(image_file).astype(np.uint8)
            x, y = self.images_grid[idx]
            patch = image[x:x + self.patch_size[0], y:y + self.patch_size[1]]
            return patch, self.imageIDX[idx]

        else:
            patch = self.images[idx]
            return [patch]
示例#22
0
def glue_to_one_picture_from_coord_lowlayer(url, regions, window=200, K=16, layer=1):
    block = int(np.sqrt(K))
    slide = OpenSlide(url)
    scale = slide.level_downsamples[2] / slide.level_downsamples[layer]
    slide.close()
    slide = skimage.io.MultiImage(url)[layer]
    slide = np.array(slide)
    image = np.full((int(block * window * scale), int(block * window * scale), 3), 255, dtype=np.uint8)
    # print(coordinates)
    for i, itr in enumerate(regions):
        r, c = divmod(i, block)
        ws = int(window * scale)
        x0 = int(itr[0] * scale)
        x1 = x0 + ws
        y0 = int(itr[1] * scale)
        y1 = y0 + ws
        buf = slide[y0:y1, x0:x1, :]
        h, w = buf.shape[:2]
        if h < ws or w < ws:
            dx = max(0, ws - w)
            dy = max(0, ws - h)
            buf = np.pad(buf, [[0, dy], [0, dx], [0, 0]], 'constant', constant_values=(255, 255))
        image[r * ws:(r+1) * ws, c * ws:(c+1) * ws, :] = buf
    return image
class SlideReader:
    handle = None
    suffix = None
    attrs = None
    _path = None

    def __init__(self):
        pass

    @property
    def path(self):
        return self._path

    def open(self, path):
        if path == self._path or path is None:
            return
        else:
            self.close()
        try:
            self.suffix = os.path.splitext(path)[-1]
            if self.suffix == ".sdpc":
                self.handle = Sdpc()
                self.handle.open(path)
            elif self.suffix == ".srp":
                self.handle = Srp()
                self.handle.open(path)
            elif self.suffix == ".svs" or self.suffix == ".mrxs":
                self.handle = OpenSlide(path)
            else:
                raise ValueError("File type: {} is not supported.".format(self.suffix))
            self._path = path
        except Exception as e:
            logger.error(f'{e}\nNote: some system requires absolute path for wsi image.')
            self.close()

    def close(self):
        self._path = None
        if self.handle is None:
            return
        self.handle.close()
        self.handle = None

    def get_attrs(self):
        return get_attrs(self)

    def get_tile(self, location: tuple, size: tuple, level: int):
        """ get tile from slide
        :param location: (x, y) at level 0
        :param size:  (w, h) at level 0
        :param level: read in level
        :return:  RGB img array
        """
        if level == 0:
            return self.get_tile_for_level0(location, size)
        # ensure the attrs had been created
        if self.attrs:
            pass
        else:
            self.get_attrs()
        # main operations for reading tile
        tile_size = mpp_transformer(size, self.attrs["mpp"], self.attrs["mpp"] * (self.attrs["level_ratio"] ** level))
        if self.suffix in ['.sdpc', '.srp']:
            # parts exceed right and bottom is filled with (255, 255, 255)
            location = mpp_transformer(location,
                                       self.attrs["mpp"], self.attrs["mpp"] * (self.attrs["level_ratio"] ** level))
            tile = self.handle.getTile(level, location[0], location[1], tile_size[0], tile_size[1])
        elif self.suffix in ['.svs', '.mrxs']:
            # parts exceed right and bottom is filled with (0, 0, 0)
            tile = np.array(self.handle.read_region(location, level, tile_size).convert('RGB'))
        return tile

    def get_tile_for_level0(self, location: tuple, size: tuple):
        """ get tile from slide in level 0
        :param location: (x, y) at level 0
        :param size:  (w, h) at level 0
        :return:  RGB img array
        """
        # main operations for reading tile
        if self.suffix in ['.sdpc', '.srp']:
            # parts exceed right and bottom is filled with (255, 255, 255)
            tile = self.handle.getTile(0, location[0], location[1], size[0], size[1])
        elif self.suffix in ['.svs', '.mrxs']:
            # parts exceed right and bottom is filled with (0, 0, 0)
            tile = np.array(self.handle.read_region(location, 0, size).convert('RGB'))
        return tile

    def __del__(self):
        self.close()
示例#24
0
    for prob_path, wsi_path in zip(prob_paths, wsi_paths):
        dir_name = wsi_path.split('/')[-1].split('.')[0]
        dir_name = utils.TEST_RESULT + '/' + dir_name
        if os.path.exists(dir_name):
            shutil.rmtree(dir_name)
        os.mkdir(dir_name)

        wsi_img = OpenSlide(wsi_path)
        dispaly_level = int(
            input('Level from 0 to %d you want to dispaly:' %
                  (wsi_img.level_count - 1)))
        rgb_img = np.array(
            wsi_img.read_region(
                (0, 0), dispaly_level,
                wsi_img.level_dimensions[dispaly_level]))[:, :, :3].copy()
        wsi_img.close()
        plt.imsave(dir_name + '/rgb.png', rgb_img)

        image_open, mask = generate_image_open(rgb_img.copy())
        plt.imsave(dir_name + '/open.png', image_open, cmap=plt.cm.gray)
        plt.imsave(dir_name + '/mask.png', mask, cmap=plt.cm.gray)

        prob_img = plt.imread(prob_path)
        plt.imshow(prob_img, cmap=plt.cm.gray)
        plt.imsave(dir_name + '/prob_img.png', prob_img, cmap=plt.cm.gray)

        # features, region_coors = extract_features(heatmap,image_open)
        # img_contour = image_open.copy()
        # cv2.drawContours(img_contour,region_coors,-1,(255,0,0),2)
        # plt.imshow(img_contour)
        # plt.imsave(dir_name + '/contour.jpg',img_contour)
class WSI(object):
    """
        # ================================
        # Class to annotate WSIs with ROIs
        # ================================

    """
    index = 0
    negative_patch_index = 0
    positive_patch_index = 0
    wsi_paths = []
    mask_paths = []
    def_level = 7
    level_extract = 2
    key = 0

    def extract_patches_mask(self, bounding_boxes):
        """
        Extract positive patches targeting annotated tumor region

        Save extracted patches to desk as .png image files

        :param bounding_boxes: list of bounding boxes corresponds to tumor regions
        :return:

        """
        mag_factor = pow(2, self.level_used)

        print('No. of ROIs to extract patches from: %d' % len(bounding_boxes))

        for i, bounding_box in enumerate(bounding_boxes):
            b_x_start = int(bounding_box[0]) * mag_factor
            b_y_start = int(bounding_box[1]) * mag_factor
            b_x_end = (int(bounding_box[0]) +
                       int(bounding_box[2])) * mag_factor
            b_y_end = (int(bounding_box[1]) +
                       int(bounding_box[3])) * mag_factor
            X = np.random.random_integers(b_x_start, high=b_x_end, size=500)
            Y = np.random.random_integers(b_y_start, high=b_y_end, size=500)
            # X = np.arange(b_x_start, b_x_end-256, 5)
            # Y = np.arange(b_y_start, b_y_end-256, 5)

            for x, y in zip(X, Y):
                mask = self.mask_image.read_region((x, y), 0,
                                                   (PATCH_SIZE, PATCH_SIZE))
                mask_gt = np.array(mask)
                mask_gt = cv2.cvtColor(mask_gt, cv2.COLOR_BGR2GRAY)

                white_pixel_cnt_gt = cv2.countNonZero(mask_gt)

                if white_pixel_cnt_gt > ((PATCH_SIZE * PATCH_SIZE) * 0.90):
                    # mask = Image.fromarray(mask)
                    patch = self.wsi_image.read_region(
                        (x, y), 0, (PATCH_SIZE, PATCH_SIZE))
                    patch.save(
                        PROCESSED_PATCHES_FROM_USE_MASK_POSITIVE_PATH +
                        PATCH_TUMOR_PREFIX + str(PATCH_SIZE) + '_' +
                        str(self.positive_patch_index), 'PNG')
                    self.positive_patch_index += 1
                    patch.close()

                mask.close()

    def extract_patches_normal(self, bounding_boxes):
        """
            Extract negative patches from Normal WSIs

            Save extracted patches to desk as .png image files

            :param bounding_boxes: list of bounding boxes corresponds to detected ROIs
            :return:

        """
        mag_factor = pow(2, self.level_used)
        mag_factor2 = pow(2, self.level_extract)
        bbx_co = []
        bby_co = []

        print('No. of ROIs to extract patches from: %d' % len(bounding_boxes))

        for i, bounding_box in enumerate(bounding_boxes):
            # sometimes the bounding boxes annotate a very small area not in the ROI
            if (bounding_box[2] * bounding_box[3]) < 2500:
                continue
            b_x_start = int(bounding_box[0]) * mag_factor
            b_y_start = int(bounding_box[1]) * mag_factor
            b_x_end = (int(bounding_box[0]) +
                       int(bounding_box[2])) * mag_factor
            b_y_end = (int(bounding_box[1]) +
                       int(bounding_box[3])) * mag_factor

            # X = np.arange(b_x_start, b_x_end-256, 5)
            # Y = np.arange(b_y_start, b_y_end-256, 5)
            h = int(bounding_box[2]) * mag_factor
            w = int(bounding_box[3]) * mag_factor
            print("Size of bounding box = %s" % h + " by %s" % w)

            tumoridx = 0
            ntumoridx = 0
            patchidx = 0
            for y_left in range(b_y_start, b_y_end, PATCH_SIZE * mag_factor2):

                bby_co.append(range(y_left, y_left + PATCH_SIZE * mag_factor2))

                for x_left in range(b_x_start, b_x_end,
                                    PATCH_SIZE * mag_factor2):

                    if x_left in bbx_co and y_left in bby_co:
                        print("Skipping double Bounding Box %s " %
                              self.cur_wsi_path)
                        continue

                    bbx_co.append(
                        range(x_left, x_left + PATCH_SIZE * mag_factor2))

                    patch = self.wsi_image.read_region(
                        (x_left, y_left), 2, (PATCH_SIZE, PATCH_SIZE))
                    mask = np.zeros((PATCH_SIZE, PATCH_SIZE), dtype=np.uint8)
                    mask = Image.fromarray(mask)
                    # mask = self.mask_image.read_region((x_left, y_left), 2, (PATCH_SIZE, PATCH_SIZE))

                    _std = ImageStat.Stat(patch).stddev
                    patch_array = np.array(patch)
                    # thresholding stddev for patch extraction
                    patchidx += 1
                    if (sum(_std[:3]) / len(_std[:3])) < 15:
                        continue

                    patch_hsv = cv2.cvtColor(patch_array, cv2.COLOR_BGR2HSV)
                    # [20, 20, 20]
                    lower_red = np.array([0, 0, 0])
                    # [255, 255, 255]
                    upper_red = np.array([200, 200, 200])
                    mask_patch = cv2.inRange(patch_hsv, lower_red, upper_red)
                    white_pixel_cnt = cv2.countNonZero(mask_patch)

                    if white_pixel_cnt > ((PATCH_SIZE * PATCH_SIZE) * 0.04):
                        # mask = Image.fromarray(mask)
                        if self.negative_patch_index % 2 == 0:
                            patch.save(
                                PROCESSED_PATCHES_POSITIVE_PATH +
                                PATCH_NORMAL_PREFIX + str(PATCH_SIZE) + '_' +
                                str(self.negative_patch_index) + '.png', 'PNG')
                            mask.save(
                                PROCESSED_PATCHES_POSITIVE_MASK_PATH +
                                'mask_' + PATCH_NORMAL_PREFIX +
                                str(PATCH_SIZE) + '_' +
                                str(self.negative_patch_index) + '.png', 'PNG')
                        self.negative_patch_index += 1
                        ntumoridx += 1

                    patch.close()

            print(
                "Processed patches in bounding box %s of %s :" %
                (i, self.cur_wsi_path), "%s" % patchidx,
                " negative: %s" % ntumoridx)

    def extract_patches_tumor(self, bounding_boxes):
        """
            Extract both, negative patches from Normal area and positive patches from Tumor area

            Save extracted patches to desk as .png image files

            :param bounding_boxes: list of bounding boxes corresponds to detected ROIs
            :return:

        """
        mag_factor = pow(2, self.level_used)
        mag_factor2 = pow(2, self.level_extract)
        bbx_co = []
        bby_co = []
        print('No. of ROIs to extract patches from: %d' % len(bounding_boxes))
        for i, bounding_box in enumerate(bounding_boxes):
            # sometimes the bounding boxes annotate a very small area not in the ROI
            if (bounding_box[2] * bounding_box[3]) < 2500:
                continue
            b_x_start = int(bounding_box[0]) * mag_factor
            b_y_start = int(bounding_box[1]) * mag_factor
            b_x_end = (int(bounding_box[0]) +
                       int(bounding_box[2])) * mag_factor
            b_y_end = (int(bounding_box[1]) +
                       int(bounding_box[3])) * mag_factor

            # pdb.set_trace()
            h = int(bounding_box[2]) * mag_factor
            w = int(bounding_box[3]) * mag_factor
            print("Size of bounding box = %s" % h + " by %s" % w)

            tumoridx = 0
            ntumoridx = 0
            patchidx = 0
            for y_left in range(b_y_start, b_y_end, PATCH_SIZE * mag_factor2):

                bby_co.append(range(y_left, y_left + PATCH_SIZE * mag_factor2))

                for x_left in range(b_x_start, b_x_end,
                                    PATCH_SIZE * mag_factor2):

                    if x_left in bbx_co and y_left in bby_co:
                        print("Skipping double Bounding Box %s " %
                              self.cur_wsi_path)
                        continue

                    bbx_co.append(
                        range(x_left, x_left + PATCH_SIZE * mag_factor2))

                    patch = self.wsi_image.read_region(
                        (x_left, y_left), 2, (PATCH_SIZE, PATCH_SIZE))
                    mask = self.mask_image.read_region(
                        (x_left, y_left), 2, (PATCH_SIZE, PATCH_SIZE))
                    # pdb.set_trace()
                    _std = ImageStat.Stat(patch).stddev
                    # thresholding stddev for patch extraction
                    patchidx += 1
                    if (sum(_std[:3]) / len(_std[:3])) < 15:
                        continue

                    mask_gt = np.array(mask)
                    # mask_gt = cv2.cvtColor(mask_gt, cv2.COLOR_BGR2GRAY)
                    mask_gt = cv2.cvtColor(mask_gt, cv2.COLOR_BGR2GRAY)
                    patch_array = np.array(patch)

                    white_pixel_cnt_gt = cv2.countNonZero(mask_gt)
                    if white_pixel_cnt_gt == 0:  # mask_gt does not contain tumor area
                        pass
                    #     patch_hsv = cv2.cvtColor(patch_array, cv2.COLOR_BGR2HSV)
                    #     lower_red = np.array([0, 0, 0])
                    #     upper_red = np.array([200, 200, 220])
                    #     mask_patch = cv2.inRange(patch_hsv, lower_red, upper_red)
                    #     white_pixel_cnt = cv2.countNonZero(mask_patch)

                    #     if white_pixel_cnt > ((PATCH_SIZE * PATCH_SIZE) * 0.50):
                    #         # mask = Image.fromarray(mask)
                    #         if self.negative_patch_index % 4 == 0:
                    #             patch.save(PROCESSED_PATCHES_POSITIVE_PATH + PATCH_NORMAL_PREFIX + str(PATCH_SIZE) + '_' +
                    #                     str(self.negative_patch_index)+ '.png', 'PNG')
                    #             mask.save(PROCESSED_PATCHES_POSITIVE_MASK_PATH + 'mask_' + PATCH_NORMAL_PREFIX + str(PATCH_SIZE) + '_' +
                    #             str(self.negative_patch_index)+ '.png','PNG')
                    #         self.negative_patch_index += 1
                    #         ntumoridx += 1
                    else:  # mask_gt contains tumor area
                        if white_pixel_cnt_gt >= (
                            (PATCH_SIZE * PATCH_SIZE) * 0.005):
                            patch_hsv = cv2.cvtColor(patch_array,
                                                     cv2.COLOR_BGR2HSV)
                            lower_red = np.array([0, 0, 0])
                            upper_red = np.array([200, 200, 220])
                            mask_patch = cv2.inRange(patch_hsv, lower_red,
                                                     upper_red)
                            white_pixel_cnt = cv2.countNonZero(mask_patch)
                            # if white_pixel_cnt > ((PATCH_SIZE * PATCH_SIZE) * 0.10):
                            # if self.positive_patch_index % 2 == 0:
                            patch.save(
                                PROCESSED_PATCHES_POSITIVE_PATH +
                                PATCH_TUMOR_PREFIX + str(PATCH_SIZE) + '_' +
                                str(self.positive_patch_index) + '.png', 'PNG')
                            mask.save(
                                PROCESSED_PATCHES_POSITIVE_MASK_PATH +
                                'mask_' + PATCH_TUMOR_PREFIX +
                                str(PATCH_SIZE) + '_' +
                                str(self.positive_patch_index) + '.png', 'PNG')

                            self.positive_patch_index += 1
                            tumoridx += 1

                    patch.close()
                    mask.close()

            print("Processed patches in bounding box %s " % i, "%s" % patchidx,
                  " positive: %s " % tumoridx, " negative: %s" % ntumoridx)

    def read_wsi_mask(self, wsi_path, mask_path):
        try:
            self.cur_wsi_path = wsi_path
            self.wsi_image = OpenSlide(wsi_path)
            self.mask_image = OpenSlide(mask_path)

            self.level_used = min(self.def_level,
                                  self.wsi_image.level_count - 1,
                                  self.mask_image.level_count - 1)

            self.mask_pil = self.mask_image.read_region(
                (0, 0), self.level_used,
                self.mask_image.level_dimensions[self.level_used])
            self.mask = np.array(self.mask_pil)

        except OpenSlideUnsupportedFormatError:
            print('Exception: OpenSlideUnsupportedFormatError')
            return False

        return True

    def read_wsi_normal(self, wsi_path):
        """
            # =====================================================================================
            # read WSI image and resize
            # Due to memory constraint, we use down sampled (4th level, 1/32 resolution) image
            # ======================================================================================
        """
        try:
            self.cur_wsi_path = wsi_path
            self.wsi_image = OpenSlide(wsi_path)
            self.level_used = min(self.def_level,
                                  self.wsi_image.level_count - 1)

            self.rgb_image_pil = self.wsi_image.read_region(
                (0, 0), self.level_used,
                self.wsi_image.level_dimensions[self.level_used])
            self.rgb_image = np.array(self.rgb_image_pil)

        except OpenSlideUnsupportedFormatError:
            print('Exception: OpenSlideUnsupportedFormatError')
            return False

        return True

    def read_wsi_tumor(self, wsi_path, mask_path):
        """
            # =====================================================================================
     i       # read WSI image and resize
            # Due to memory constraint, we use down sampled (4th level, 1/32 resolution) image
            # ======================================================================================
        """
        try:
            self.cur_wsi_path = wsi_path
            self.wsi_image = OpenSlide(wsi_path)
            self.mask_image = OpenSlide(mask_path)

            self.level_used = min(self.def_level,
                                  self.wsi_image.level_count - 1,
                                  self.mask_image.level_count - 1)
            # print(self.level_used)
            self.rgb_image_pil = self.wsi_image.read_region(
                (0, 0), self.level_used,
                self.wsi_image.level_dimensions[self.level_used])
            self.rgb_image = np.array(self.rgb_image_pil)

        except OpenSlideUnsupportedFormatError:
            print('Exception: OpenSlideUnsupportedFormatError')
            return False

        return True

    def find_roi_n_extract_patches_mask(self):
        mask = cv2.cvtColor(self.mask, cv2.COLOR_BGR2GRAY)
        contour_mask, bounding_boxes = self.get_image_contours_mask(
            np.array(mask), np.array(self.mask))

        # contour_mask = cv2.resize(contour_mask, (0, 0), fx=0.40, fy=0.40)
        # cv2.imshow('contour_mask', np.array(contour_mask))
        self.mask_pil.close()
        self.extract_patches_mask(bounding_boxes)
        self.wsi_image.close()
        self.mask_image.close()

    def find_roi_n_extract_patches_normal(self):
        hsv = cv2.cvtColor(self.rgb_image, cv2.COLOR_BGR2HSV)
        # [20, 20, 20]
        lower_red = np.array([20, 50, 20])
        # [255, 255, 255]
        upper_red = np.array([200, 150, 200])
        mask = cv2.inRange(hsv, lower_red, upper_red)

        # (50, 50)
        close_kernel = np.ones((25, 25), dtype=np.uint8)
        image_close = Image.fromarray(
            cv2.morphologyEx(np.array(mask), cv2.MORPH_CLOSE, close_kernel))
        # (30, 30)
        open_kernel = np.ones((30, 30), dtype=np.uint8)
        image_open = Image.fromarray(
            cv2.morphologyEx(np.array(image_close), cv2.MORPH_OPEN,
                             open_kernel))
        contour_rgb, bounding_boxes = self.get_image_contours_normal(
            np.array(image_open), self.rgb_image)

        # contour_rgb = cv2.resize(contour_rgb, (0, 0), fx=0.40, fy=0.40)
        # cv2.imshow('contour_rgb', np.array(contour_rgb))
        self.rgb_image_pil.close()
        self.extract_patches_normal(bounding_boxes)
        self.wsi_image.close()

    def find_roi_n_extract_patches_tumor(self):
        hsv = cv2.cvtColor(self.rgb_image, cv2.COLOR_BGR2HSV)
        lower_red = np.array([20, 20, 20])
        upper_red = np.array([255, 255, 255])
        mask = cv2.inRange(hsv, lower_red, upper_red)

        # (50, 50)
        close_kernel = np.ones((50, 50), dtype=np.uint8)
        image_close = Image.fromarray(
            cv2.morphologyEx(np.array(mask), cv2.MORPH_CLOSE, close_kernel))
        # (30, 30)
        open_kernel = np.ones((30, 30), dtype=np.uint8)
        image_open = Image.fromarray(
            cv2.morphologyEx(np.array(image_close), cv2.MORPH_OPEN,
                             open_kernel))
        contour_rgb, bounding_boxes = self.get_image_contours_tumor(
            np.array(image_open), self.rgb_image)
        # pdb.set_trace()
        # Image.fromarray(np.array(contour_rgb)).show()
        # pdb.set_trace()
        # contour_rgb = cv2.resize(contour_rgb, (0, 0), fx=0.40, fy=0.40)
        # cv2.imshow('contour_rgb', np.array(contour_rgb))
        self.rgb_image_pil.close()
        self.extract_patches_tumor(bounding_boxes)
        self.wsi_image.close()
        self.mask_image.close()

    @staticmethod
    def get_image_contours_mask(cont_img, mask_img):
        contours, _ = cv2.findContours(cont_img, cv2.RETR_EXTERNAL,
                                       cv2.CHAIN_APPROX_SIMPLE)
        bounding_boxes = [cv2.boundingRect(c) for c in contours]
        contours_mask_image_array = np.array(mask_img)
        line_color = (255, 0, 0)  # blue color code
        cv2.drawContours(contours_mask_image_array, contours, -1, line_color,
                         1)
        return contours_mask_image_array, bounding_boxes

    @staticmethod
    def get_image_contours_normal(cont_img, rgb_image):
        contours, _ = cv2.findContours(cont_img, cv2.RETR_EXTERNAL,
                                       cv2.CHAIN_APPROX_SIMPLE)
        bounding_boxes = [cv2.boundingRect(c) for c in contours]
        contours_rgb_image_array = np.array(rgb_image)
        line_color = (255, 0, 0)  # blue color code
        cv2.drawContours(contours_rgb_image_array, contours, -1, line_color, 3)
        return contours_rgb_image_array, bounding_boxes

    @staticmethod
    def get_image_contours_tumor(cont_img, rgb_image):
        contours, _ = cv2.findContours(cont_img, cv2.RETR_EXTERNAL,
                                       cv2.CHAIN_APPROX_SIMPLE)
        bounding_boxes = [cv2.boundingRect(c) for c in contours]
        contours_rgb_image_array = np.array(rgb_image)

        line_color = (255, 0, 0)  # blue color code
        cv2.drawContours(contours_rgb_image_array, contours, -1, line_color, 3)
        # cv2.drawContours(mask_image, contours_mask, -1, line_color, 3)
        return contours_rgb_image_array, bounding_boxes

    def wait(self):
        self.key = cv2.waitKey(0) & 0xFF
        print('key: %d' % self.key)

        if self.key == 27:  # escape
            return False
        elif self.key == 81:  # <- (prev)
            self.index -= 1
            if self.index < 0:
                self.index = len(self.wsi_paths) - 1
        elif self.key == 83:  # -> (next)
            self.index += 1
            if self.index >= len(self.wsi_paths):
                self.index = 0

        return True
示例#26
0
class WSIData:
    def __init__(
        self,
        tif_path,
        centre,
        is_excluded,
        resource_group,
        label_tif_path=None,
        label_xml_path=None,
        patient=None,
    ):
        self._wsi_slide = OpenSlide(str(tif_path))
        self._tif_path = tif_path
        self._is_excluded = is_excluded
        self._label_tif_path = label_tif_path
        self._label_xml_path = label_xml_path
        self._label_slide = None
        self._patient = patient
        self._resource_group = resource_group

        if not isinstance(centre, _StainNormalizer.Centre):
            raise TypeError('centre must be an instance of {}.Centre.'.format(
                self.__class__.__name__))
        self._centre = centre
        self._name = tif_path.stem

    def _get_label_slide(self):
        if self._label_slide is None and self._label_tif_path is not None:
            self._label_slide = (self._label_tif_path
                                 and OpenSlide(str(self._label_tif_path)))
        return self._label_slide

    @staticmethod
    def _threshold_sv(img_np):
        '''Performs thresholding on the WSI image to extract the tissue region.

        RGB image is converted to HSV color space. The H and S channels are
        then thresholded via Otsu's Threshold, then combined via bitwise AND.
        Morphological transormation was applied by removing small holes and
        regions, and was filtered using median blur.

        Parameters
        ----------
        img_np: np.uint8[H, W, 3] np.array
            RGB WSI as an np.array image

        Returns
        -------
        bool[H, W] np.array
            ROI mask of the WSI.
        '''
        img_hsv = rgb2hsv(img_np)
        # channel_h = img_hsv[:, :, 0]
        channel_s = img_hsv[:, :, 1]
        channel_v = img_hsv[:, :, 2]

        # thresh_h = threshold_otsu(channel_h)
        thresh_s = threshold_otsu(channel_s)
        thresh_v = threshold_otsu(channel_v)

        # binary_h = channel_h > thresh_h
        binary_s = channel_s > min(thresh_s, 0.1)
        binary_v = channel_s > min(thresh_v, 0.2)

        # binary = np.bitwise_and(binary_h, binary_s)
        binary = np.bitwise_and(binary_s, binary_v)
        binary = morphology.remove_small_objects(binary, _SMALL_OBJECT_AREA)
        binary = morphology.remove_small_holes(binary, _SMALL_HOLE_AREA)
        binary = median(binary, morphology.disk(_MEDIAN_DISK))

        return binary.astype(bool)

    @staticmethod
    def _threshold_hs(img_np):
        '''Performs thresholding on the WSI image to extract the tissue region.

        RGB image is converted to HSV color space. The H and S channels are
        then thresholded via Otsu's Threshold, then combined via bitwise AND.
        Morphological transormation was applied by removing small holes and
        regions, and was filtered using median blur.

        Parameters
        ----------
        img_np: np.uint8[H, W, 3] np.array
            RGB WSI as an np.array image

        Returns
        -------
        bool[H, W] np.array
            ROI mask of the WSI.
        '''
        img_hsv = rgb2hsv(img_np)
        channel_h = img_hsv[:, :, 0]
        channel_s = img_hsv[:, :, 1]

        thresh_h = threshold_otsu(channel_h)
        thresh_s = threshold_otsu(channel_s)

        binary_h = channel_h > thresh_h
        binary_s = channel_s > thresh_s

        binary = np.bitwise_and(binary_h, binary_s)
        binary = morphology.remove_small_objects(binary, _SMALL_OBJECT_AREA)
        binary = morphology.remove_small_holes(binary, _SMALL_HOLE_AREA)
        binary = median(binary, morphology.disk(_MEDIAN_DISK))

        return binary.astype(bool)

    @staticmethod
    def _threshold_gray(img_np):
        binary = np.full(img_np.shape[:2], False)
        binary[np.where(rgb2gray(img_np) < 0.8)] = True
        binary = morphology.remove_small_objects(binary, _SMALL_OBJECT_AREA)
        binary = morphology.remove_small_holes(binary, _SMALL_HOLE_AREA)
        binary = median(binary, morphology.disk(_MEDIAN_DISK))
        return binary

    def _roi_threshold(self, img_np):
        # return self._threshold_sv(img_np)
        # return self._threshold_gray(img_np)
        return self._threshold_hs(img_np)

    def _mark_metastases_regions_in_label(self, blank_mask_np, label_np):
        '''Marks the blank mask using the given label image.

        This is to be overriden due to the different nature of the labels
        from the CAMELYON 16 and 17 data set.

        Parameters
        ----------
        blank_mask_np: bool[H, W] np.array
            Mask to serve as the new label

        label_np: np.uint[H, W] np.array
            Gray scale image with the mask values.

        Returns
        -------
        bool[H, W] np.array
            Mask to serve as the new label
        '''
        blank_mask_np[np.where(label_np == 1)] = True
        blank_mask_np[np.where(label_np == 2)] = False

    @property
    def name(self):
        return self._name

    @property
    def centre(self):
        return self._centre

    @property
    def label_xml_path(self):
        return self._label_xml_path

    @property
    def tif_path(self):
        return self._tif_path

    @property
    def is_excluded(self):
        return self._is_excluded

    @property
    def patient(self):
        return self._patient

    @property
    def resource_group(self):
        return self._resource_group

    def get_full_wsi_image(self, level):
        '''Returns the whole WSI as an RGB image.

        Parameters
        ----------
        level: int
            Level downsample to read the WSI. (values: 0..8)

        Returns
        -------
        np.uint8[H, W, 3] np.array
            Whole WSI RGB image.
        '''
        wsi_dim = self._wsi_slide.level_dimensions[level]
        wsi_img = self._wsi_slide.read_region((0, 0), level, wsi_dim)
        return np.array(wsi_img.convert('RGB'))

    def get_level_dimension(self, level):
        '''Returns the dimensions of the WSI for a given level.

        Parameters
        ----------
        level: int
            Level downsample to get dimension. (values: 0..8)

        Returns
        -------
        (width: int, height: int)
            Dimension of the WSI.
        '''
        return self._wsi_slide.level_dimensions[level]

    def get_level_downsample(self, level):
        '''Returns the dimensions of the WSI for a given level.

        Parameters
        ----------
        level: int
            Level downsample to get dimension. (values: 0..8)

        Returns
        -------
        (width: int, height: int)
            Dimension of the WSI.
        '''
        return self._wsi_slide.level_downsamples[level]

    def get_roi_mask(self, level):
        '''Returns the ROI of the WSI i.e. the tissue region.

        Parameters
        ----------
        level: int
            Level downsample to read the WSI. (values: 0..8)

        Returns
        -------
        bool[H, W] np.array
            Tissue ROI mask of the WSI.
        '''
        wsi_img = self.get_full_wsi_image(level)
        wsi_img_np = np.array(wsi_img, dtype=np.uint8)
        roi_mask = self._roi_threshold(wsi_img_np)
        metastases_roi_mask, label_np = self._get_metastases_mask(level)
        roi_mask[np.where(metastases_roi_mask)] = True
        roi_mask[np.where(label_np == 3)] = False
        return roi_mask

    def _get_metastases_mask(self, level):
        '''Get metastases ROI of the WSI.

        Parameters
        ----------
        level: int
            Level downsample to read the WSI. (values: 0..8)

        Returns
        -------
        bool[H, W] np.array
            Metastases ROI mask of the WSI.
        '''
        wsi_w, wsi_h = self._wsi_slide.level_dimensions[level]
        metastases_mask = np.full((wsi_h, wsi_w), False)

        label_slide = self._get_label_slide()
        if label_slide is not None:
            label_dim = label_slide.level_dimensions[level]
            label_img = label_slide.read_region((0, 0), level, label_dim)
            label_np = np.array(label_img.convert('L'))
            label_w, label_h = label_dim
            label_np = label_np[:min(wsi_h, label_h), :min(wsi_w, label_w)]
            self._mark_metastases_regions_in_label(metastases_mask, label_np)
        else:
            label_np = np.full((wsi_h, wsi_w), 0)

        return metastases_mask, label_np

    def get_metastases_mask(self, level):
        return self._get_metastases_mask(level)[0]

    def read_region_and_label(self, coordinates, level, dimension):
        '''Extracts a RGB region from the WSI.

        The given coordinates will be the center of the region, with the
        given dimension.

        Parameters
        ----------
        coordinates: (x: int, y: int)
            Coordinates in the WSI at level 0. Upper-left of the region.

        level: int
            Level downsample to read the WSI. (values: 0..8)

        dimension: (w: int, h: int)
            Dimension of the region to extract.

        Returns
        -------
        np.uint8[H, W, 3] np.array
            RGB region from WSI.

        bool[H, W] np.array
            Label mask for the given region.
        '''
        scale_factor = 2**level
        w, h = dimension
        w_0, h_0 = w * scale_factor, h * scale_factor
        x, y = coordinates

        ## coordinate is at the center of the patches
        # x, y = x - (w * 2**level // 2), y - (h * 2**level // 2)
        args = (x, y), 0, (w_0, h_0)
        patch_img = self._wsi_slide.read_region(*args)
        patch_np = np.array(patch_img.convert('RGB'))
        patch_np = resize(patch_np, dimension, mode='reflect')

        metastases_np = np.full((w, h), False)

        label_slide = self._get_label_slide()
        if label_slide is not None:
            label_img = label_slide.read_region(*args)
            label_np = np.array(label_img.convert('L'))
            label_np = label_np[::scale_factor, ::scale_factor]
            self._mark_metastases_regions_in_label(metastases_np, label_np)

        return patch_np, metastases_np

    def _get_roi_patch_positions(self,
                                 level,
                                 get_roi,
                                 stride=256,
                                 patch_side=513,
                                 ds_level=5):
        '''Positions returned are in level 0.'''
        assert ds_level > level, 'level must be less than ds_level'

        width, height = self._wsi_slide.level_dimensions[level]
        ds_roi = get_roi(ds_level)
        ds = 2**(ds_level - level)
        upscale_factor = 2**level

        for y in range(0, math.ceil(height / stride) * stride, stride):
            for x in range(0, math.ceil(width / stride) * stride, stride):
                y_ds = y // ds
                x_ds = x // ds
                y_ds_end = (y + stride) // ds
                x_ds_end = (x + stride) // ds
                roi_patch = ds_roi[y_ds:y_ds_end, x_ds:x_ds_end]

                if (np.sum(roi_patch) / roi_patch.size >
                        _ROI_PATCH_COVER_PERCENTAGE):
                    yield x * upscale_factor, y * upscale_factor

    def get_roi_patch_positions(self,
                                level,
                                stride=256,
                                patch_side=513,
                                ds_level=5,
                                force_not_excluded=False):
        if not self._is_excluded or force_not_excluded:
            get_roi = self.get_roi_mask
        else:
            get_roi = self.get_metastases_mask

        return self._get_roi_patch_positions(
            level,
            get_roi,
            stride,
            patch_side,
            ds_level,
        )

    def close(self):
        self._wsi_slide.close()
        self._label_slide is not None and self._label_slide.close()

    def __repr__(self):
        return '<Centre: {}, Name: {}>'.format(self._centre, self._name)
示例#27
0
def predict_wsi(model, global_fixed, slide_path):
    # size_g, size_p = (244, 244), (244, 244)
    # size_g, size_p = (1008, 1008), (1008, 1008)
    # n_class = 2
    # sub_batch_size = 1
    def predict(image_as_tensor,
                size_g=(244, 244),
                size_p=(244, 244),
                n_class=2):
        images_glb = resize(image_as_tensor, size_g)
        scores = [
            np.zeros((1, n_class, image_as_tensor[i].size[1],
                      image_as_tensor[i].size[0]))
            for i in range(len(image_as_tensor))
        ]

        images_glb = images_transform(images_glb)

        patches, coordinates, templates, sizes, ratios = global2patch(
            image_as_tensor, size_p)

        predicted_ensembles = [
            np.zeros((len(coordinates[i]), n_class, size_p[0], size_p[1]))
            for i in range(len(image_as_tensor))
        ]

        for i in range(len(image_as_tensor)):
            j = 0
            while j < len(coordinates[i]):
                patches_var = images_transform(patches[i][j:j +
                                                          1])  # b, c, h, w

                fm_patches, _ = model.module.collect_local_fm(
                    images_glb[i:i + 1],
                    patches_var,
                    ratios[i],
                    coordinates[i],
                    [j, j + 1],
                    len(image_as_tensor),
                    global_model=global_fixed,
                    template=templates[i],
                    n_patch_all=len(coordinates[i]),
                )
                j += 1

        _, fm_global = model.forward(images_glb,
                                     None,
                                     None,
                                     None,
                                     mode=PhaseMode.GlobalFromLocal)

        for i in range(len(image_as_tensor)):
            j = 0
            # while j < n ** 2:
            while j < len(coordinates[i]):
                fl = fm_patches[i][j:j + 1].cuda()
                fg = crop_global(fm_global[i:i + 1], coordinates[i][j:j + 1],
                                 ratios[i])[0]

                fg = F.interpolate(fg, size=fl.size()[2:], mode="bilinear")
                output_ensembles = model.module.ensemble(
                    fl, fg)  # include cordinates
                # output_ensembles = F.interpolate(model.module.ensemble(fl, fg), size_p, **model.module._up_kwargs)

                # ensemble predictions
                predicted_ensembles[i][j:j + output_ensembles.size()[0]] += (
                    F.interpolate(
                        output_ensembles,
                        size=size_p,
                        mode="nearest",
                    ).data.cpu().numpy())
                j += 1

            scores[i] += np.rot90(
                np.array(
                    patch2global(
                        predicted_ensembles[i:i + 1],
                        n_class,
                        sizes[i:i + 1],
                        coordinates[i:i + 1],
                        size_p,
                    )),
                k=0,
                axes=(3, 2),
            )

        return [score.argmax(1)[0] for score in scores]

    slide = OpenSlide(slide_path)
    w, h = slide.level_dimensions[2]
    img = slide.read_region((0, 0), 2, (w, h))
    slide.close()
    img.convert('RGB').save('/tmp/temp.jpg')
    slide = ImageSlide('/tmp/temp.jpg')

    dz = deepzoom.DeepZoomGenerator(slide, tile_size=1024, overlap=0)

    cols, rows = dz.level_tiles[-1]
    out = np.zeros((rows * 1024, cols * 1024), dtype=np.uint8)

    for row in range(rows):
        for col in range(cols):
            tile = dz.get_tile(dz.level_count - 1, (col, row))  # col, row
            tile_coors = dz.get_tile_coordinates(dz.level_count - 1,
                                                 (col, row))
            left, top = tile_coors[0]
            t_w, t_h = tile_coors[2]
            if tile.size != (1024, 1024):
                tile = add_extra_pixels(tile, expected_shape=(1024, 1024))

            tile = np.array(tile)
            processed = apply_filters(tile)

            pred = predict([Image.fromarray(processed)])
            pil_pred = pred[0].astype(np.uint8)

            newmask = remove_mask_overlay_background(processed, pil_pred)

            # applied_mask = apply_mask(tile, newmask)
            applied_mask = newmask
            out[top:top + t_h, left:left + t_w] = applied_mask[:t_h, :t_w]

    return out[:h, :w]
示例#28
0
class OpenSlideStore(Mapping):
    """Wraps an OpenSlide object as a multiscale Zarr Store.

    Parameters
    ----------
    path: str
        The file to open with OpenSlide.
    tilesize: int
        Desired "chunk" size for zarr store.
    """
    def __init__(self, path: str, tilesize: int = 512):
        self._slide = OpenSlide(path)
        self._tilesize = tilesize
        self._store = create_meta_store(self._slide, tilesize)

    def __getitem__(self, key: str):
        if key in self._store:
            # key is for metadata
            return self._store[key]

        # key should now be a path to an array chunk
        # e.g '3/4.5.0' -> '<level>/<chunk_key>'
        try:
            x, y, level = _parse_chunk_path(key)
            location = self._ref_pos(x, y, level)
            size = (self._tilesize, self._tilesize)
            tile = self._slide.read_region(location, level, size)
        except ArgumentError as err:
            # Can occur if trying to read a closed slide
            raise err
        except:
            # TODO: probably need better error handling.
            # If anything goes wrong, we just signal the chunk
            # is missing from the store.
            raise KeyError(key)

        return np.array(tile).tobytes()

    def __contains__(self, key: str):
        return key in self._store

    def __eq__(self, other):
        return (isinstance(other, OpenSlideStore)
                and self._slide._filename == other._slide._filename)

    def __iter__(self):
        return iter(self.keys())

    def __len__(self):
        return sum(1 for _ in self)

    def __enter__(self):
        return self

    def __exit__(self, *args):
        self.close()

    def _ref_pos(self, x: int, y: int, level: int):
        dsample = self._slide.level_downsamples[level]
        xref = int(x * dsample * self._tilesize)
        yref = int(y * dsample * self._tilesize)
        return xref, yref

    def keys(self):
        return self._store.keys()

    def close(self):
        self._slide.close()
示例#29
0
class Opener:
    def __init__(self, path):
        self.warning = ''
        self.path = path
        self.reader = None
        self.tilesize = 1024
        self.ext = check_ext(path)
        self.default_dtype = np.uint16

        if self.ext == '.ome.tif' or self.ext == '.ome.tiff':
            self.io = TiffFile(self.path, is_ome=False)
            self.group = zarr.open(self.io.series[0].aszarr())
            self.reader = 'tifffile'
            self.ome_version = self._get_ome_version()
            print("OME ", self.ome_version)
            num_channels = self.get_shape()[0]
            tile_0 = self.get_tifffile_tile(num_channels, 0, 0, 0, 0, 1024)
            if tile_0 is not None:
                self.default_dtype = tile_0.dtype

            if (num_channels == 3 and tile_0.dtype == 'uint8'):
                self.rgba = True
                self.rgba_type = '3 channel'
            elif (num_channels == 1 and tile_0.dtype == 'uint8'):
                self.rgba = True
                self.rgba_type = '1 channel'
            else:
                self.rgba = False
                self.rgba_type = None

            print("RGB ", self.rgba)
            print("RGB type ", self.rgba_type)

        elif self.ext == '.svs':
            self.io = OpenSlide(self.path)
            self.dz = DeepZoomGenerator(self.io,
                                        tile_size=1024,
                                        overlap=0,
                                        limit_bounds=True)
            self.reader = 'openslide'
            self.rgba = True
            self.rgba_type = None
            self.default_dtype = np.uint8

            print("RGB ", self.rgba)
            print("RGB type ", self.rgba_type)

        else:
            self.reader = None

    def _get_ome_version(self):
        try:
            software = self.io.pages[0].tags[305].value
            sub_ifds = self.io.pages[0].tags[330].value
            if "Faas" in software or sub_ifds is None:
                return 5

            m = re.search('OME\\sBio-Formats\\s(\\d+)\\.\\d+\\.\\d+', software)
            if m is None:
                return 5
            return int(m.group(1))
        except Exception as e:
            print(e)
            return 5

    def load_xml_markers(self):
        if self.ext == '.ome.tif' or self.ext == '.ome.tiff':
            try:
                metadata = ome_types.from_tiff(self.path)
            except Exception as e:
                return []

            if not metadata or not metadata.images or not metadata.images[0]:
                return []

            metadata_pixels = metadata.images[0].pixels
            if not metadata_pixels or not metadata_pixels.channels:
                return []

            return [c.name for c in metadata_pixels.channels]
        else:
            return []

    def close(self):
        self.io.close()

    def is_rgba(self, rgba_type=None):
        if rgba_type is None:
            return self.rgba
        else:
            return self.rgba and rgba_type == self.rgba_type

    def get_level_tiles(self, level, tile_size):
        if self.reader == 'tifffile':

            # Negative indexing to support shape len 3 or len 2
            ny = int(np.ceil(self.group[level].shape[-2] / tile_size))
            nx = int(np.ceil(self.group[level].shape[-1] / tile_size))
            return (nx, ny)
        elif self.reader == 'openslide':
            l = self.dz.level_count - 1 - level
            return self.dz.level_tiles[l]

    def get_shape(self):
        def parse_shape(shape):
            if len(shape) >= 3:
                (num_channels, shape_y, shape_x) = shape[-3:]
            else:
                (shape_y, shape_x) = shape
                num_channels = 1

            return (num_channels, shape_x, shape_y)

        if self.reader == 'tifffile':

            (num_channels, shape_x, shape_y) = parse_shape(self.group[0].shape)
            all_levels = [parse_shape(v.shape) for v in self.group.values()]
            num_levels = len(
                [shape for shape in all_levels if max(shape[1:]) > 512])
            return (num_channels, num_levels, shape_x, shape_y)

        elif self.reader == 'openslide':

            (width, height) = self.io.dimensions

            def has_one_tile(counts):
                return max(counts) == 1

            small_levels = list(filter(has_one_tile, self.dz.level_tiles))
            level_count = self.dz.level_count - len(small_levels) + 1

            return (3, level_count, width, height)

    def read_tiles(self, level, channel_number, tx, ty, tilesize):
        ix = tx * tilesize
        iy = ty * tilesize

        num_channels = self.get_shape()[0]
        try:
            if num_channels == 1:
                tile = self.group[level][iy:iy + tilesize, ix:ix + tilesize]
            else:
                tile = self.group[level][channel_number, iy:iy + tilesize,
                                         ix:ix + tilesize]
            tile = np.squeeze(tile)
            return tile
        except Exception as e:
            G['logger'].error(e)
            return None

    def get_tifffile_tile(self,
                          num_channels,
                          level,
                          tx,
                          ty,
                          channel_number,
                          tilesize=1024):

        if self.reader == 'tifffile':

            tile = self.read_tiles(level, channel_number, tx, ty, tilesize)

            if tile is None:
                return np.zeros((tilesize, tilesize), dtype=self.default_dtype)

            return tile

    def get_tile(self, num_channels, level, tx, ty, channel_number, fmt=None):

        if self.reader == 'tifffile':

            if self.is_rgba('3 channel'):
                tile_0 = self.get_tifffile_tile(num_channels, level, tx, ty, 0,
                                                1024)
                tile_1 = self.get_tifffile_tile(num_channels, level, tx, ty, 1,
                                                1024)
                tile_2 = self.get_tifffile_tile(num_channels, level, tx, ty, 2,
                                                1024)
                tile = np.zeros((tile_0.shape[0], tile_0.shape[1], 3),
                                dtype=np.uint8)
                tile[:, :, 0] = tile_0
                tile[:, :, 1] = tile_1
                tile[:, :, 2] = tile_2
                _format = 'I;8'
            else:
                tile = self.get_tifffile_tile(num_channels, level, tx, ty,
                                              channel_number, 1024)
                _format = fmt if fmt else 'I;16'

                if (_format == 'RGBA' and tile.dtype != np.uint32):
                    tile = tile.astype(np.uint32)

                if (_format == 'I;16' and tile.dtype != np.uint16):
                    if tile.dtype == np.uint8:
                        tile = 255 * tile.astype(np.uint16)
                    else:
                        # TODO: real support for uint32, signed values, and floats
                        tile = np.clip(tile, 0, 65535).astype(np.uint16)

            return Image.fromarray(tile, _format)

        elif self.reader == 'openslide':
            l = self.dz.level_count - 1 - level
            img = self.dz.get_tile(l, (tx, ty))
            return img

    def save_mask_tiles(self, filename, mask_params, logger, tile_size, level,
                        tx, ty):

        should_skip_tile = {}

        def get_empty_path(path):
            basename = os.path.splitext(path)[0]
            return pathlib.Path(f'{basename}_tmp.txt')

        for image_params in mask_params['images']:

            output_file = str(image_params['out_path'] / filename)
            path_exists = os.path.exists(output_file) or os.path.exists(
                get_empty_path(output_file))
            should_skip = path_exists and image_params['is_up_to_date']
            should_skip_tile[output_file] = should_skip

        if all(should_skip_tile.values()):
            logger.warning(f'Not saving tile level {level} ty {ty} tx {tx}')
            logger.warning(
                f'Every mask {filename} exists with same rendering settings')
            return

        if self.reader == 'tifffile':
            num_channels = self.get_shape()[0]
            tile = self.get_tifffile_tile(num_channels, level, tx, ty, 0,
                                          tile_size)

            for image_params in mask_params['images']:

                output_file = str(image_params['out_path'] / filename)
                if should_skip_tile[output_file]:
                    continue

                target = np.zeros(tile.shape + (4, ), np.uint8)
                skip_empty_tile = True

                for channel in image_params['settings']['channels']:
                    rgba_color = [
                        int(255 * i)
                        for i in (colors.to_rgba(channel['color']))
                    ]
                    ids = channel['ids']

                    if len(ids) > 0:
                        bool_tile = np.isin(tile, ids)
                        # Signal that we must actually save the image
                        if not skip_empty_tile or np.any(bool_tile):
                            skip_empty_tile = False
                        target[bool_tile] = rgba_color
                    else:
                        # Note, any channel without ids to map will override all others
                        target = colorize_mask(target, tile)
                        skip_empty_tile = False

                if skip_empty_tile:
                    empty_file = get_empty_path(output_file)
                    if not os.path.exists(empty_file):
                        with open(empty_file, 'w') as fp:
                            pass
                else:
                    img = Image.frombytes('RGBA', target.T.shape[1:],
                                          target.tobytes())
                    img.save(output_file, quality=85)

    def save_tile(self, output_file, settings, tile_size, level, tx, ty):
        if self.reader == 'tifffile' and self.is_rgba('3 channel'):

            num_channels = self.get_shape()[0]
            tile_0 = self.get_tifffile_tile(num_channels, level, tx, ty, 0,
                                            tile_size)
            tile_1 = self.get_tifffile_tile(num_channels, level, tx, ty, 1,
                                            tile_size)
            tile_2 = self.get_tifffile_tile(num_channels, level, tx, ty, 2,
                                            tile_size)
            tile = np.zeros((tile_0.shape[0], tile_0.shape[1], 3),
                            dtype=np.uint8)
            tile[:, :, 0] = tile_0
            tile[:, :, 1] = tile_1
            tile[:, :, 2] = tile_2

            img = Image.fromarray(tile, 'RGB')
            img.save(output_file, quality=85)

        elif self.reader == 'tifffile' and self.is_rgba('1 channel'):

            num_channels = self.get_shape()[0]
            tile = self.get_tifffile_tile(num_channels, level, tx, ty, 0,
                                          tile_size)

            img = Image.fromarray(tile, 'RGB')
            img.save(output_file, quality=85)

        elif self.reader == 'tifffile':
            target = None
            for i, (marker, color, start, end) in enumerate(
                    zip(settings['Channel Number'], settings['Color'],
                        settings['Low'], settings['High'])):
                num_channels = self.get_shape()[0]
                tile = self.get_tifffile_tile(num_channels, level, tx, ty,
                                              int(marker), tile_size)

                if (tile.dtype != np.uint16):
                    if tile.dtype == np.uint8:
                        tile = 255 * tile.astype(np.uint16)
                    else:
                        tile = tile.astype(np.uint16)

                if i == 0 or target is None:
                    target = np.zeros(tile.shape + (3, ), np.float32)

                composite_channel(target, tile, colors.to_rgb(color),
                                  float(start), float(end))

            if target is not None:
                np.clip(target, 0, 1, out=target)
                target_u8 = (target * 255).astype(np.uint8)
                img = Image.frombytes('RGB', target.T.shape[1:],
                                      target_u8.tobytes())
                img.save(output_file, quality=85)

        elif self.reader == 'openslide':
            l = self.dz.level_count - 1 - level
            img = self.dz.get_tile(l, (tx, ty))
            img.save(output_file, quality=85)
示例#30
0
                                tumor_mask = Image.fromarray(mask_np)
                                mask_save_path = save_path + "mask" + "/mask_" + os.path.basename(wsi_path) + "_level_0" + "_x_" + str(x_cut_id) + "_y_" + str(y_cut_id) +".png"
                                meta_dict["mask_path"] = mask_save_path
                                tumor_mask.save(mask_save_path)

                                img_pil = Image.fromarray(np_img[:,:,0:3])
                                img_save_path = save_path + "patch" + "/img_" + os.path.basename(wsi_path) + "_level_0" + "_x_" + str(x_cut_id) + "_y_" + str(y_cut_id) +".png"
                                meta_dict["img_path"] = img_save_path
                                img_pil.save(img_save_path)
                                meta_data_array["patches"].append(meta_dict)
            # pdb.set_trace()                    
            print(len(meta_data_array["patches"]))
        if label == "normal":
            patch_json_path = save_path + "json_fgmask"  + "/" + str(num) + "_json_" + os.path.basename(wsi_path) +  "_level_0" + '.json'
        else:
            patch_json_path = save_path + "json_fgmask" + "/" + str(num) +"_json_" + os.path.basename(wsi_path) +  "_level_0" + '.json'
        # save_json_path = os.path.join(json_base_path, wsi_name + "_level_0" + '.json')
        # if not os.path.exists(patch_json_path):
        #     os.makedirs(patch_json_path)

        if len(meta_data_array["patches"]) != 0 :
            with io.open(patch_json_path, 'w', encoding='utf8') as outfile:
                json_patch = json.dumps(meta_data_array)
                outfile.write(str(json_patch))
            slide.close()
            print('INFO: json save done!')
        else:
            print("pass")

class _SlideTest(object):
    def setUp(self):
        self.osr = OpenSlide(file_path(self.FILENAME))

    def tearDown(self):
        self.osr.close()
示例#32
0
class WSI(object):
    """
        # ================================
        # Class to annotate WSIs with ROIs
        # ================================

    """
    index = 0
    negative_patch_index = 118456
    positive_patch_index = 2230
    wsi_paths = []
    mask_paths = []
    def_level = 7
    key = 0

    def extract_patches_mask(self, bounding_boxes):
        """
        Extract positive patches targeting annotated tumor region

        Save extracted patches to desk as .png image files

        :param bounding_boxes: list of bounding boxes corresponds to tumor regions
        :return:

        """
        mag_factor = pow(2, self.level_used)

        print('No. of ROIs to extract patches from: %d' % len(bounding_boxes))

        for i, bounding_box in enumerate(bounding_boxes):
            b_x_start = int(bounding_box[0]) * mag_factor
            b_y_start = int(bounding_box[1]) * mag_factor
            b_x_end = (int(bounding_box[0]) + int(bounding_box[2])) * mag_factor
            b_y_end = (int(bounding_box[1]) + int(bounding_box[3])) * mag_factor
            X = np.random.random_integers(b_x_start, high=b_x_end, size=500)
            Y = np.random.random_integers(b_y_start, high=b_y_end, size=500)
            # X = np.arange(b_x_start, b_x_end-256, 5)
            # Y = np.arange(b_y_start, b_y_end-256, 5)

            for x, y in zip(X, Y):
                mask = self.mask_image.read_region((x, y), 0, (PATCH_SIZE, PATCH_SIZE))
                mask_gt = np.array(mask)
                mask_gt = cv2.cvtColor(mask_gt, cv2.COLOR_BGR2GRAY)

                white_pixel_cnt_gt = cv2.countNonZero(mask_gt)

                if white_pixel_cnt_gt > ((PATCH_SIZE * PATCH_SIZE) * 0.90):
                    # mask = Image.fromarray(mask)
                    patch = self.wsi_image.read_region((x, y), 0, (PATCH_SIZE, PATCH_SIZE))
                    patch.save(PROCESSED_PATCHES_FROM_USE_MASK_POSITIVE_PATH + PATCH_TUMOR_PREFIX +
                               str(self.positive_patch_index), 'PNG')
                    self.positive_patch_index += 1
                    patch.close()

                mask.close()

    def extract_patches_normal(self, bounding_boxes):
        """
            Extract negative patches from Normal WSIs

            Save extracted patches to desk as .png image files

            :param bounding_boxes: list of bounding boxes corresponds to detected ROIs
            :return:

        """
        mag_factor = pow(2, self.level_used)

        print('No. of ROIs to extract patches from: %d' % len(bounding_boxes))

        for i, bounding_box in enumerate(bounding_boxes):
            b_x_start = int(bounding_box[0]) * mag_factor
            b_y_start = int(bounding_box[1]) * mag_factor
            b_x_end = (int(bounding_box[0]) + int(bounding_box[2])) * mag_factor
            b_y_end = (int(bounding_box[1]) + int(bounding_box[3])) * mag_factor
            X = np.random.random_integers(b_x_start, high=b_x_end, size=500)
            Y = np.random.random_integers(b_y_start, high=b_y_end, size=500)
            # X = np.arange(b_x_start, b_x_end-256, 5)
            # Y = np.arange(b_y_start, b_y_end-256, 5)

            for x, y in zip(X, Y):
                patch = self.wsi_image.read_region((x, y), 0, (PATCH_SIZE, PATCH_SIZE))
                patch_array = np.array(patch)

                patch_hsv = cv2.cvtColor(patch_array, cv2.COLOR_BGR2HSV)
                # [20, 20, 20]
                lower_red = np.array([20, 20, 20])
                # [255, 255, 255]
                upper_red = np.array([200, 200, 200])
                mask = cv2.inRange(patch_hsv, lower_red, upper_red)
                white_pixel_cnt = cv2.countNonZero(mask)

                if white_pixel_cnt > ((PATCH_SIZE * PATCH_SIZE) * 0.50):
                    # mask = Image.fromarray(mask)
                    patch.save(PROCESSED_PATCHES_NORMAL_NEGATIVE_PATH + PATCH_NORMAL_PREFIX +
                               str(self.negative_patch_index), 'PNG')
                    # mask.save(PROCESSED_PATCHES_NORMAL_PATH + PATCH_NORMAL_PREFIX + str(self.patch_index),
                    #           'PNG')
                    self.negative_patch_index += 1

                patch.close()

    def extract_patches_tumor(self, bounding_boxes):
        """
            Extract both, negative patches from Normal area and positive patches from Tumor area

            Save extracted patches to desk as .png image files

            :param bounding_boxes: list of bounding boxes corresponds to detected ROIs
            :return:
            
        """
        mag_factor = pow(2, self.level_used)

        print('No. of ROIs to extract patches from: %d' % len(bounding_boxes))

        for i, bounding_box in enumerate(bounding_boxes):
            b_x_start = int(bounding_box[0]) * mag_factor
            b_y_start = int(bounding_box[1]) * mag_factor
            b_x_end = (int(bounding_box[0]) + int(bounding_box[2])) * mag_factor
            b_y_end = (int(bounding_box[1]) + int(bounding_box[3])) * mag_factor
            X = np.random.random_integers(b_x_start, high=b_x_end, size=500)
            Y = np.random.random_integers(b_y_start, high=b_y_end, size=500)
            # X = np.arange(b_x_start, b_x_end-256, 5)
            # Y = np.arange(b_y_start, b_y_end-256, 5)

            for x, y in zip(X, Y):
                patch = self.wsi_image.read_region((x, y), 0, (PATCH_SIZE, PATCH_SIZE))
                mask = self.mask_image.read_region((x, y), 0, (PATCH_SIZE, PATCH_SIZE))
                mask_gt = np.array(mask)
                # mask_gt = cv2.cvtColor(mask_gt, cv2.COLOR_BGR2GRAY)
                mask_gt = cv2.cvtColor(mask_gt, cv2.COLOR_BGR2GRAY)
                patch_array = np.array(patch)

                white_pixel_cnt_gt = cv2.countNonZero(mask_gt)

                if white_pixel_cnt_gt == 0:  # mask_gt does not contain tumor area
                    patch_hsv = cv2.cvtColor(patch_array, cv2.COLOR_BGR2HSV)
                    lower_red = np.array([20, 20, 20])
                    upper_red = np.array([200, 200, 200])
                    mask_patch = cv2.inRange(patch_hsv, lower_red, upper_red)
                    white_pixel_cnt = cv2.countNonZero(mask_patch)

                    if white_pixel_cnt > ((PATCH_SIZE * PATCH_SIZE) * 0.50):
                        # mask = Image.fromarray(mask)
                        patch.save(PROCESSED_PATCHES_TUMOR_NEGATIVE_PATH + PATCH_NORMAL_PREFIX +
                                   str(self.negative_patch_index), 'PNG')
                        # mask.save(PROCESSED_PATCHES_NORMAL_PATH + PATCH_NORMAL_PREFIX + str(self.patch_index),
                        #           'PNG')
                        self.negative_patch_index += 1
                else:  # mask_gt contains tumor area
                    if white_pixel_cnt_gt >= ((PATCH_SIZE * PATCH_SIZE) * 0.85):
                        patch.save(PROCESSED_PATCHES_POSITIVE_PATH + PATCH_TUMOR_PREFIX +
                                   str(self.positive_patch_index), 'PNG')
                        self.positive_patch_index += 1

                patch.close()
                mask.close()

    def read_wsi_mask(self, wsi_path, mask_path):
        try:
            self.cur_wsi_path = wsi_path
            self.wsi_image = OpenSlide(wsi_path)
            self.mask_image = OpenSlide(mask_path)

            self.level_used = min(self.def_level, self.wsi_image.level_count - 1, self.mask_image.level_count - 1)

            self.mask_pil = self.mask_image.read_region((0, 0), self.level_used,
                                                            self.mask_image.level_dimensions[self.level_used])
            self.mask = np.array(self.mask_pil)

        except OpenSlideUnsupportedFormatError:
            print('Exception: OpenSlideUnsupportedFormatError')
            return False

        return True

    def read_wsi_normal(self, wsi_path):
        """
            # =====================================================================================
            # read WSI image and resize
            # Due to memory constraint, we use down sampled (4th level, 1/32 resolution) image
            # ======================================================================================
        """
        try:
            self.cur_wsi_path = wsi_path
            self.wsi_image = OpenSlide(wsi_path)
            self.level_used = min(self.def_level, self.wsi_image.level_count - 1)

            self.rgb_image_pil = self.wsi_image.read_region((0, 0), self.level_used,
                                                            self.wsi_image.level_dimensions[self.level_used])
            self.rgb_image = np.array(self.rgb_image_pil)

        except OpenSlideUnsupportedFormatError:
            print('Exception: OpenSlideUnsupportedFormatError')
            return False

        return True

    def read_wsi_tumor(self, wsi_path, mask_path):
        """
            # =====================================================================================
            # read WSI image and resize
            # Due to memory constraint, we use down sampled (4th level, 1/32 resolution) image
            # ======================================================================================
        """
        try:
            self.cur_wsi_path = wsi_path
            self.wsi_image = OpenSlide(wsi_path)
            self.mask_image = OpenSlide(mask_path)

            self.level_used = min(self.def_level, self.wsi_image.level_count - 1, self.mask_image.level_count - 1)

            self.rgb_image_pil = self.wsi_image.read_region((0, 0), self.level_used,
                                                            self.wsi_image.level_dimensions[self.level_used])
            self.rgb_image = np.array(self.rgb_image_pil)

        except OpenSlideUnsupportedFormatError:
            print('Exception: OpenSlideUnsupportedFormatError')
            return False

        return True

    def find_roi_n_extract_patches_mask(self):
        mask = cv2.cvtColor(self.mask, cv2.COLOR_BGR2GRAY)
        contour_mask, bounding_boxes = self.get_image_contours_mask(np.array(mask), np.array(self.mask))

        # contour_mask = cv2.resize(contour_mask, (0, 0), fx=0.40, fy=0.40)
        # cv2.imshow('contour_mask', np.array(contour_mask))
        self.mask_pil.close()
        self.extract_patches_mask(bounding_boxes)
        self.wsi_image.close()
        self.mask_image.close()

    def find_roi_n_extract_patches_normal(self):
        hsv = cv2.cvtColor(self.rgb_image, cv2.COLOR_BGR2HSV)
        # [20, 20, 20]
        lower_red = np.array([20, 50, 20])
        # [255, 255, 255]
        upper_red = np.array([200, 150, 200])
        mask = cv2.inRange(hsv, lower_red, upper_red)

        # (50, 50)
        close_kernel = np.ones((25, 25), dtype=np.uint8)
        image_close = Image.fromarray(cv2.morphologyEx(np.array(mask), cv2.MORPH_CLOSE, close_kernel))
        # (30, 30)
        open_kernel = np.ones((30, 30), dtype=np.uint8)
        image_open = Image.fromarray(cv2.morphologyEx(np.array(image_close), cv2.MORPH_OPEN, open_kernel))
        contour_rgb, bounding_boxes = self.get_image_contours_normal(np.array(image_open), self.rgb_image)

        # contour_rgb = cv2.resize(contour_rgb, (0, 0), fx=0.40, fy=0.40)
        # cv2.imshow('contour_rgb', np.array(contour_rgb))
        self.rgb_image_pil.close()
        self.extract_patches_normal(bounding_boxes)
        self.wsi_image.close()

    def find_roi_n_extract_patches_tumor(self):
        hsv = cv2.cvtColor(self.rgb_image, cv2.COLOR_BGR2HSV)
        lower_red = np.array([20, 20, 20])
        upper_red = np.array([255, 255, 255])
        mask = cv2.inRange(hsv, lower_red, upper_red)

        # (50, 50)
        close_kernel = np.ones((50, 50), dtype=np.uint8)
        image_close = Image.fromarray(cv2.morphologyEx(np.array(mask), cv2.MORPH_CLOSE, close_kernel))
        # (30, 30)
        open_kernel = np.ones((30, 30), dtype=np.uint8)
        image_open = Image.fromarray(cv2.morphologyEx(np.array(image_close), cv2.MORPH_OPEN, open_kernel))
        contour_rgb, bounding_boxes = self.get_image_contours_tumor(np.array(image_open), self.rgb_image)

        # contour_rgb = cv2.resize(contour_rgb, (0, 0), fx=0.40, fy=0.40)
        # cv2.imshow('contour_rgb', np.array(contour_rgb))
        self.rgb_image_pil.close()
        self.extract_patches_tumor(bounding_boxes)
        self.wsi_image.close()
        self.mask_image.close()

    @staticmethod
    def get_image_contours_mask(cont_img, mask_img):
        _, contours, _ = cv2.findContours(cont_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        bounding_boxes = [cv2.boundingRect(c) for c in contours]
        contours_mask_image_array = np.array(mask_img)
        line_color = (255, 0, 0)  # blue color code
        cv2.drawContours(contours_mask_image_array, contours, -1, line_color, 1)
        return contours_mask_image_array, bounding_boxes

    @staticmethod
    def get_image_contours_normal(cont_img, rgb_image):
        _, contours, _ = cv2.findContours(cont_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        bounding_boxes = [cv2.boundingRect(c) for c in contours]
        contours_rgb_image_array = np.array(rgb_image)
        line_color = (255, 0, 0)  # blue color code
        cv2.drawContours(contours_rgb_image_array, contours, -1, line_color, 3)
        return contours_rgb_image_array, bounding_boxes

    @staticmethod
    def get_image_contours_tumor(cont_img, rgb_image):
        _, contours, _ = cv2.findContours(cont_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        bounding_boxes = [cv2.boundingRect(c) for c in contours]
        contours_rgb_image_array = np.array(rgb_image)

        line_color = (255, 0, 0)  # blue color code
        cv2.drawContours(contours_rgb_image_array, contours, -1, line_color, 3)
        # cv2.drawContours(mask_image, contours_mask, -1, line_color, 3)
        return contours_rgb_image_array, bounding_boxes

    def wait(self):
        self.key = cv2.waitKey(0) & 0xFF
        print('key: %d' % self.key)

        if self.key == 27:  # escape
            return False
        elif self.key == 81:  # <- (prev)
            self.index -= 1
            if self.index < 0:
                self.index = len(self.wsi_paths) - 1
        elif self.key == 83:  # -> (next)
            self.index += 1
            if self.index >= len(self.wsi_paths):
                self.index = 0

        return True
示例#33
0
class _SlideTest(object):
    def setUp(self):
        self.osr = OpenSlide(file_path(self.FILENAME))

    def tearDown(self):
        self.osr.close()
示例#34
0
def delete_label_annotion(file_dir):
    mrxs_lst = []
    is_label = input(
        "Please input mrxs filename and ID of its delete label:\n"
        "For example, mrxs_file1:ID1 (DD10:UENFI10JD-DKKEMIJD-19DNK937,DD11:JFD73JF-2KD93-DKV10)\n"
    )
    # cv2.destroyAllWindows()
    label_lst = is_label.split(",")
    label_dict = {}
    if label_lst != ['']:
        for li in label_lst:
            if label_dict.get(li.split(":")[0]) is not None:
                label_dict[li.split(":")[0]].append(li.split(":")[1])
            else:
                label_dict[li.split(":")[0]] = [li.split(":")[1]]

    if not os.path.isdir(file_dir):
        mrxs_lst[0] = file_dir
    else:
        for i in os.listdir(file_dir):
            if os.path.splitext(i)[-1] == '.mrxs':
                print(
                    "==============================================================================="
                )
                print(os.path.join(file_dir, os.path.splitext(i)[0]))
                mrxs_lst.append(os.path.join(file_dir, os.path.splitext(i)[0]))
    for mrxs_fille_name in mrxs_lst:
        # mrxs_fille_name = "/home/gytang/medicine/fake/DCB-003"
        osr = OpenSlide(mrxs_fille_name + '.mrxs')
        axis_list = []
        find_file_flag = 0
        file_list = os.listdir(mrxs_fille_name)
        file_list.sort()
        delete_label = None
        if label_dict.get(os.path.basename(mrxs_fille_name)) is not None:
            delete_label = label_dict.get(os.path.basename(mrxs_fille_name))
        for i in file_list[::-1]:
            a = magic.from_file(os.path.join(mrxs_fille_name, i))
            #print("{}: magic tool read file property: {}".format(i, a))
            if a.find("ASCII text") != -1 or a.find("UTF-8 Unicode") != -1:
                with open(os.path.join(mrxs_fille_name, i)) as f:
                    lines = f.readlines()
                lines.reverse()
                if lines[-1].find("<attributes>") == -1:
                    continue
                if find_file_flag == 1:
                    print(
                        "There are already exsited a .dat label config file. The mrxs is not as normal as we occured."
                    )
                find_file_flag = 1
                ID_name = []
                res = []
                for idx, line in enumerate(lines):
                    try:
                        line = parseString(line)
                        # print("ok:", line[0])
                    except:
                        # print("Can't analysis the {}th line".format(idx))
                        # print(line)
                        res.append(line)
                        continue
                    descript_lst = line.getElementsByTagName('descriptor')
                    for elements in descript_lst:
                        ID = elements.getAttribute('ID')[1:-1]
                        if ID not in ID_name:
                            ID_name.append(ID)
                            if delete_label is not None:
                                if ID in delete_label:
                                    continue
                            sib_lst = line.getElementsByTagName(
                                'SimpleBookmark')
                            if sib_lst == []:
                                continue
                            label = sib_lst[0].getAttribute('Caption')
                            detail_label = sib_lst[0].getAttribute('Desc')
                            if label.find(
                                    "Annotation") == 0 and detail_label != "":
                                # print("alter annotation label.")
                                for node in sib_lst:
                                    node.setAttribute("Caption", detail_label)
                        res.append(line)

                with open(os.path.join(mrxs_fille_name, i), 'w') as fd:
                    if res is not None:
                        res.reverse()
                    for re in res:
                        try:
                            # print("ok:", line[0])
                            line = re.toxml() + '\n'
                            if line.find('<?xml version="1.0" ?>') == 0:
                                line = line.replace('<?xml version="1.0" ?>',
                                                    '')
                            # print(line)
                            fd.write(line)
                        except:
                            # print("Can't analysis the {}th line")
                            # print(re)
                            fd.write(re)
                            continue

                break

        if find_file_flag == 0:
            print(
                "No .dat label file in mrxs {} which searched by magic tool.".
                format(mrxs_fille_name))
        osr.close()