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_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) 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])) except OpenSlideUnsupportedFormatError: print('Exception: OpenSlideUnsupportedFormatError') return None, None, None return wsi_image, rgb_image, level_used
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
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
class InferTumorSegDataset(Dataset): def __init__(self, wsi_path, patch_size, stride_size, selected_level, mask_level): self._wsi_path = wsi_path self._patch_size = patch_size self._stride_size = stride_size self._selected_level = selected_level self._mask_level = mask_level self._os_image = OpenSlide(os.path.join(self._wsi_path)) self._points = [] self._basic_preprocessing() def _basic_preprocessing(self): mask_xdim, mask_ydim = self._os_image.level_dimensions[ self._mask_level] print(self._wsi_path, self._os_image) extracted_image = self._os_image.read_region( (0, 0), self._mask_level, (mask_xdim, mask_ydim)).convert('RGB') mask = tissue_mask_generation(extracted_image) del extracted_image ydim, xdim = self._os_image.level_dimensions[self._selected_level] mask = resize(mask, (xdim, ydim)) mask = (mask > 0).astype(np.uint8) for i in range(0, ydim - self._patch_size[0], self._stride_size[0]): for j in range(0, xdim - self._patch_size[1], self._stride_size[1]): self._points.append([j, i]) for i in range(len(self._points)): point = self._points[i] if not np.any(mask[point[0]:point[0] + self._patch_size[0], point[1]:point[1] + self._patch_size[1]]): self._points[i] = [-1, -1] self._points = np.array(self._points) self._points = np.delete( self._points, np.argwhere(self._points == np.array([-1, -1])), 0) self._points[:, [0, 1]] = self._points[:, [1, 0]] self._mask = mask def __len__(self): return len(self._points) def __getitem__(self, idx): x_loc, y_loc = self._points[idx] patch = np.array( self._os_image.read_region( (x_loc * 4, y_loc * 4), self._selected_level, (self._patch_size[0], self._patch_size[1])).convert('RGB')) patch = np.array(patch / 255) patch = patch.transpose([2, 0, 1]) return patch, (x_loc, y_loc)
def next_batch(self, n, **kwargs): assert self.initialized == True, "Data Source not initialized" flat = 'flat' in kwargs and kwargs['flat'] is True noise = 'noise' in kwargs and kwargs['noise'] is True nc = kwargs['nc'] if 'nc' in kwargs else 0.05 level = kwargs['level'] if 'level' in kwargs else 0 isfloat = kwargs['isfloat'] if 'isfloat' in kwargs else False selected_image = np.random.randint(len(self.images)) chunks = np.random.random((3,n)) im_path = self.images[selected_image][0] osr = OpenSlide(im_path) mask = self.masks[selected_image] imsize = osr.level_dimensions[level] ratios = [imsize[0]*1./mask.shape[0], imsize[1]*1./mask.shape[1]] n_px_in_mask = mask.sum() indexes = np.arange(n_px_in_mask)+1 mask_indexes = np.zeros(mask.shape) mask_indexes[mask] = indexes chunks[0,:] *= n_px_in_mask # index in mask chunks[1,:] *= (ratios[0]-self.chunksize[0]) # y pixel offset in higher def image from top-left of region chunks[2,:] *= (ratios[1]-self.chunksize[1]) # x pixel offset in higher def image from top-left of region if isfloat: chunks = chunks.astype('float') if chunks.max() > 1: chunks = chunks / 255. else: if chunks.max() < 1: chunks = chunks*255 chunks = np.round(chunks).astype('uint16') lowres_coordinates = [np.where(mask_indexes==c) for c in chunks[0,:]] highres_coordinates = [ ( int((lr[0]*ratios[0] + chunks[1,i])[0]), int((lr[1]*ratios[1] + chunks[2,i])[0]) ) for i,lr in enumerate(lowres_coordinates) ] if noise: if flat: toAdd = np.random.rand(n, self.chunksize[0]*self.chunksize[1]*3)*nc else: toAdd = np.random.rand(n, self.chunksize[0], self.chunksize[1], 3)*nc else: if flat: toAdd = np.zeros((n, self.chunksize[0]*self.chunksize[1]*3)) else: toAdd = np.zeros((n, self.chunksize[0], self.chunksize[1], 3)) if flat : return [np.asarray(osr.read_region((chunk[0], chunk[1]), level, self.chunksize)).flatten().astype(np.float32)[:,:,:3]/255.+toAdd[i] for i,chunk in enumerate(highres_coordinates)] else: return [np.asarray(osr.read_region((chunk[0], chunk[1]), level, self.chunksize)).astype(np.float32)[:,:,:3]/255.+toAdd[i] for i,chunk in enumerate(highres_coordinates)]
def convert_image(path_img, level=DEFAULT_LEVEL, overwrite=False): """ convert TIFF/SVS image to standard format The output image has the same name and it is exported in the same folder :param str path_img: path to the input image :param int level: selected level of the internal pyramid representation the level 0 means full scale and higher number is small image in pyramid scaling :param bool overwrite: whether overwrite existing image on output """ slide_img = OpenSlide(path_img) if level >= len(slide_img.level_dimensions): raise ValueError('unsupported level %i of %i' % (level, slide_img.level_count)) path_img_new = os.path.splitext(path_img)[0] + IMAGE_EXTENSION if os.path.isfile(path_img_new) and not overwrite: logging.warning('existing "%s"', path_img_new) return level_size = slide_img.level_dimensions[level] level_scale = slide_img.level_downsamples[level] level_downsample = 1 while max(np.array(level_size) / level_downsample) > MAX_LOAD_IMAGE_SIZE: level_downsample *= 2 logging.debug('using down-sample: %i', level_downsample) tile_size = (np.array(level_size) / level_downsample).astype(int) locations = [(i * tile_size[0], j * tile_size[1]) for i in range(level_downsample) for j in range(level_downsample)] im = np.array(slide_img.read_region((0, 0), 0, size=(10, 10))) nb_channels = min(3, im.shape[2]) if im.ndim == 3 else 1 img_size = list(tile_size * level_downsample)[::-1] + [nb_channels] image = np.zeros(img_size, dtype=np.uint8) for loc_i, loc_j in tqdm.tqdm(locations, desc=os.path.basename(path_img)): loc_img = int(loc_i * level_scale), int(loc_j * level_scale) img = np.array(slide_img.read_region(loc_img, level, size=tile_size)) image[loc_j:loc_j + img.shape[0], loc_i:loc_i + img.shape[1], ...] = img[:, :, :nb_channels] del img if nb_channels == 2: image = image[:, :, 0] logging.debug('save image: "%s"', path_img_new) cv.imwrite(path_img_new, image, params=(cv.IMWRITE_PNG_COMPRESSION, 9)) gc.collect() time.sleep(1)
class Slide(object): def __init__(self, image_path): super(Slide, self).__init__() self.image_path = image_path.strip() self.image_file = os.path.basename(image_path) self.image_name, self.suffix = self.image_file.split('.') if not self.suffix in config.format_mapping.keys(): raise Exception('Error: File format ' + self.suffix + ' is supported yet.') self._slide = OpenSlide(image_path) self._level_downsamples = self._slide.level_downsamples self._level_dimensions = self._slide.level_dimensions self.width, self.height = self._level_dimensions[0] @property def dimensions(self): return self._level_dimensions[0] @property def level_dimensions(self): return self._level_dimensions @property def level_downsamples(self): return self._level_downsamples def read_region(self, level, location, size): (x, y), (w, h) = location, size try: _ds = self._level_downsamples[level] patch = self._slide.read_region(level=level, location=(int(x * _ds), int(y * _ds)), size=size) except Exception: raise else: return patch def get_thumbnail(self): try: image = self._slide.read_region(location=(0, 0), level=0, size=(1000, 1000)) except Exception: raise else: return image
def main(): filepath = sys.argv[1] img = OpenSlide(filepath) if not os.path.exists(sys.argv[3]): os.makedirs(sys.argv[3]) if str(img.properties.values.__self__.get('tiff.ImageDescription')).split( "|")[1] == "AppMag = 40": sz = 1024 seq = 924 else: sz = 512 seq = 462 [w, h] = img.dimensions for x in range(1, w, seq): for y in range(1, h, seq): print(str(x) + ", " + str(y)) img1 = img.read_region(location=(x, y), level=0, size=(sz, sz)) img11 = img1.convert("RGB") img111 = img11.resize((512, 512), Image.ANTIALIAS) grad = getGradientMagnitude(np.array(img111)) unique, counts = np.unique(grad, return_counts=True) if counts[np.argwhere(unique <= 20)].sum() < 512 * 512 * 0.6: img111.save(sys.argv[3] + "/" + sys.argv[2] + "_" + str(x) + "_" + str(y) + '.jpg', 'JPEG', optimize=True, quality=94)
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 convert(data): UNIT_X, UNIT_Y = 5000, 5000 try: fname, input_dir, output_dir = data save_name = fname.split(".")[0] + ".jpg" print("Processing : %s"%fname) os_obj = OpenSlide(input_dir+"/"+fname) w, h = os_obj.dimensions w_rep, h_rep = int(w/UNIT_X)+1, int(h/UNIT_Y)+1 w_end, h_end = w%UNIT_X, h%UNIT_Y w_size, h_size = UNIT_X, UNIT_Y w_start, h_start = 0, 0 v_lst = [] for i in range(h_rep): if i == h_rep-1: h_size = h_end h_lst = [] for j in range(w_rep): if j == w_rep-1: w_size = w_end img = os_obj.read_region((w_start,h_start), 0, (w_size,h_size)) img = img.convert("RGB") h_lst.append(img) w_start += UNIT_X v_lst.append(h_lst) w_size = UNIT_X h_start += UNIT_Y w_start = 0 concat_h = [_get_concat_h(v) for v in v_lst] concat_hv = _get_concat_v(concat_h) concat_hv.save(output_dir+"/"+save_name) except: print("Can't open image file : %s"%fname) traceback.print_exc() return
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
def convert_image(path_img, level=DEFAULT_LEVEL, level_shift=DEFAULT_LEVEL_OFFSET, overwrite=False): slide_img = OpenSlide(path_img) assert level < len(slide_img.level_dimensions), \ 'unsupported level %i' % level assert (level + level_shift) < len(slide_img.level_dimensions), \ 'unsupported shift %i for level %i' % (level_shift, level) path_img_new = os.path.splitext(path_img)[0] + IMAGE_EXTENSION if os.path.isfile(path_img_new) and not overwrite: logging.debug('existing "%s"', path_img_new) return tile_size = slide_img.level_dimensions[level + level_shift] img_tiles_d0 = [] tqdm_bar = tqdm.tqdm(total=(2 ** level_shift) ** 2, desc=os.path.basename(path_img)) for i in range(2 ** level_shift): img_tiles_d1 = [] for j in range(2 ** level_shift): loc = (i * tile_size[0] * 2 ** level, j * tile_size[1] * 2 ** level) img = slide_img.read_region(loc, level, size=tile_size) img_tiles_d1.append(img) tqdm_bar.update() img_tiles_d0.append(np.vstack(img_tiles_d1)) image = np.hstack(img_tiles_d0) if image.shape[2] == 4: image = cv.cvtColor(image, cv.COLOR_RGBA2RGB) logging.debug('save image: "%s"', path_img_new) cv.imwrite(path_img_new, image, params=(cv.IMWRITE_PNG_COMPRESSION, 9)) gc.collect() time.sleep(1)
def save_slide_cutting_multiple(file_path, save_location, multiple): slide = OpenSlide(file_path) level_cnt = slide.level_count for level in reversed(range(level_cnt)): downsample = slide.level_downsamples[level] w_lv_, h_lv_ = slide.level_dimensions[level] wsi_pil_lv_ = slide.read_region((0, 0), level, (w_lv_, h_lv_)) wsi_ary_lv_ = np.array(wsi_pil_lv_) wsi_bgr_lv_ = cv2.cvtColor(wsi_ary_lv_, cv2.COLOR_RGBA2BGR) downsample = round(downsample) print(downsample) if downsample > multiple: continue elif downsample < multiple: downsample = multiple / downsample w = int(w_lv_ / downsample) h = int(h_lv_ / downsample) img = cv2.resize(wsi_bgr_lv_, (w, h), interpolation=cv2.INTER_LINEAR) cv2.imwrite(save_location, img) break else: img = wsi_bgr_lv_ cv2.imwrite(save_location, img) break
def generate_patches(filepath, window, stride, K, auto_ws, scaling_factor): slide = OpenSlide(filepath) image = np.asarray(slide.read_region((0, 0), 2, slide.level_dimensions[2]))[:, :, :3] image = np.array(image) if auto_ws: window = detect_best_window_size(image, 16, scaling_factor) stride = window h, w = image.shape[:2] regions = [] j = 0 while window + stride * j <= h: i = 0 while window + stride * i <= w: x_start = i * stride y_start = j * stride patch = image[y_start:y_start + window, x_start:x_start + window, :] white_ratio, g_mean, b_mean = compute_statistics(patch) region = (x_start, y_start, white_ratio, g_mean, b_mean) regions.append(region) i += 1 j += 1 regions = select_k_best_regions(regions, K) patches = get_k_best_regions(regions, image, window) return image, regions, patches, window
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
class MIRAX_Slide: def __init__(self, mirax_path): self.mirax_slide = OpenSlide(mirax_path) def read_region(self, location, level, size): """ Read image region :param location: (x, y) :param level: :param size: (w, h) :return: """ image = self.mirax_slide.read_region(location, level, size) image = np.array(image) if image.shape[2] > 3: image = image[:, :, 0:3] # convert from RGBA to RGB return image def read_properties(self): mmp_x = float(self.mirax_slide.properties['openslide.mpp-x']) # pixel size in um mmp_y = float(self.mirax_slide.properties['openslide.mpp-y']) # pixel size in um # Distance in X from the center of the entire slide (i.e., the macro image) to the center of the main image, in nm offset_x = float(self.mirax_slide.properties['hamamatsu.XOffsetFromSlideCentre']) # Distance in Y from the center of the entire slide to the center of the main image, in nm offset_y = float(self.mirax_slide.properties['hamamatsu.YOffsetFromSlideCentre']) level_0_dim = self.mirax_slide.level_dimensions[0] level_0_width = level_0_dim[0] level_0_height = level_0_dim[1]
def sz(image_file, path_to_slide="../Neutrophil/"): slide = OpenSlide(path_to_slide + image_file) assert 'openslide.bounds-height' in slide.properties assert 'openslide.bounds-width' in slide.properties assert 'openslide.bounds-x' in slide.properties assert 'openslide.bounds-y' in slide.properties x = int(slide.properties['openslide.bounds-x']) y = int(slide.properties['openslide.bounds-y']) bounds_height = int(slide.properties['openslide.bounds-height']) bounds_width = int(slide.properties['openslide.bounds-width']) half_width_region = 49 full_width_region = 299 stepsize = full_width_region - half_width_region n_x = int((bounds_width - 1) / stepsize) n_y = int((bounds_height - 1) / stepsize) residue_x = int((bounds_width - n_x * stepsize) / 50) residue_y = int((bounds_height - n_y * stepsize) / 50) lowres = slide.read_region( (x, y), 2, (int(n_x * stepsize / 16), int(n_y * stepsize / 16))) lowres = np.array(lowres)[:, :, :3] return n_x, n_y, lowres, residue_x, residue_y
def semantic2patch(self, patch_path): """ Extract the patch of boundary :param patch_path: the path of save :return: """ contours = measure.find_contours(self.result, 0.5) casepath = os.path.join(self.img_WSI_dir, self.name) wsi = OpenSlide(casepath) for i in xrange(len(contours)): print i, '/', len(contours) for j in range(len(contours[i])): y = int(contours[i][j, 0] * self.stride * 2**self.using_level) x = int(contours[i][j, 1] * self.stride * 2**self.using_level) img = np.array( wsi.read_region((x, y), self.using_level, self.patch))[..., 0:-1] savepath = os.path.join( patch_path, ''.join(os.path.basename(casepath).split(' '))) if not os.path.exists(savepath): os.makedirs(savepath) io.imsave( os.path.join(savepath, str(x) + '_' + str(y) + '.png'), img.astype(np.float) / 255) print 'semantic2patch done'
def read_img(slide_file, location, level, size): slide = OpenSlide(slide_file) location = [int(x) for x in location] size = [int(x) for x in size] img = np.array( slide.read_region(location, int(level), size).convert(mode='RGB')) return img
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
def random_tiles(imgfilename, inputdir, outputdir): print(imgfilename, inputdir, outputdir) print(os.path.join(inputdir, imgfilename), "\n \n ") if imgfilename.find(".svs") != -1 or imgfilename.find("mrxs") != -1: filepath = os.path.join(inputdir, imgfilename) sample_id = imgfilename.split(".")[0] #try: img = OpenSlide(filepath) if len(os.listdir(os.path.join(outputdir, sample_id))) == 0: print("Sample ID in process ", sample_id) try: os.mkdir(os.path.join(outputdir)) except: print("The Folder already exist") try: os.mkdir(os.path.join(outputdir, sample_id)) except: print("The Folder for the sample id {} already exist".format(sample_id)) print("HERE") if imgfilename.find(".svs") != -1 : if str(img.properties.values.__self__.get('tiff.ImageDescription')).split("|")[1] == "AppMag = 40": sz=2048 seq=1748 else: sz=1024 seq=874 elif imgfilename.find("mrxs") != -1: if str(img.properties.values.__self__.get("mirax.GENERAL.OBJECTIVE_MAGNIFICATION")) == 20: sz=1024 seq=874 else: sz=2048 seq=1748 [w, h] = img.dimensions couple_coords_accepted = [] for x in range(1, w, seq): for y in range(1, h, seq): #try: img1=img.read_region(location=(x,y), level=0, size=(sz,sz)) print('img1') img11=img1.convert("RGB") print('img11') img111=img11.resize((512,512),Image.ANTIALIAS) print('img111') pix = np.array(img111) print('pix') grad=getGradientMagnitude(pix) print('grad') unique, counts = np.unique(grad, return_counts=True) print('unique') mean_ch = np.mean(pix, axis=2) print('mean_ch') bright_pixels_count = np.argwhere(mean_ch > 220).shape[0] print('bright_pixels_count') if counts[np.argwhere(unique<=15)].sum() < 512*512*0.6 and bright_pixels_count < 512*512*0.5 : couple_coords_accepted.append((x,y)) img111.save( os.path.join(outputdir, sample_id, sample_id + "_" + str(x) + "_" + str(y) + '.jpg' ) , 'JPEG', optimize=True, quality=94) print('tiles written ', os.path.join(outputdir, sample_id, sample_id + "_" + str(x) + "_" + str(y) + '.jpg' ))
def save_slide_as_jpg_with_level(file_path, save_location, level): slide_tif = OpenSlide(file_path) print(('==> saving slide_lv_%s at ' + save_location) % level) wsi_pil_lv_ = slide_tif.read_region((0, 0), level, \ slide_tif.level_dimensions[level]) wsi_ary_lv_ = np.array(wsi_pil_lv_) wsi_bgr_lv_ = cv2.cvtColor(wsi_ary_lv_, cv2.COLOR_RGBA2BGR) cv2.imwrite(save_location, wsi_bgr_lv_)
def wsi_to_numpy(wsi: openslide.OpenSlide, level: int = 3) -> np.ndarray: """Transforme une lame format .svs en une matrice numpy selon un niveau de résolution prédéfini en paramètre. Attention: le niveau doit être suffisamment élevé pour que l'image rentre en mémoire ! """ assert level > 0, 'Sélectionnez un niveau de résolution plus faible!' image = np.asarray( wsi.read_region((0, 0), level, wsi.level_dimensions[level]).convert('RGB')) return image
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
def wsi_to_jpg(sample_path, output_path): slide = OpenSlide(sample_path) w, h = slide.dimensions new_w = math.floor(w / SCALE_FACTOR) new_h = math.floor(h / SCALE_FACTOR) level = slide.get_best_level_for_downsample(SCALE_FACTOR) pil_img = slide.read_region( (0, 0), level, slide.level_dimensions[level]).convert("RGB").resize( (new_w, new_h), PIL.Image.BILINEAR) pil_img.save(output_path)
def read_wsi(tif_file_path, level): ''' Identify and load slides. Returns: - wsi_image: OpenSlide object. - rgba_image: WSI image loaded, NumPy array type. ''' time_s = time.time() try: wsi_image = OpenSlide(tif_file_path) slide_w_, slide_h_ = wsi_image.level_dimensions[level] ''' The read_region loads the target area into RAM memory, and returns an Pillow Image object. !! Take care because WSIs are gigapixel images, which are could be extremely large to RAMs. Load the whole image in level < 3 could cause failures. ''' # Here we load the whole image from (0, 0), so transformation of coordinates # is not skipped. rgba_image_pil = wsi_image.read_region((0, 0), level, (slide_w_, slide_h_)) print("width, height:", rgba_image_pil.size) ''' !!! It should be noted that: 1. np.asarray() / np.array() would switch the position of WIDTH and HEIGHT in shape. Here, the shape of $rgb_image_pil is: (WIDTH, HEIGHT, CHANNEL). After the np.asarray() transformation, the shape of $rgb_image is: (HEIGHT, WIDTH, CHANNEL). 2. The image here is RGBA image, in which A stands for Alpha channel. The A channel is unnecessary for now and could be dropped. ''' rgba_image = np.asarray(rgba_image_pil) print("transformed:", rgba_image.shape) except OpenSlideUnsupportedFormatError: print('Exception: OpenSlideUnsupportedFormatError') return None time_e = time.time() print("Time spent on loading", tif_file_path, ": ", (time_e - time_s)) return wsi_image, rgba_image, (slide_w_, slide_h_)
def save_patch(\ file_path_tif, save_location_path_patch, patch_level, size_patch): print('=> Making patch ..') # Read file name list of slide directory file_name_list_tif = get_list_file_name(file_path_tif) for index in range(len(file_name_list_tif)): file_name_tif = file_name_list_tif[index] file_name_slide = file_name_tif.split('.')[0] file_name_csv = file_name_slide + '.csv' cur_file_path_tif =\ os.path.join(file_path_tif, file_name_tif) cur_file_path_csv =\ os.path.join(save_location_path_patch, file_name_slide) cur_file_path_csv =\ os.path.join(cur_file_path_csv, file_name_csv) # Load slide image slide = OpenSlide(cur_file_path_tif) # Read patch position csv file csv_file = open(cur_file_path_csv, 'rb') csv_reader = csv.DictReader(csv_file) csv_data = list(csv_reader) # Make patch and Save for i, row in enumerate(csv_data): x = int(row['X']) y = int(row['Y']) patch = slide.read_region((x, y),\ patch_level,\ (size_patch, size_patch)) patch = np.array(patch) patch = cv2.cvtColor(patch, cv2.COLOR_RGBA2BGR) file_name_patch_jpg = file_name_slide + '_patch_' + str(i) + '.jpg' cur_save_path_patch_jpg =\ os.path.join(save_location_path_patch,\ file_name_slide) cur_save_path_patch_jpg =\ os.path.join(cur_save_path_patch_jpg ,\ file_name_patch_jpg) cv2.imwrite(cur_save_path_patch_jpg, patch) csv_file.close() print('=> Next..')
def get_region(wsi: openslide.OpenSlide, x: int, y: int, level: int, width: int, height: int) -> Image: """Sélectionne la région d'intérêt sur la lame. Paramètres: x, y : origine en haut à gauche selon la largeur et la hauteur level : niveau de résolution (0 le plus élevé) width : largeur de la région height : hauteur de la région """ region = wsi.read_region((x, y), level, (width, height)) return region
def read_wsi_mask(mask_path, level=def_level): try: wsi_mask = OpenSlide(mask_path) mask_image = np.array(wsi_mask.read_region((0, 0), level, wsi_mask.level_dimensions[level])) except OpenSlideUnsupportedFormatError: print('Exception: OpenSlideUnsupportedFormatError') return None, None return wsi_mask, mask_image
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 process(opts): i, pid, x_center, y_center, file_path_tif, path_patch, patch_size, patch_level = opts x = int(int(float(x_center)) - 1024 / 2) y = int(int(float(y_center)) - 1024 / 2) wsi_path = os.path.join(file_path_tif + pid + '.svs') slide = OpenSlide(wsi_path) img = slide.read_region((x, y), 0, (1024, 1024)).convert('RGB') if not os.path.exists(os.path.join(path_patch, pid + "_" + str(i) + '.png')): print(os.path.join(path_patch, pid + "_" + str(i) + '.png')) img.save(os.path.join(path_patch, pid + "_" + str(i) + '.png'))
def tile(image_file, outdir, level, std_img, path_to_slide=str(cwd+"/../images/"), dp=None, ft=1): slide = OpenSlide(path_to_slide+image_file) slp = str(path_to_slide+image_file) print(slp) print(slide.level_dimensions) bounds_width = slide.level_dimensions[level][0] bounds_height = slide.level_dimensions[level][1] x = 0 y = 0 half_width_region = 49*ft full_width_region = 299*ft stepsize = (full_width_region - half_width_region) n_x = int((bounds_width - 1) / stepsize) n_y = int((bounds_height - 1) / stepsize) lowres = slide.read_region((x, y), level+1, (int(n_x*stepsize/4), int(n_y*stepsize/4))) lowres = np.array(lowres)[:,:,:3] x0 = 0 # create multiporcessing pool print(mp.cpu_count()) pool = mp.Pool(processes=mp.cpu_count()) tasks = [] while x0 < n_x: task = tuple((slp, n_y, x, y, full_width_region, stepsize, x0, outdir, level, dp, std_img)) tasks.append(task) x0 += 1 # slice images with multiprocessing temp = pool.starmap(v_slide, tasks) tempdict = list(temp) temp = None pool.close() pool.join() tempdict = list(filter(None, tempdict)) imloc = [] list(map(imloc.extend, tempdict)) imlocpd = pd.DataFrame(imloc, columns = ["X_pos", "Y_pos", "X", "Y", "Loc"]) imlocpd = imlocpd.sort_values(["X_pos", "Y_pos"], ascending=[True, True]) imlocpd = imlocpd.reset_index(drop=True) imlocpd = imlocpd.reset_index(drop=False) imlocpd.columns = ["Num", "X_pos", "Y_pos", "X", "Y", "Loc"] if dp: imlocpd.to_csv(outdir + "/{}_dict.csv".format(dp), index=False) else: imlocpd.to_csv(outdir + "/dict.csv", index=False) tempdict = None ct = len(imloc) print(ct) return n_x, n_y, lowres, ct
def run(filename, split, level, dred, dbrown, debug): # Create OpenSlide object ndpi = OpenSlide(filename) ndpi_width = ndpi.dimensions[0] ndpi_height = ndpi.dimensions[1] total_width = ndpi.level_dimensions[level][0] total_height = ndpi.level_dimensions[level][1] red_sum = 0.0 brown_sum = 0.0 surface_sum = 0.0 startTime = time.time() if debug: print "filename: {}".format(filename) print "split: {}".format(split) print "level: {}".format(level) print "dred: {}".format(dred) print "dbrown: {}".format(dbrown) print "debug: {}".format(debug) if debug: print "================ START =================" print "LOAD {}".format(filename) print "width:".ljust(20) + str(ndpi_width) print "height:".ljust(20) + str(ndpi_height) print "level count:".ljust(20) + str(ndpi.level_count) print "split image {}x{} , level:{} , factor:{}".format(total_width,total_height,level,split) bar = Bar('Processing', max=split**2) for i in range(split): for j in range(split): x = i * ndpi_width / split y = j * ndpi_height / split w = total_width / split h = total_height / split if debug: print "\n>SLICE [{}][{}]".format(i,j) print "x:{:3} y:{:3} w:{:3}px h:{:3}px:".format(x,y,w,h) region = ndpi.read_region((x,y), level, (w, h)) red = bgsa.get_red(region, brightness=-dred) brown = bgsa.get_brown(region, brightness=-dbrown) # surface = bgsa.get_surface(region) region.save("output/normal_slice{}{}.png".format(i,j)) red.save("output/red_slice_{}{}.png".format(i,j)) brown.save("output/brown_slice_{}{}.png".format(i,j)) # surface.save("output/surface_slice_{}{}.png".format(i,j)) red_sum += bgsa.get_white_pixels(red) brown_sum += bgsa.get_white_pixels(brown) # surface_sum+= bgsa.get_white_pixels(surface) if debug: bar.next() # print "white:{}% black{}%".format(results["white"], results["black"]) if debug: bar.finish() print "Finished....in {:.2f} sec".format(time.time() - startTime) print "total red :".ljust(20) + str(red_sum) print "total brown :".ljust(20) + str(brown_sum) return {"red":red_sum, "brown":brown_sum}
class QtCustomWindow( qt.QWidget ): def __init__( self, parent = None ) : qt.QWidget.__init__( self, parent ) self.current_x=0 self.current_y=0 #read region always works in the level 0 reference for reading locations self.current_zoom=0 self.image_width=750 self.image_height=750 self.setWindowTitle('Red Rock') self.hbox = qt.QHBoxLayout(self) self.lbl = qt.QLabel(self) self.setLayout(self.hbox) self.initUI() def initUI(self): # initialize the widget, make the openslide object pointing to the # multilevel image and initialize the view to the top left corner archivo=u'/home/martin/Downloads/CMU-1-JP2K-33005.svs' # openSlide object self.osr = OS(archivo) self.level_count = self.osr.level_count self.current_x=0 self.current_y=0 self.current_zoom=self.level_count-1 self.level_dimensions=self.osr.level_dimensions print self.level_dimensions print self.osr.level_downsamples #width, height = osr.dimensions self.hbox.addWidget(self.lbl) #self.move(300, 200) self.updatePixmap() self.show() def updatePixmap(self): im=self.osr.read_region((self.current_x,self.current_y),self.current_zoom,(self.image_width,self.image_height)) data = im.tostring('raw', 'RGBA') image = qt.QImage(data, im.size[0], im.size[1], qt.QImage.Format_ARGB32) pix = qt.QPixmap.fromImage(image) self.lbl.setPixmap(pix) self.lbl.show() def keyPressEvent( self, event ) : key = event.text() #larger steps for zoomed out images step =int(64*self.osr.level_downsamples[self.current_zoom]) print type(step) print type(self.current_x) if key=="s": self.current_y += step print type(self.current_y) self.current_y = min(self.current_y,int( self.level_dimensions[0][1]-self.image_height*self.osr.level_downsamples[self.current_zoom])) elif key=="w": #larger steps for zoomed out images self.current_y -= step self.current_y = max(0, self.current_y) elif key=="a": #larger steps for zoomed out images self.current_x -= step self.current_x = max(0, self.current_x) elif key=="d": #larger steps for zoomed out images self.current_x += step self.current_x = min(self.current_x,int( self.level_dimensions[0][0]-self.image_width*self.osr.level_downsamples[self.current_zoom])) elif key=="q": new_zoom = min(self.current_zoom+1,self.level_count-1) self.updateCorner(new_zoom) self.current_zoom=new_zoom elif key=="e": new_zoom = max(self.current_zoom-1,0) self.updateCorner(new_zoom) self.current_zoom=new_zoom print "x: %s" % self.current_x print "y: %s" % self.current_y print "zoom: %s" % self.current_zoom self.updatePixmap() def updateCorner(self,new_zoom): #updates the current_x and current_y values to preserve the image center #while zooming self.current_x=int(self.current_x + self.image_width /2 *(self.osr.level_downsamples[self.current_zoom]-self.osr.level_downsamples[new_zoom])) self.current_y=int(self.current_y + self.image_height /2 *(self.osr.level_downsamples[self.current_zoom]-self.osr.level_downsamples[new_zoom]))
class WSI(object): """ # ================================ # Class to annotate WSIs with ROIs # ================================ """ index = 0 wsi_paths = [] mask_paths = [] def_level = 7 key = 0 def read_normal_wsi(self, wsi_path): """ # ===================================================================================== # read WSI image and resize # Due to memory constraint, we use down sampled (4th level, 1/32 resolution) image # ====================================================================================== """ try: self.wsi_path = wsi_path self.wsi_image = OpenSlide(wsi_path) level = min(self.def_level, self.wsi_image.level_count - 1) print('level used: %d' % level) print(self.wsi_image.level_dimensions[level]) self.rgb_image_pil = self.wsi_image.read_region((0, 0), level, self.wsi_image.level_dimensions[level]) self.rgb_image = np.array(self.rgb_image_pil) except OpenSlideUnsupportedFormatError: print('Exception: OpenSlideUnsupportedFormatError') return False return True def read_tumor_wsi(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.wsi_path = wsi_path self.wsi_image = OpenSlide(wsi_path) self.mask_image = OpenSlide(mask_path) level_used = self.wsi_image.level_count - 1 self.rgb_image_pil = self.wsi_image.read_region((0, 0), level_used, self.wsi_image.level_dimensions[level_used]) self.rgb_image = np.array(self.rgb_image_pil) mask_level = self.mask_image.level_count - 1 self.rgb_mask_pil = self.mask_image.read_region((0, 0), mask_level, self.mask_image.level_dimensions[mask_level]) resize_factor = float(1.0 / pow(2, level_used - mask_level)) self.mask = cv2.resize(np.array(self.rgb_mask_pil), (0, 0), fx=resize_factor, fy=resize_factor) # self.rgb_image = cv2.resize(self.rgb_image, (0, 0), fx=0.50, fy=0.50) except OpenSlideUnsupportedFormatError: print('Exception: OpenSlideUnsupportedFormatError') return False return True def find_roi_normal(self): # self.mask = cv2.cvtColor(self.mask, cv2.CV_32SC1) hsv = cv2.cvtColor(self.rgb_image, cv2.COLOR_BGR2HSV) # [20, 20, 20] lower_red = np.array([30, 30, 30]) # [255, 255, 255] upper_red = np.array([200, 200, 200]) mask = cv2.inRange(hsv, lower_red, upper_red) res = cv2.bitwise_and(self.rgb_image, self.rgb_image, mask=mask) # (50, 50) close_kernel = np.ones((50, 50), dtype=np.uint8) close_kernel_tmp = np.ones((30, 30), dtype=np.uint8) image_close = Image.fromarray(cv2.morphologyEx(np.array(mask), cv2.MORPH_CLOSE, close_kernel)) image_close_tmp = Image.fromarray(cv2.morphologyEx(np.array(mask), cv2.MORPH_CLOSE, close_kernel_tmp)) # (30, 30) open_kernel = np.ones((30, 30), dtype=np.uint8) open_kernel_tmp = np.ones((30, 30), dtype=np.uint8) image_open = Image.fromarray(cv2.morphologyEx(np.array(image_close), cv2.MORPH_OPEN, open_kernel)) image_open_tmp = Image.fromarray(cv2.morphologyEx(np.array(image_close_tmp), cv2.MORPH_OPEN, open_kernel_tmp)) contour_rgb, bounding_boxes, contour_rgb_tmp = self.get_normal_image_contours(np.array(image_open), self.rgb_image, np.array(image_open_tmp)) # self.draw_bbox(bounding_boxes) self.display(contour_rgb, contour_rgb_tmp) def find_roi_tumor(self): # self.mask = cv2.cvtColor(self.mask, cv2.CV_32SC1) hsv = cv2.cvtColor(self.rgb_image, cv2.COLOR_BGR2HSV) lower_red = np.array([20, 20, 20]) upper_red = np.array([200, 200, 200]) mask = cv2.inRange(hsv, lower_red, upper_red) res = cv2.bitwise_and(self.rgb_image, self.rgb_image, mask=mask) # (50, 50) close_kernel = np.ones((20, 20), dtype=np.uint8) close_kernel_tmp = np.ones((50, 50), dtype=np.uint8) image_close = Image.fromarray(cv2.morphologyEx(np.array(mask), cv2.MORPH_CLOSE, close_kernel)) image_close_tmp = Image.fromarray(cv2.morphologyEx(np.array(mask), cv2.MORPH_CLOSE, close_kernel_tmp)) # (30, 30) open_kernel = np.ones((5, 5), dtype=np.uint8) open_kernel_tmp = np.ones((30, 30), dtype=np.uint8) image_open = Image.fromarray(cv2.morphologyEx(np.array(image_close), cv2.MORPH_OPEN, open_kernel)) image_open_tmp = Image.fromarray(cv2.morphologyEx(np.array(image_close_tmp), cv2.MORPH_OPEN, open_kernel_tmp)) contour_rgb, contour_mask, bounding_boxes, contour_rgb_tmp, contour_mask_tmp = self.get_tumor_image_contours( np.array(image_open), self.rgb_image, self.mask, np.array(image_open_tmp)) wsi_name = utils.get_filename_from_path(self.wsi_path) # cv2.imwrite(os.path.join(utils.THESIS_FIGURE_DIR, wsi_name) + '_hsv_mask.png', mask) # cv2.imwrite(os.path.join(utils.THESIS_FIGURE_DIR, wsi_name) + '_mask.png', self.mask) # cv2.imwrite(os.path.join(utils.THESIS_FIGURE_DIR, wsi_name) + '_image_close.png', np.array(image_close)) # cv2.imwrite(os.path.join(utils.THESIS_FIGURE_DIR, wsi_name) + '_image_open.png', np.array(image_open)) # rgb_bbox = self.rgb_image.copy() # for i, bounding_box in enumerate(bounding_boxes): # x = int(bounding_box[0]) # y = int(bounding_box[1]) # cv2.rectangle(rgb_bbox, (x, y), (x + bounding_box[2], y + bounding_box[3]), color=(0, 0, 255), # thickness=2) # cv2.imshow('contour_rgb', contour_rgb) # cv2.imshow('contour_bbox', rgb_bbox) # cv2.imwrite(os.path.join(utils.THESIS_FIGURE_DIR, wsi_name) + 'contour.png', contour_rgb) # cv2.imwrite(os.path.join(utils.THESIS_FIGURE_DIR, wsi_name) + '_bbox.png', rgb_bbox) cv2.imwrite(os.path.join(utils.THESIS_FIGURE_DIR, wsi_name) + '_hsv.png', hsv) cv2.imshow('hsv', hsv) cv2.waitKey(0) # self.display(contour_rgb, contour_rgb_tmp, contour_mask) def get_normal_image_contours(self, cont_img, rgb_image, cont_img_tmp): _, contours, _ = cv2.findContours(cont_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) _, contours_tmp, _ = cv2.findContours(cont_img_tmp, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # print(contours) boundingBoxes = [cv2.boundingRect(c) for c in contours] # print(boundingBoxes) contours_rgb_image_array = np.array(rgb_image) contours_rgb_image_array_tmp = 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(contours_rgb_image_array_tmp, contours_tmp, -1, line_color, 3) # cv2.drawContours(mask_image, contours_mask, -1, line_color, 3) return contours_rgb_image_array, boundingBoxes, contours_rgb_image_array_tmp def get_tumor_image_contours(self, cont_img, rgb_image, mask_image, cont_img_tmp): _, contours, _ = cv2.findContours(cont_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) _, contours_tmp, _ = cv2.findContours(cont_img_tmp, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # _, contours_mask, _ = cv2.findContours(mask_image, cv2.RETR_FLOODFILL, cv2.CHAIN_APPROX_SIMPLE) # print(contours) boundingBoxes = [cv2.boundingRect(c) for c in contours] # print(boundingBoxes) contours_rgb_image_array = np.array(rgb_image) contours_rgb_image_array_tmp = np.array(rgb_image) contours_mask_image_array = np.array(mask_image) contours_mask_image_array_tmp = np.array(mask_image) line_color = (255, 0, 0) # blue color code cv2.drawContours(contours_rgb_image_array, contours, -1, line_color, 3) cv2.drawContours(contours_rgb_image_array_tmp, contours_tmp, -1, line_color, 3) cv2.drawContours(contours_mask_image_array, contours, -1, line_color, 3) cv2.drawContours(contours_mask_image_array_tmp, contours_tmp, -1, line_color, 3) # cv2.drawContours(mask_image, contours_mask, -1, line_color, 3) return contours_rgb_image_array, contours_mask_image_array, boundingBoxes, contours_rgb_image_array_tmp, \ contours_mask_image_array_tmp def display(self, contour_rgb, contour_rgb_tmp, contour_mask=None): # cv2.imshow('rgb', self.rgb_image) # cv2.imshow('mask', mask) # cv2.imshow('image_close',np.array(image_close)) # cv2.imshow('image_open', np.array(image_open)) contour_rgb = cv2.resize(contour_rgb, (0, 0), fx=0.60, fy=0.60) cv2.imshow('contour_rgb', np.array(contour_rgb)) contour_rgb_tmp = cv2.resize(contour_rgb_tmp, (0, 0), fx=0.60, fy=0.60) cv2.imshow('contour_rgb_tmp', np.array(contour_rgb_tmp)) if contour_mask is not None: contour_mask = cv2.resize(contour_mask, (0, 0), fx=0.60, fy=0.60) cv2.imshow('contour_mask', np.array(contour_mask)) # contour_mask_tmp = cv2.resize(contour_mask_tmp, (0, 0), fx=0.40, fy=0.40) # cv2.imshow('contour_mask_tmp', np.array(contour_mask_tmp)) # cv2.imshow('contour_mask_only', np.array(contour_mask_only)) # cv2.imshow('wsi_mask', self.mask) # cv2.imshow('self_bb', np.array(self.rgb_image_pil)) def draw_bbox(self, bounding_boxes): draw = ImageDraw.Draw(self.rgb_image_pil) for i, bounding_box in enumerate(bounding_boxes): x = int(bounding_box[0]) y = int(bounding_box[1]) thickness = 5 for offset in range(thickness): draw.rectangle([x + offset, y + offset, x + offset + bounding_box[2], y + offset + bounding_box[3]], outline=(255, 0, 0)) 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
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
class QtCustomWindow( qt.QWidget ): def __init__( self, parent = None ) : qt.QWidget.__init__( self, parent ) self.current_x=0 self.current_y=0 #read region always works in the level 0 reference for reading locations self.current_zoom=0 self.image_width=750 self.image_height=750 self.setWindowTitle('Large scale image viewing module') self.resize(self.image_width,self.image_height) self.hbox = qt.QHBoxLayout(self) self.lbl = qt.QLabel(self) sp=qt.QSizePolicy() # Size policy setting sp.setHorizontalPolicy(qt.QSizePolicy.Ignored) sp.setVerticalPolicy(qt.QSizePolicy.Ignored) self.lbl.setSizePolicy(sp) self.setLayout(self.hbox) self.filename=None # #self.mouseInitialPosition self.panningLock=False self.zoomingLock=False self.resizeLock=True self.initUI() def initUI(self): # initialize the widget, make the openslide object pointing to the # multilevel image and initialize the view to the top left corner if self.filename is not None: # openSlide object self.osr = OS(self.filename) self.level_count = self.osr.level_count self.current_x=0 self.current_y=0 self.current_zoom=self.level_count-1 self.level_dimensions=self.osr.level_dimensions self.step =int(64*self.osr.level_downsamples[self.current_zoom]) self.hbox.addWidget(self.lbl) self.updatePixmap() self.show() self.resizeLock=False def updatePixmap(self): self.panningLock=True im=self.osr.read_region((self.current_x,self.current_y),self.current_zoom,(self.image_width,self.image_height)) data = im.tostring('raw', 'RGBA') image = qt.QImage(data, im.size[0], im.size[1], qt.QImage.Format_ARGB32) pix = qt.QPixmap.fromImage(image) self.lbl.setPixmap(pix) self.lbl.show() self.panningLock=False def keyPressEvent( self, event ) : key = event.text() if key=="s": self.current_y += self.step self.current_y = min(self.current_y,int( self.level_dimensions[0][1]-self.image_height*self.osr.level_downsamples[self.current_zoom])) elif key=="w": #larger steps for zoomed out images self.current_y -= self.step self.current_y = max(0, self.current_y) elif key=="a": #larger steps for zoomed out images self.current_x -= self.step self.current_x = max(0, self.current_x) elif key=="d": #larger steps for zoomed out images self.current_x += self.step self.current_x = min(self.current_x,int( self.level_dimensions[0][0]-self.image_width*self.osr.level_downsamples[self.current_zoom])) elif key=="q": new_zoom = min(self.current_zoom+1,self.level_count-1) self.updateCorner(new_zoom) elif key=="e": new_zoom = max(self.current_zoom-1,0) self.updateCorner(new_zoom) # print "x: %s" % self.current_x # print "y: %s" % self.current_y # print "zoom: %s" % self.current_zoom # print "downsample: %s" % self.osr.level_downsamples[self.current_zoom] self.updatePixmap() def updateCorner(self,new_zoom, new_center=None): #updates the current_x and current_y values to preserve the desired image center if new_center: center_x=new_center[0] center_y=new_center[1] else: center_x=self.image_width /2 center_y=self.image_height /2 #while zooming self.current_x=int(self.current_x + center_x *self.osr.level_downsamples[self.current_zoom]- self.image_width /2 * self.osr.level_downsamples[new_zoom]) self.current_y=int(self.current_y + center_y *self.osr.level_downsamples[self.current_zoom]- self.image_height /2 * self.osr.level_downsamples[new_zoom]) #update step size and current zoom self.current_zoom=new_zoom #larger steps for zoomed out images self.step =int(64*self.osr.level_downsamples[self.current_zoom]) ################################################################################ ################################################################################ def mousePressEvent(self, QMouseEvent): #cursor =qt.QCursor() position = QMouseEvent.pos() xpos = QMouseEvent.x() ypos = QMouseEvent.y() self.mouseInitialPosition=np.array([QMouseEvent.x(), QMouseEvent.y()]) def mouseMoveEvent(self, QMouseEvent): #cursor =qt.QCursor() if self.panningLock: return currentPosition=np.array([QMouseEvent.x(), QMouseEvent.y()]) difference=self.mouseInitialPosition-currentPosition self.mouseInitialPosition=currentPosition self.current_x= self.current_x + int(difference[0]*self.osr.level_downsamples[self.current_zoom]) self.current_x = max(0, self.current_x) self.current_x = min(self.current_x,int( self.level_dimensions[0][0]-self.image_width*self.osr.level_downsamples[self.current_zoom])) self.current_y= self.current_y + int(difference[1]*self.osr.level_downsamples[self.current_zoom]) self.current_y = max(0, self.current_y) self.current_y = min(self.current_y,int( self.level_dimensions[0][1]-self.image_height*self.osr.level_downsamples[self.current_zoom])) self.updatePixmap() def wheelEvent(self,QMouseEvent): new_zoom = min(max(self.current_zoom- QMouseEvent.delta()/120,0), self.level_count-1) if new_zoom != self.current_zoom: new_center=[0,0] new_center[0]= int(QMouseEvent.x()) new_center[1]= int(QMouseEvent.y()) self.updateCorner(new_zoom, new_center) self.updatePixmap() def resizeEvent(self,event): if self.resizeLock: return self.resizeLock=True difw=event.size().width()-event.oldSize().width() difh = event.size().height()-event.oldSize().height() self.image_width+=difw self.image_height+=difh self.updatePixmap() self.resizeLock=False