Skip to content

Instantly share code, notes, and snippets.

@jmuhlich
Created July 24, 2018 19:35
Show Gist options
  • Save jmuhlich/9c8d3db4ab6c1c0b3fd7c2941b954080 to your computer and use it in GitHub Desktop.
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
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)
@Yu-AnChen
Copy link

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"?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment