gttr-cell-quantification / tests /test_loader.py
stevafernandes's picture
update app
448496b verified
Raw History Blame Contribute Delete
4.05 kB
"""Loader and app-input tests: stacks, channel order, bit depth, projections, 1-based z, background default."""
import os, sys, json
import numpy as np, pytest, tifffile
from PIL import Image
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import infer
from quantify import measure
def test_multipage_gray_tiff_is_max_projected(tmp_path):
z = np.zeros((5, 64, 64), np.uint8)
for i in range(5): z[i, 10 + i, 10 + i] = 100 + i
p = tmp_path / 'stack.tif'; tifffile.imwrite(p, z)
rgb, info = infer.load_image(str(p))
assert rgb.shape == (64, 64, 3)
assert info['projection'] == 'maximum projection of 5 z-slices'
assert all(rgb[10 + i, 10 + i, 0] == 100 + i for i in range(5)) and rgb[..., 2].max() == 0
rgb2, info2 = infer.load_image(str(p), projection='slice', z_index=2)
assert rgb2[12, 12, 0] == 102 and rgb2[10, 10, 0] == 0 and info2['z'] == 2
def test_two_channel_hyperstack_uses_lsm_channel_order(tmp_path):
a = np.zeros((3, 2, 64, 64), np.uint16) # Z, C, Y, X: channel 0 = DAPI, channel 1 = red (12-bit)
a[:, 0, 5, 5] = 4000; a[1, 1, 20, 20] = 1600
p = tmp_path / 'hyper.tif'; tifffile.imwrite(p, a, imagej=True, metadata={'axes': 'ZCYX'})
rgb, info = infer.load_image(str(p))
assert info['bit_divisor'] == 16.0
assert rgb[20, 20, 0] == 100 and rgb[5, 5, 2] == 250 and rgb[5, 5, 0] == 0
assert 'channel 2 = GTTR' in info['channels']
def test_grayscale_rgb_goes_to_red_only(tmp_path):
g = (np.random.RandomState(0).rand(64, 64) * 255).astype(np.uint8)
p = tmp_path / 'gray_rgb.png'; Image.fromarray(np.stack([g] * 3, -1)).save(p)
rgb, info = infer.load_image(str(p))
assert np.array_equal(rgb[..., 0], g) and rgb[..., 2].max() == 0 and 'grayscale' in info['channels']
def test_16bit_scaling_rules(tmp_path):
for mx, div in ((200, 1.0), (4000, 16.0), (60000, 256.0)):
a = np.zeros((64, 64), np.uint16); a[0, 0] = mx
p = tmp_path / f'u16_{mx}.tif'; tifffile.imwrite(p, a)
rgb, info = infer.load_image(str(p))
assert info['bit_divisor'] == div and rgb[0, 0, 0] == int(mx // div)
def test_time_series_uses_first_time_point(tmp_path):
a = np.zeros((4, 1, 1, 32, 32), np.uint8); a[0, 0, 0, 1, 1] = 9; a[3, 0, 0, 2, 2] = 200
p = tmp_path / 't.tif'; tifffile.imwrite(p, a, imagej=True, metadata={'axes': 'TZCYX'})
rgb, info = infer.load_image(str(p))
assert rgb[1, 1, 0] == 9 and rgb[2, 2, 0] == 0 and 'time series' in info['note_time']
def test_default_background_is_darkest_patch_not_mode():
red = np.full((300, 300), 30, np.uint8)
red[:, 150:] = 254 # half the field saturated, no cells there
cells = np.zeros((300, 300), np.int32)
rows, bg, _ = measure(red, cells, cells, 9.6)
assert bg == pytest.approx(30.0)
def test_app_inputs_one_based_z_and_validation():
os.environ.setdefault('GTTR_MODEL_DIR', os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), '..', 'work', 'models_stub'))
import gradio as gr
import app
path, px, fb, projection, z_index, bg = app._parse_inputs('x.lsm', app.CAL_CHOICES[1], 9.4, app.Z_CHOICES[1], 1, app.BG_CHOICES[1], 2.5)
assert (px, projection, z_index, bg) == (9.4, 'slice', 0, 2.5)
path, px, fb, projection, z_index, bg = app._parse_inputs('x.lsm', app.CAL_CHOICES[0], 9.4, app.Z_CHOICES[0], 1, app.BG_CHOICES[0], 0)
assert px is None and fb == 9.4 and projection == 'max' and bg is None # metadata first, entered value as fallback
for bad in [dict(px_value=0), dict(px_value=None), dict(px_value=0, cal_mode=app.CAL_CHOICES[0]), dict(z_value='a', z_mode=app.Z_CHOICES[1]),
dict(bg_value=-1, bg_mode=app.BG_CHOICES[1]), dict(bg_value=300, bg_mode=app.BG_CHOICES[1])]:
kw = dict(file='x.lsm', cal_mode=app.CAL_CHOICES[1], px_value=9.4, z_mode=app.Z_CHOICES[0], z_value=1, bg_mode=app.BG_CHOICES[0], bg_value=0)
kw.update(bad)
with pytest.raises(gr.Error):
app._parse_inputs(**kw)