Skip to content

Commit

Permalink
Codify the expectation that in-place experiment construction does not…
Browse files Browse the repository at this point in the history
… rely on TileFetcher data

When we construct an experiment in-place, the source data comes from the tiles already on disk.  Therefore, whatever image data we provide the TileFetcher that we pass into `write_experiment_json` should be discarded.  This verifies that the data from the tiles on the disk are what gets constituted into the experiment, and that the data returned by TileFetcher is ignored.

Test plan: passed the test.
Fixes #1388
  • Loading branch information
Tony Tung committed Jun 12, 2019
1 parent 803d930 commit 14cb0de
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 25 deletions.
43 changes: 25 additions & 18 deletions starfish/core/experiment/builder/test/inplace_script.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
import os
import sys
from pathlib import Path
from typing import cast, Mapping, Tuple, Union
from typing import Mapping, Tuple, Union

import numpy as np
from skimage.io import imread, imsave
from skimage.io import imsave
from slicedimage import ImageFormat

from starfish.core.experiment.builder import FetchedTile, TileFetcher, write_experiment_json
Expand All @@ -19,7 +19,15 @@
SHAPE = {Axes.Y: 500, Axes.X: 1390}


class InplaceTile(InplaceFetchedTile):
def tile_fn(input_dir: Path, prefix: str, fov: int, r: int, ch: int, zplane: int) -> Path:
filename = '{}-Z{}-H{}-C{}.tiff'.format(prefix, zplane, r, ch)
return input_dir / f"fov_{fov:03}" / filename


class ZeroesInplaceTile(InplaceFetchedTile):
"""These tiles contain all zeroes. This is irrelevant to the actual experiment construction
because we are using in-place mode. That means we build references to the files already on-disk
and any data returned is merely metadata (tile shape, tile coordinates, and tile checksum."""
def __init__(self, file_path: Path):
self.file_path = file_path

Expand All @@ -43,22 +51,21 @@ def sha256(self) -> str:
return hasher.hexdigest()

def tile_data(self) -> np.ndarray:
return imread(os.fspath(self.file_path))
return np.zeros(shape=(SHAPE[Axes.Y], SHAPE[Axes.X]), dtype=np.float32)

@property
def filepath(self) -> Path:
return self.file_path


class InplaceFetcher(TileFetcher):
class ZeroesInplaceFetcher(TileFetcher):
def __init__(self, input_dir: Path, prefix: str):
self.input_dir = input_dir
self.prefix = prefix

def get_tile(self, fov: int, r: int, ch: int, z: int) -> FetchedTile:
filename = '{}-Z{}-H{}-C{}.tiff'.format(self.prefix, z, r, ch)
file_path = self.input_dir / f"fov_{fov:03}" / filename
return InplaceTile(file_path)
file_path = tile_fn(self.input_dir, self.prefix, fov, r, ch, z)
return ZeroesInplaceTile(file_path)


def fov_path_generator(parent_toc_path: Path, toc_name: str) -> Path:
Expand All @@ -82,9 +89,9 @@ def add_codebook(experiment_json_doc):
tile_format=ImageFormat.TIFF,
primary_image_dimensions=primary_image_dimensions,
aux_name_to_dimensions=aux_name_to_dimensions,
primary_tile_fetcher=InplaceFetcher(image_dir, FieldOfView.PRIMARY_IMAGES),
primary_tile_fetcher=ZeroesInplaceFetcher(image_dir, FieldOfView.PRIMARY_IMAGES),
aux_tile_fetcher={
aux_img_name: InplaceFetcher(image_dir, aux_img_name)
aux_img_name: ZeroesInplaceFetcher(image_dir, aux_img_name)
for aux_img_name in aux_name_to_dimensions.keys()
},
postprocess_func=add_codebook,
Expand All @@ -95,19 +102,21 @@ def add_codebook(experiment_json_doc):


def write_image(
base_path: Path,
prefix: str,
num_fovs: int,
image_dimensions: Mapping[Union[Axes, str], int],
fetcher: InplaceFetcher
):
"""Writes the constituent tiles of an image to disk. The tiles are made up with random noise.
"""
for fov_num in range(num_fovs):
for r in range(image_dimensions[Axes.ROUND]):
for ch in range(image_dimensions[Axes.CH]):
for zplane in range(image_dimensions[Axes.ZPLANE]):
tile = cast(InplaceFetchedTile, fetcher.get_tile(fov_num, r, ch, zplane))
path = tile.filepath
path = tile_fn(base_path, prefix, fov_num, r, ch, zplane)
path.parent.mkdir(parents=True, exist_ok=True)

data = np.random.random(size=(SHAPE[Axes.Y], SHAPE[Axes.X]))
data = np.random.random(size=(SHAPE[Axes.Y], SHAPE[Axes.X])).astype(np.float32)

imsave(os.fspath(path), data, plugin="tifffile")

Expand All @@ -130,12 +139,10 @@ def write_inplace(path: str, num_fovs: int = 2):
tmpdir = Path(path)

# write out the image files
primary_fetcher = InplaceFetcher(tmpdir, FieldOfView.PRIMARY_IMAGES)
write_image(num_fovs, primary_image_dimensions, primary_fetcher)
write_image(tmpdir, FieldOfView.PRIMARY_IMAGES, num_fovs, primary_image_dimensions)

for aux_img_name in aux_name_to_dimensions.keys():
aux_fetcher = InplaceFetcher(tmpdir, aux_img_name)
write_image(num_fovs, aux_name_to_dimensions[aux_img_name], aux_fetcher)
write_image(tmpdir, aux_img_name, num_fovs, aux_name_to_dimensions[aux_img_name])

# format the experiment.
format_data(tmpdir, primary_image_dimensions, aux_name_to_dimensions, num_fovs)
Expand Down
20 changes: 13 additions & 7 deletions starfish/core/experiment/builder/test/test_inplace.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
import os
import subprocess
import sys
import tempfile
from pathlib import Path

from starfish.core.experiment.experiment import Experiment
import numpy as np

from starfish.core.experiment.experiment import Experiment, FieldOfView

def test_inplace():

def test_inplace(tmpdir):
this = Path(__file__)
inplace_script = this.parent / "inplace_script.py"

with tempfile.TemporaryDirectory() as tmpdir_str:
tmpdir = Path(tmpdir_str)
tmpdir_path = Path(tmpdir)

subprocess.check_call([sys.executable, os.fspath(inplace_script), os.fspath(tmpdir_path)])

subprocess.check_call([sys.executable, os.fspath(inplace_script), os.fspath(tmpdir)])
Experiment.from_json(os.fspath(tmpdir / "experiment.json"))
# load up the experiment, and select an image. Ensure that it has non-zero data. This is to
# verify that we are sourcing the data from the tiles that were already on-disk, and not the
# artificially zero'ed tiles that we feed the experiment builder.
experiment = Experiment.from_json(os.fspath(tmpdir_path / "experiment.json"))
primary_image = experiment.fov().get_image(FieldOfView.PRIMARY_IMAGES)
assert not np.allclose(primary_image.xarray, 0)

0 comments on commit 14cb0de

Please sign in to comment.