如何合并使用 openslide-python 获得的图块

How to merge tiles obtained using openslide-python

我正在尝试以正确的顺序组合图块,以便它们最终成为相同的整个幻灯片图像(.svs 文件)。

.svs 文件是根据下面的函数从文件路径中读取的:

def open_slide(filepath = None):
try:
    slide = openslide.open_slide(filepath)
   
except OpenSlideError as o:
    print("Error" + str(o))
    
    slide = None
except FileNotFoundError as f:
    print("Error" +  str(f))
    slide = None
return slide

在下图中,我正在尝试合并我使用 openslide-python 的 DeepZoom 生成器获得的图块(参见下面的代码片段)

def create_tile_generator(slide, tile_size, overlap):
 gen = DeepZoomGenerator(slide, tile_size=tile_size, overlap=overlap, limit_bounds=False)

这就是我将 .svs 拆分成它的图块的方式:

def split_wsi_to_tiles(wsi_path = None):
print("splitting wsi into tiles")
tile_indices = process_slide(slide_num = SLIDE_NUM , filepath= wsi_path, tile_size = TILE_SIZE, overlap = OVERLAP)
i = 0
tile_indices_savepath  = os.path.join(os.getcwd(),"saved","tile_indices")
save_file(filepath = tile_indices_savepath,filename=name,file= tile_indices)
for ti in tile_indices:
    suffix =  str(i)
    (slide_num,tile) = process_tile_index(tile_index =ti,filepath = svs_path )
    tile = cv2.cvtColor(tile, cv2.COLOR_BGR2RGB)
    cv2.imwrite(save_path + suffix + ext,tile)
    i = i + 1
print("done splitting wsi into tiles")
return tile_indices_savepath

辅助函数process_slide和process_tile_index如下

def process_slide(slide_num =1 , filepath= None, tile_size = 256, overlap = 0):
slide = open_slide(filepath = filepath)
generator = create_tile_generator(slide, tile_size, overlap)
zoom_level = get_40x_zoom_level(slide, generator)
print("zoom level set to " + str(zoom_level))
cols, rows = generator.level_tiles[zoom_level - 1]
tile_indices = [(slide_num, tile_size, overlap, zoom_level, col, row)
              for col in range(cols) for row in range(rows)]
return tile_indices
def process_tile_index(tile_index=None,filepath= None):
    slide_num, tile_size, overlap, zoom_level, col, row = tile_index
    slide = open_slide(filepath = filepath)
    generator = create_tile_generator(slide, tile_size, overlap)
    tile = np.asarray(generator.get_tile(zoom_level, (col, row)))
    return (slide_num, tile)

get_40x_zoom_level函数说明:

def get_40x_zoom_level(slide, generator):
global level
highest_zoom_level = generator.level_count - 1  # 0-based indexing
try:
    mag = int(slide.properties[openslide.PROPERTY_NAME_OBJECTIVE_POWER])
    
    offset = math.floor((mag / 40) / 2)
    level = highest_zoom_level - offset
except (ValueError, KeyError) as e:
    level = highest_zoom_level
print("zoom level set at " +str(level) )
save_file(filepath= os.path.join(os.getcwd(),"saved"),filename = "level.pickle",file = level)
return level

这就是我尝试将图块合并回其整个幻灯片图像的方式(不一定是 .svs 格式,但相同的图像):

def merge_tiles_to_wsi(tile_path= None,wsi_path = None):
print("merging tiles into wsi")
tile_indices = load_file(filepath = tile_indices_savepath,filename = name)
slide = open_slide(filepath = wsi_path)

level = load_file(filepath= os.path.join(os.getcwd(),"saved"),filename = "level.pickle")
generator = create_tile_generator(slide, TILE_SIZE, OVERLAP)
slide_dims = generator.level_dimensions[level]
row_size = slide_dims[0]
col_size = slide_dims[1]
channel_size = 3
slide_shape = (row_size,col_size,channel_size)
print("shape of slide is " + str(slide_shape))
wsi = np.zeros(slide_shape)
for ti in tile_indices:
    slide_num, tile_size, overlap, zoom_level, col, row  = ti
    generator = create_tile_generator(slide, tile_size, overlap)
    tile = np.asarray(generator.get_tile(zoom_level, (col, row)))
    
    row_length = tile.shape[0]
    col_length = tile.shape[1]
    row_end = row + row_length
    col_end = col + col_length
    print("col: " + str(col) + " row: " + str(row) + str(wsi[row:row_end,col:col_end].shape) + " " + str(tile.shape))
    wsi[row:row_end,col:col_end] = tile
    # view_image(img= wsi)
print("merging tiles into wsi")

return wsi

这是最终输出的样子 out.png

libvips can do this merge and join for you. You can call it from pyvips,Python绑定。

要加载 svs 图像并将其拆分为图块,您可以这样写:

import pyvips

image = pyvips.Image.new_from_file("my-slide.svs")
image.dzsave("my-deepzoom")

它会写入 my-deepzoom.dzi 和一个包含所有图块的目录 my-deepzoom_files。您可以调整很多参数,请参阅文档中的章节:

https://libvips.github.io/libvips/API/current/Making-image-pyramids.md.html

速度非常快,甚至可以在最普通的硬件上制作任何大小的金字塔。

您可以使用 arrayjoin 重新组合图块以形成图像。您给它一个按行优先顺序排列的图像列表,并将 across 设置为每行的图像数。例如:

import pyvips

tiles = [pyvips.Image.new_from_file(f"{x}_{y}.jpeg", access="sequential")
         for y in range(height) for x in range(width)] 
image = pyvips.Image.arrayjoin(tiles, across=width)
image.write_to_file("huge.tif", compression="jpeg", tile=True)

速度非常快,可以连接非常大的图像数组。