Created
July 24, 2018 19:35
-
-
Save jmuhlich/9c8d3db4ab6c1c0b3fd7c2941b954080 to your computer and use it in GitHub Desktop.
Renders RGB JPEG pyramid tiles from OME-TIFF pyramids for the melanoma triplet dataset
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import sys | |
import itertools | |
import pathlib | |
import json | |
import numpy as np | |
import pytiff | |
from PIL import Image | |
def composite_channel(target, image, color, range_min, range_max): | |
''' Render _image_ in pseudocolor and composite into _target_ | |
Args: | |
target: Numpy float32 array containing composition target image | |
image: Numpy uint16 array of image to render and composite | |
color: Color as r, g, b float array, 0-1 | |
range_min: Threshhold range minimum, 0-65535 | |
range_max: Threshhold range maximum, 0-65535 | |
''' | |
f_image = (image.astype('float32') - range_min) / (range_max - range_min) | |
for i, component in enumerate(color): | |
target[:, :, i] += f_image * component | |
TILE_SIZE = 1024 | |
EXT = 'jpg' | |
# List markers as: green, white, red | |
RENDER_GROUPS = [ | |
['CD8', 'FOXP3', 'CD3'], | |
['PD1', 'CD3', 'PDL1'], | |
['pS6', 'pRB', 'HES1'], | |
['S100', 'CD45RO', 'SMA'], | |
] | |
COLORS = [ | |
[0, 0, 1], # blue | |
[0, 1, 0], # green | |
[1, 1, 1], # white | |
[1, 0, 0], # red | |
] | |
input_file_path = pathlib.Path(sys.argv[1]) | |
render_settings_path = pathlib.Path(sys.argv[2]) | |
output_path = pathlib.Path(sys.argv[3]) | |
tiff = pytiff.Tiff(str(input_file_path)) | |
with open(render_settings_path) as f: | |
render_settings = json.load(f) | |
channels = { | |
v['label']: dict(index=int(k), **v) | |
for k, v in render_settings['channels'].items() | |
} | |
num_channels = len(render_settings['channels']) | |
assert tiff.number_of_pages % num_channels == 0, "Pyramid/channel mismatch" | |
num_levels = tiff.number_of_pages // num_channels | |
for level in range(num_levels): | |
page_base = level * num_channels | |
tiff.set_page(page_base) | |
ny = int(np.ceil(tiff.shape[0] / TILE_SIZE)) | |
nx = int(np.ceil(tiff.shape[1] / TILE_SIZE)) | |
print(f"level {level} ({ny} x {nx})") | |
for ty, tx in itertools.product(range(0, ny), range(0, nx)): | |
iy = ty * TILE_SIZE | |
ix = tx * TILE_SIZE | |
filename = f'{level}_{tx}_{ty}.{EXT}' | |
print(f" {filename}") | |
for marker_list in RENDER_GROUPS: | |
group_dir = pathlib.Path('-'.join(marker_list)) | |
(output_path / group_dir).mkdir(exist_ok=True) | |
print(f" {group_dir}") | |
marker_list = ['DNA'] + marker_list | |
for i, (marker, color) in enumerate(zip(marker_list, COLORS)): | |
ch_info = channels[marker] | |
tiff.set_page(page_base + ch_info['index']) | |
tile = tiff[iy:iy+TILE_SIZE, ix:ix+TILE_SIZE] | |
if i == 0: | |
target = np.zeros(tile.shape + (3,), np.float32) | |
composite_channel( | |
target, tile, color, ch_info['start'], ch_info['end'] | |
) | |
np.clip(target, 0, 1, out=target) | |
np.power(target, 1/2.2, out=target) | |
target_u8 = (target * 255).astype(np.uint8) | |
img = Image.frombytes('RGB', target.T.shape[1:], target_u8.tobytes()) | |
img.save(str(output_path / group_dir / filename), quality=85) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The
pytiff.Tiff
reader doesn't like the "µ" character in the xml metadata.Is "µm" a standard value in bio-formats? Should/can we change it to "micron"?