def composite_codebook() -> Tuple[Codebook, ImageStack]: """ Produce an Imagestack representing a composite experiment where the first 2 rounds are multiplexed data and the last round is sequential data. Returns ------- Codebook : codebook containing codes that match the data ImageStack : noiseless ImageStack containing one spot per code in codebook """ codebook_data = [{ Features.CODEWORD: [{ Axes.ROUND.value: 0, Axes.CH.value: 0, Features.CODE_VALUE: 1 }, { Axes.ROUND.value: 1, Axes.CH.value: 1, Features.CODE_VALUE: 1 }], Features.TARGET: "GENE_A" }, { Features.CODEWORD: [{ Axes.ROUND.value: 0, Axes.CH.value: 1, Features.CODE_VALUE: 1 }, { Axes.ROUND.value: 1, Axes.CH.value: 0, Features.CODE_VALUE: 1 }], Features.TARGET: "GENE_B" }, { Features.CODEWORD: [{ Axes.ROUND.value: 2, Axes.CH.value: 0, Features.CODE_VALUE: 1 }], Features.TARGET: "GENE_C" }, { Features.CODEWORD: [{ Axes.ROUND.value: 2, Axes.CH.value: 1, Features.CODE_VALUE: 1 }], Features.TARGET: "GENE_D" }] codebook = Codebook.from_code_array(codebook_data) imagestack = create_imagestack_from_codebook( pixel_dimensions=(10, 100, 100), spot_coordinates=((4, 10, 90), (5, 90, 10), (6, 90, 10), (7, 90, 10)), codebook=codebook) return codebook, imagestack
def seqfishCodebook(nRound, nChannel, nCodes): def barcodeConv(lis, chs): barcode = np.zeros((len(lis), chs)) for i in range(len(lis)): barcode[i][lis[i]] = 1 return barcode def incrBarcode(lis, chs): currInd = len(lis) - 1 lis[currInd] += 1 while lis[currInd] == chs: lis[currInd] = 0 currInd -= 1 lis[currInd] += 1 return lis allCombo = np.zeros((nChannel**nRound, nRound, nChannel)) barcode = [0] * nRound for i in range(np.shape(allCombo)[0]): allCombo[i] = barcodeConv(barcode, nChannel) barcode = incrBarcode(barcode, nChannel) hammingDistance = 1 blanks = [] i = 0 while i < len(allCombo): blanks.append(allCombo[i]) j = i + 1 while j < len(allCombo): if np.count_nonzero( ~(allCombo[i] == allCombo[j])) / 2 <= hammingDistance: allCombo = allCombo[[ k for k in range(len(allCombo)) if k != j ]] else: j += 1 i += 1 data = np.asarray(blanks)[random.sample(range(len(blanks)), nCodes)] return Codebook.from_numpy(code_names=range(len(data)), n_round=nRound, n_channel=nChannel, data=data)
def from_json(cls, json_url: str) -> "Experiment": """ Construct an Experiment from an experiment.json file format specifier. Loads configuration from StarfishConfig. Parameters ---------- json_url : str file path or web link to an experiment.json file Returns ------- Experiment : Experiment object serving the requested experiment data """ config = StarfishConfig() if config.strict: valid = validate_sptx.validate(json_url) if not valid: raise Exception("validation failed") backend, name, baseurl = resolve_path_or_url(json_url, config.slicedimage) with backend.read_contextmanager(name) as fh: experiment_document = json.load(fh) version = cls.verify_version(experiment_document['version']) _, codebook_name, codebook_baseurl = resolve_url(experiment_document['codebook'], baseurl, config.slicedimage) codebook_absolute_url = pathjoin(codebook_baseurl, codebook_name) codebook = Codebook.open_json(codebook_absolute_url) extras = experiment_document['extras'] fovs: MutableSequence[FieldOfView] = list() fov_tilesets: MutableMapping[str, TileSet] if version < Version("5.0.0"): primary_image: Collection = Reader.parse_doc(experiment_document['primary_images'], baseurl, config.slicedimage) auxiliary_images: MutableMapping[str, Collection] = dict() for aux_image_type, aux_image_url in experiment_document['auxiliary_images'].items(): auxiliary_images[aux_image_type] = Reader.parse_doc( aux_image_url, baseurl, config.slicedimage) for fov_name, primary_tileset in primary_image.all_tilesets(): fov_tilesets = dict() fov_tilesets[FieldOfView.PRIMARY_IMAGES] = primary_tileset for aux_image_type, aux_image_collection in auxiliary_images.items(): aux_image_tileset = aux_image_collection.find_tileset(fov_name) if aux_image_tileset is not None: fov_tilesets[aux_image_type] = aux_image_tileset fov = FieldOfView(fov_name, image_tilesets=fov_tilesets) fovs.append(fov) else: images: MutableMapping[str, Collection] = dict() all_fov_names: MutableSet[str] = set() for image_type, image_url in experiment_document['images'].items(): image = Reader.parse_doc(image_url, baseurl, config.slicedimage) images[image_type] = image for fov_name, _ in image.all_tilesets(): all_fov_names.add(fov_name) for fov_name in all_fov_names: fov_tilesets = dict() for image_type, image_collection in images.items(): image_tileset = image_collection.find_tileset(fov_name) if image_tileset is not None: fov_tilesets[image_type] = image_tileset fov = FieldOfView(fov_name, image_tilesets=fov_tilesets) fovs.append(fov) return Experiment(fovs, codebook, extras, src_doc=experiment_document)
def write_irregular_experiment_json( path: str, tile_format: ImageFormat, *, image_tile_identifiers: Mapping[str, Iterable[TileIdentifier]], tile_fetchers: Mapping[str, TileFetcher], postprocess_func: Optional[Callable[[dict], dict]]=None, default_shape: Optional[Mapping[Axes, int]]=None, fov_path_generator: Callable[[Path, str], Path] = None, tile_opener: Optional[Callable[[Path, Tile, str], BinaryIO]] = None, ) -> None: """ Build and returns a top-level experiment description with the following characteristics: Parameters ---------- path : str Directory to write the files to. tile_format : ImageFormat File format to write the tiles as. image_tile_identifiers : Mapping[str, Iterable[TileIdentifier]] Dictionary mapping the image type to an iterable of TileIdentifiers. tile_fetchers : Mapping[str, TileFetcher] Dictionary mapping the image type to a TileFetcher. postprocess_func : Optional[Callable[[dict], dict]] If provided, this is called with the experiment document for any postprocessing. An example of this would be to add something to one of the top-level extras field. The callable should return what is to be written as the experiment document. default_shape : Optional[Tuple[int, int]] (default = None) Default shape for the tiles in this experiment. fov_path_generator : Optional[Callable[[Path, str], Path]] Generates the path for a FOV's json file. If one is not provided, the default generates the FOV's json file at the same level as the top-level json file for an image. If this is not provided, a reasonable default will be provided. tile_opener : Optional[Callable[[Path, Tile, str], BinaryIO]] Callable that gets invoked with the following arguments: 1. the directory of the experiment that is being constructed, 2. the tile that is being written, and 3. the file extension that the tile should be written with. The callable is expected to return an open file handle. If this is not provided, a reasonable default will be provided. """ if postprocess_func is None: postprocess_func = lambda doc: doc if fov_path_generator is None: fov_path_generator = _fov_path_generator if tile_opener is None: tile_opener = _tile_opener experiment_doc: Dict[str, Any] = { 'version': str(CURRENT_VERSION), 'images': {}, 'extras': {}, } for image_type, tile_identifiers in image_tile_identifiers.items(): tile_fetcher = tile_fetchers[image_type] image = build_irregular_image(tile_identifiers, tile_fetcher, default_shape) Writer.write_to_path( image, os.path.join(path, f"{image_type}.json"), pretty=True, partition_path_generator=fov_path_generator, tile_opener=tile_opener, tile_format=tile_format, ) experiment_doc['images'][image_type] = f"{image_type}.json" experiment_doc["codebook"] = "codebook.json" codebook_array = [ { "codeword": [ {"r": 0, "c": 0, "v": 1}, ], "target": "PLEASE_REPLACE_ME" }, ] codebook = Codebook.from_code_array(codebook_array) codebook_json_filename = "codebook.json" codebook.to_json(os.path.join(path, codebook_json_filename)) experiment_doc = postprocess_func(experiment_doc) with open(os.path.join(path, "experiment.json"), "w") as fh: json.dump(experiment_doc, fh, indent=4)
def write_experiment_json( path: str, fov_count: int, tile_format: ImageFormat, *, primary_image_dimensions: Mapping[Union[str, Axes], int], aux_name_to_dimensions: Mapping[str, Mapping[Union[str, Axes], int]], primary_tile_fetcher: Optional[TileFetcher] = None, aux_tile_fetcher: Optional[Mapping[str, TileFetcher]] = None, postprocess_func: Optional[Callable[[dict], dict]] = None, default_shape: Optional[Mapping[Axes, int]] = None, dimension_order: Sequence[Axes] = (Axes.ZPLANE, Axes.ROUND, Axes.CH), fov_path_generator: Callable[[Path, str], Path] = _fov_path_generator, tile_opener: Callable[[Path, Tile, str], BinaryIO] = _tile_opener, ) -> None: """ Build and returns a top-level experiment description with the following characteristics: Parameters ---------- path : str Directory to write the files to. fov_count : int Number of fields of view in this experiment. tile_format : ImageFormat File format to write the tiles as. primary_image_dimensions : Mapping[Union[str, Axes], int] Dictionary mapping dimension name to dimension size for the primary image. aux_name_to_dimensions : Mapping[str, Mapping[Union[str, Axes], int]] Dictionary mapping the auxiliary image type to dictionaries, which map from dimension name to dimension size. primary_tile_fetcher : Optional[TileFetcher] TileFetcher for primary images. Set this if you want specific image data to be set for the primary images. If not provided, the image data is set to random noise via :class:`RandomNoiseTileFetcher`. aux_tile_fetcher : Optional[Mapping[str, TileFetcher]] TileFetchers for auxiliary images. Set this if you want specific image data to be set for one or more aux image types. If not provided for any given aux image, the image data is set to random noise via :class:`RandomNoiseTileFetcher`. postprocess_func : Optional[Callable[[dict], dict]] If provided, this is called with the experiment document for any postprocessing. An example of this would be to add something to one of the top-level extras field. The callable should return what is to be written as the experiment document. default_shape : Optional[Tuple[int, int]] (default = None) Default shape for the tiles in this experiment. dimension_order : Sequence[Axes] Ordering for which dimensions vary, in order of the slowest changing dimension to the fastest. For instance, if the order is (ROUND, Z, CH) and each dimension has size 2, then the sequence is: (ROUND=0, CH=0, Z=0) (ROUND=0, CH=1, Z=0) (ROUND=0, CH=0, Z=1) (ROUND=0, CH=1, Z=1) (ROUND=1, CH=0, Z=0) (ROUND=1, CH=1, Z=0) (ROUND=1, CH=0, Z=1) (ROUND=1, CH=1, Z=1) (default = (Axes.Z, Axes.ROUND, Axes.CH)) fov_path_generator : Callable[[Path, str], Path] Generates the path for a FOV's json file. If one is not provided, the default generates the FOV's json file at the same level as the top-level json file for an image. tile_opener : Callable[[Path, Tile, str], BinaryIO] """ if primary_tile_fetcher is None: primary_tile_fetcher = tile_fetcher_factory(RandomNoiseTile) if aux_tile_fetcher is None: aux_tile_fetcher = {} if postprocess_func is None: postprocess_func = lambda doc: doc experiment_doc: Dict[str, Any] = { 'version': str(CURRENT_VERSION), 'images': {}, 'extras': {}, } primary_image = build_image( range(fov_count), range(primary_image_dimensions[Axes.ROUND]), range(primary_image_dimensions[Axes.CH]), range(primary_image_dimensions[Axes.ZPLANE]), primary_tile_fetcher, axes_order=dimension_order, default_shape=default_shape, ) Writer.write_to_path( primary_image, os.path.join(path, "primary_images.json"), pretty=True, partition_path_generator=fov_path_generator, tile_opener=tile_opener, tile_format=tile_format, ) experiment_doc['images']['primary'] = "primary_images.json" for aux_name, aux_dimensions in aux_name_to_dimensions.items(): if aux_dimensions is None: continue auxiliary_image = build_image( range(fov_count), range(aux_dimensions[Axes.ROUND]), range(aux_dimensions[Axes.CH]), range(aux_dimensions[Axes.ZPLANE]), aux_tile_fetcher.get(aux_name, tile_fetcher_factory(RandomNoiseTile)), axes_order=dimension_order, default_shape=default_shape, ) Writer.write_to_path( auxiliary_image, os.path.join(path, "{}.json".format(aux_name)), pretty=True, partition_path_generator=fov_path_generator, tile_opener=tile_opener, tile_format=tile_format, ) experiment_doc['images'][aux_name] = "{}.json".format(aux_name) experiment_doc["codebook"] = "codebook.json" codebook_array = [ { "codeword": [ { "r": 0, "c": 0, "v": 1 }, ], "target": "PLEASE_REPLACE_ME" }, ] codebook = Codebook.from_code_array(codebook_array) codebook_json_filename = "codebook.json" codebook.to_json(os.path.join(path, codebook_json_filename)) experiment_doc = postprocess_func(experiment_doc) with open(os.path.join(path, "experiment.json"), "w") as fh: json.dump(experiment_doc, fh, indent=4)
def composite_codebook_mixed_round() -> Tuple[Codebook, ImageStack]: """ Produce an Imagestack representing a composite experiment where the first 2 and a half rounds are multiplexed data and the last round and a half is sequential data. This represents the type of hybrid experiment happening at the allen. Returns ------- Codebook : codebook containing codes that match the data ImageStack : noiseless ImageStack containing one spot per code in codebook """ codebook_data = [ { Features.CODEWORD: [{ Axes.ROUND.value: 0, Axes.CH.value: 0, Features.CODE_VALUE: 1 }, { Axes.ROUND.value: 1, Axes.CH.value: 1, Features.CODE_VALUE: 1 }], Features.TARGET: "GENE_A" }, { Features.CODEWORD: [{ Axes.ROUND.value: 0, Axes.CH.value: 1, Features.CODE_VALUE: 1 }, { Axes.ROUND.value: 1, Axes.CH.value: 0, Features.CODE_VALUE: 1 }], Features.TARGET: "GENE_B" }, { Features.CODEWORD: [{ Axes.ROUND.value: 2, Axes.CH.value: 0, Features.CODE_VALUE: 1 }, { Axes.ROUND.value: 1, Axes.CH.value: 1, Features.CODE_VALUE: 1 }], Features.TARGET: "GENE_C" }, { Features.CODEWORD: [{ Axes.ROUND.value: 2, Axes.CH.value: 2, Features.CODE_VALUE: 1 }], Features.TARGET: "GENE_D" }, { Features.CODEWORD: [{ Axes.ROUND.value: 3, Axes.CH.value: 0, Features.CODE_VALUE: 1 }], Features.TARGET: "GENE_E" }, ] codebook = Codebook.from_code_array(codebook_data) imagestack = create_imagestack_from_codebook( pixel_dimensions=(1, 10, 10), spot_coordinates=((0, 5, 6), (0, 2, 3), (0, 7, 1), (0, 8, 2), (0, 3, 6)), codebook=codebook) return codebook, imagestack
def compostie_codebook_seperate_stacks( ) -> Tuple[Codebook, ImageStack, ImageStack]: """ Produce separate different sized ImageStacks containing data from two different experiment types (multiplexed and non multiplexed) and one codebook with the information for both. Returns ------- Codebook : codebook containing codes that match the data ImageStack : noiseless ImageStack containing one spot per code in codebook """ # 3 round 3 ch multiplexed data multiplexed_data = [ { Features.CODEWORD: [{ Axes.ROUND.value: 0, Axes.CH.value: 0, Features.CODE_VALUE: 1 }, { Axes.ROUND.value: 2, Axes.CH.value: 1, Features.CODE_VALUE: 1 }], Features.TARGET: "GENE_A" }, { Features.CODEWORD: [{ Axes.ROUND.value: 0, Axes.CH.value: 1, Features.CODE_VALUE: 1 }, { Axes.ROUND.value: 1, Axes.CH.value: 2, Features.CODE_VALUE: 1 }], Features.TARGET: "GENE_B" }, ] codebook = Codebook.from_code_array(multiplexed_data) multiplexed_stack = create_imagestack_from_codebook( pixel_dimensions=(10, 100, 100), spot_coordinates=((4, 10, 90), (5, 90, 10)), codebook=codebook) # 3 rounds 1 ch non multiplexed data sequential_data = [{ Features.CODEWORD: [{ Axes.ROUND.value: 3, Axes.CH.value: 1, Features.CODE_VALUE: 1 }], Features.TARGET: "GENE_C" }, { Features.CODEWORD: [{ Axes.ROUND.value: 4, Axes.CH.value: 0, Features.CODE_VALUE: 1 }], Features.TARGET: "GENE_D" }, { Features.CODEWORD: [{ Axes.ROUND.value: 5, Axes.CH.value: 1, Features.CODE_VALUE: 1 }], Features.TARGET: "GENE_D" }] codebook = Codebook.from_code_array(sequential_data) sequential_stack = create_imagestack_from_codebook( pixel_dimensions=(10, 100, 100), spot_coordinates=((4, 10, 90), (5, 90, 10), (7, 90, 10)), codebook=codebook) sequential_stack = sequential_stack.sel(indexers={Axes.ROUND: (3, 6)}) # create codebook with combined target values combined = [{ Features.CODEWORD: [{ Axes.ROUND.value: 0, Axes.CH.value: 0, Features.CODE_VALUE: 1 }, { Axes.ROUND.value: 2, Axes.CH.value: 1, Features.CODE_VALUE: 1 }], Features.TARGET: "GENE_A" }, { Features.CODEWORD: [{ Axes.ROUND.value: 0, Axes.CH.value: 1, Features.CODE_VALUE: 1 }, { Axes.ROUND.value: 1, Axes.CH.value: 2, Features.CODE_VALUE: 1 }], Features.TARGET: "GENE_B" }, { Features.CODEWORD: [{ Axes.ROUND.value: 3, Axes.CH.value: 1, Features.CODE_VALUE: 1 }], Features.TARGET: "GENE_C" }, { Features.CODEWORD: [{ Axes.ROUND.value: 4, Axes.CH.value: 0, Features.CODE_VALUE: 1 }], Features.TARGET: "GENE_D" }, { Features.CODEWORD: [{ Axes.ROUND.value: 5, Axes.CH.value: 1, Features.CODE_VALUE: 1 }], Features.TARGET: "GENE_E" }] codebook = Codebook.from_code_array(combined) return codebook, multiplexed_stack, sequential_stack
def load(self, input_parameter: str) -> Codebook: return Codebook.open_json(input_parameter)