def test_data_roi_get_tiles_can_clamp_to_datasource_tiles(): # fmt: off data = Array5D(np.asarray([ [1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15], [16, 17, 18, 19, 20], ]).astype(np.uint8), axiskeys="yx") # fmt: on ds = ArrayDataSource.from_array5d(data, tile_shape=Shape5D(x=2, y=2)) data_slice = DataRoi(datasource=ds, x=(1, 4), y=(0, 3)) # fmt: off dataslice_expected_data = Array5D(np.asarray([[2, 3, 4], [7, 8, 9], [12, 13, 14]]).astype(np.uint8), axiskeys="yx", location=Point5D.zero(x=1)) # fmt: on assert data_slice.retrieve() == dataslice_expected_data # fmt: off dataslice_expected_slices = [ Array5D(np.asarray([[1, 2], [6, 7]]).astype(np.uint8), axiskeys="yx", location=Point5D.zero()), Array5D(np.asarray([ [3, 4], [8, 9], ]).astype(np.uint8), axiskeys="yx", location=Point5D.zero(x=2)), Array5D(np.asarray([ [11, 12], [16, 17], ]).astype(np.uint8), axiskeys="yx", location=Point5D.zero(y=2)), Array5D(np.asarray([ [13, 14], [18, 19], ]).astype(np.uint8), axiskeys="yx", location=Point5D.zero(x=2, y=2)) ] # fmt: on expected_slice_dict = {a.interval: a for a in dataslice_expected_slices} for piece in data_slice.get_datasource_tiles(clamp_to_datasource=True): expected_data = expected_slice_dict.pop(piece.interval) assert expected_data == piece.retrieve() assert len(expected_slice_dict) == 0
def compute(self, roi: DataRoi) -> ConnectedComponents: roi = roi.updated(c=self.object_channel_idx) expansion_step: Shape5D = (self.expansion_step or roi.tile_shape).updated(c=0) current_roi = roi while True: thresholded_data: ScalarData = ScalarData.fromArray5D(self.preprocessor.compute(current_roi)) connected_comps = ConnectedComponents.label(thresholded_data) if connected_comps.fully_contains_objects_in(roi): break if current_roi == roi.full(): break # FIXME: check mximum_tile_size too current_roi = current_roi.enlarged(radius=expansion_step).clamped(roi.full()) return connected_comps.clean(roi)
def test_distributed_n5_datasink(): tmp_path = create_tmp_dir(prefix="test_distributed_n5_datasink") filesystem = OsFs(tmp_path.as_posix()) outer_path = PurePosixPath("test_distributed_n5_datasink.n5") inner_path = PurePosixPath("/data") full_path = PurePosixPath("test_distributed_n5_datasink.n5/data") attributes = N5DatasetAttributes( dimensions=datasource.shape, blockSize=datasource.tile_shape, c_axiskeys=data.axiskeys, #FIXME: double check this dataType=datasource.dtype, compression=RawCompressor()) sink = N5DatasetSink(outer_path=outer_path, inner_path=inner_path, filesystem=filesystem, attributes=attributes) sink_writer = sink.create() assert not isinstance(sink_writer, Exception) sink_writers = [sink_writer] * 4 for idx, piece in enumerate(DataRoi(datasource).default_split()): sink = sink_writers[idx % len(sink_writers)] sink.write(piece.retrieve()) n5ds = N5DataSource(filesystem=filesystem, path=full_path) assert n5ds.retrieve() == data
def compute(self, roi: DataRoi) -> ScalarData: raw_data = roi.retrieve().raw(Point5D.LABELS) out = ScalarData.allocate(interval=roi, dtype=np.dtype("bool")) out.raw(Point5D.LABELS)[raw_data >= self.threshold] = True out.raw(Point5D.LABELS)[raw_data < self.threshold] = False return out
def test_distributed_n5_datasink(tmp_path: Path, data: Array5D, datasource: DataSource): filesystem = OsFs(tmp_path.as_posix()) outer_path = Path("test_distributed_n5_datasink.n5") inner_path = PurePosixPath("/data") full_path = Path("test_distributed_n5_datasink.n5/data") attributes = N5DatasetAttributes(dimensions=datasource.shape, blockSize=datasource.tile_shape, axiskeys=datasource.axiskeys, dataType=datasource.dtype, compression=RawCompressor()) sinks = [ N5DatasetSink.create(outer_path=outer_path, inner_path=inner_path, filesystem=filesystem, attributes=attributes), N5DatasetSink.open(path=full_path, filesystem=filesystem), N5DatasetSink.open(path=full_path, filesystem=filesystem), N5DatasetSink.open(path=full_path, filesystem=filesystem), ] for idx, piece in enumerate(DataRoi(datasource).default_split()): sink = sinks[idx % len(sinks)] sink.write(piece.retrieve()) n5ds = N5DataSource(filesystem=filesystem, path=full_path) assert n5ds.retrieve() == data
def test_neighboring_tiles(): # fmt: off arr = Array5D(np.asarray( [[10, 11, 12, 20, 21, 22, 30], [13, 14, 15, 23, 24, 25, 33], [16, 17, 18, 26, 27, 28, 36], [40, 41, 42, 50, 51, 52, 60], [43, 44, 45, 53, 54, 55, 63], [46, 47, 48, 56, 57, 58, 66], [70, 71, 72, 80, 81, 82, 90], [73, 74, 75, 83, 84, 85, 93], [76, 77, 78, 86, 87, 88, 96], [0, 1, 2, 3, 4, 5, 6]], dtype=np.uint8), axiskeys="yx") ds = SkimageDataSource(path=create_png(arr), filesystem=OsFs("/")) fifties_slice = DataRoi(ds, x=(3, 6), y=(3, 6)) expected_fifties_slice = Array5D(np.asarray([[50, 51, 52], [53, 54, 55], [56, 57, 58]]), axiskeys="yx") # fmt: on top_slice = DataRoi(ds, x=(3, 6), y=(0, 3)) bottom_slice = DataRoi(ds, x=(3, 6), y=(6, 9)) right_slice = DataRoi(ds, x=(6, 7), y=(3, 6)) left_slice = DataRoi(ds, x=(0, 3), y=(3, 6)) # fmt: off fifties_neighbor_data = { top_slice: Array5D(np.asarray([[20, 21, 22], [23, 24, 25], [26, 27, 28]]), axiskeys="yx"), right_slice: Array5D(np.asarray([[60], [63], [66]]), axiskeys="yx"), bottom_slice: Array5D(np.asarray([[80, 81, 82], [83, 84, 85], [86, 87, 88]]), axiskeys="yx"), left_slice: Array5D(np.asarray([[40, 41, 42], [43, 44, 45], [46, 47, 48]]), axiskeys="yx"), } # fmt: on assert (fifties_slice.retrieve().raw("yx") == expected_fifties_slice.raw( "yx")).all() # type: ignore for neighbor in fifties_slice.get_neighboring_tiles( tile_shape=Shape5D(x=3, y=3)): try: expected_slice = fifties_neighbor_data.pop(neighbor) assert (expected_slice.raw("yx") == neighbor.retrieve().raw("yx") ).all() # type: ignore except KeyError: print(f"\nWas searching for ", neighbor, "\n") for k in fifties_neighbor_data.keys(): print("--->>> ", k) assert len(fifties_neighbor_data) == 0
def __call__(self, roi: DataRoi) -> FeatureData: haloed_roi = roi.enlarged(self.halo) source_data = self.preprocessor(haloed_roi) step_shape: Shape5D = Shape5D( c=1, t=1, x=1 if self.axis_2d == "x" else source_data.shape.x, y=1 if self.axis_2d == "y" else source_data.shape.y, z=1 if self.axis_2d == "z" else source_data.shape.z, ) out = Array5D.allocate( interval=roi.updated(c=(roi.c[0] * self.channel_multiplier, roi.c[1] * self.channel_multiplier)), dtype=numpy.dtype("float32"), axiskeys=source_data.axiskeys.replace("c", "") + "c" # fastfilters puts channel last ) for data_slice in source_data.split(step_shape): source_axes = "zyx" if self.axis_2d: source_axes = source_axes.replace(self.axis_2d, "") raw_data: "ndarray[Any, dtype[float32]]" = data_slice.raw( source_axes).astype(numpy.float32) raw_feature_data: "ndarray[Any, dtype[float32]]" = self.filter_fn( raw_data) feature_data = FeatureData( raw_feature_data, axiskeys=source_axes + "c" if len(raw_feature_data.shape) > len(source_axes) else source_axes, location=data_slice.location.updated(c=data_slice.location.c * self.channel_multiplier)) out.set(feature_data, autocrop=True) out.setflags(write=False) return FeatureData( out.raw(out.axiskeys), axiskeys=out.axiskeys, location=out.location, )
def test_skimage_datasource_tiles(png_image: Path): bs = DataRoi(SkimageDataSource(png_image, filesystem=OsFs("/"))) num_checked_tiles = 0 for tile in bs.split(Shape5D(x=2, y=2)): if tile == Interval5D.zero(x=(0, 2), y=(0, 2)): expected_raw = raw_0_2x0_2y elif tile == Interval5D.zero(x=(0, 2), y=(2, 4)): expected_raw = raw_0_2x2_4y elif tile == Interval5D.zero(x=(2, 4), y=(0, 2)): expected_raw = raw_2_4x0_2y elif tile == Interval5D.zero(x=(2, 4), y=(2, 4)): expected_raw = raw_2_4x2_4y elif tile == Interval5D.zero(x=(4, 5), y=(0, 2)): expected_raw = raw_4_5x0_2y elif tile == Interval5D.zero(x=(4, 5), y=(2, 4)): expected_raw = raw_4_5x2_4y else: raise Exception(f"Unexpected tile {tile}") assert (tile.retrieve().raw("yx") == expected_raw).all() num_checked_tiles += 1 assert num_checked_tiles == 6
def __init__( self, *, position: Point5D, # an anchor point within datasource klass: int, datasource: DataSource, # an object label is tied to the datasource components_extractor: ConnectedComponentsExtractor, # and also to the method used to extract the objects ): position_roi = DataRoi(datasource, **Interval5D.enclosing([position]).to_dict()) self.data_tile = position_roi.get_tiles( datasource.tile_shape, clamp=False).__next__().clamped(datasource.shape) self.position = position self.klass = klass self.datasource = datasource self.components_extractor = components_extractor # compute connected components in constructor to prevent creation of bad annotation self.label = components_extractor.compute( self.data_tile).label_at(position)
async def precomputed_chunks_compute(self, request: web.Request) -> web.Response: encoded_raw_data = str(request.match_info.get("encoded_raw_data")) generation = int(request.match_info.get("generation")) # type: ignore xBegin = int(request.match_info.get("xBegin")) # type: ignore xEnd = int(request.match_info.get("xEnd")) # type: ignore yBegin = int(request.match_info.get("yBegin")) # type: ignore yEnd = int(request.match_info.get("yEnd")) # type: ignore zBegin = int(request.match_info.get("zBegin")) # type: ignore zEnd = int(request.match_info.get("zEnd")) # type: ignore datasource = _decode_datasource(encoded_raw_data) with self.lock: classifier = self.pixel_classifier() label_classes = self._in_label_classes() if classifier is None: return web.json_response({"error": "Classifier is not ready yet"}, status=412) if generation != self._state.generation: return web.json_response({"error": "This classifier is stale"}, status=410) predictions = await asyncio.wrap_future(self.executor.submit( classifier, DataRoi(datasource, x=(xBegin, xEnd), y=(yBegin, yEnd), z=(zBegin, zEnd)) )) if "format" in request.query: requested_format = request.query["format"] if requested_format != "png": return web.Response(status=400, text=f"Server-side rendering only available in png, not in {requested_format}") if predictions.shape.z > 1: return web.Response(status=400, text="Server-side rendering only available for 2d images") class_colors = tuple(label_classes.keys()) prediction_png_bytes = list(predictions.to_z_slice_pngs(class_colors))[0] #FIXME assumes shape.t=1 and shape.z=1 return web.Response( body=prediction_png_bytes.getbuffer(), headers={ "Cache-Control": "no-store, must-revalidate", "Expires": "0", }, content_type="image/png", ) # https://github.com/google/neuroglancer/tree/master/src/neuroglancer/datasource/precomputed#raw-chunk-encoding # "(...) data for the chunk is stored directly in little-endian binary format in [x, y, z, channel] Fortran order" resp = predictions.as_uint8().raw("xyzc").tobytes("F") return web.Response( body=resp, content_type="application/octet-stream", )
async def precomputed_chunks_compute(self, request: web.Request) -> web.Response: encoded_raw_data = str( request.match_info.get("encoded_raw_data")) # type: ignore xBegin = int(request.match_info.get("xBegin")) # type: ignore xEnd = int(request.match_info.get("xEnd")) # type: ignore yBegin = int(request.match_info.get("yBegin")) # type: ignore yEnd = int(request.match_info.get("yEnd")) # type: ignore zBegin = int(request.match_info.get("zBegin")) # type: ignore zEnd = int(request.match_info.get("zEnd")) # type: ignore datasource = _decode_datasource(encoded_raw_data) predictions = await self.runner.async_submit( self.pixel_classifier().compute, DataRoi(datasource, x=(xBegin, xEnd), y=(yBegin, yEnd), z=(zBegin, zEnd))) if "format" in request.query: requested_format = request.query["format"] if requested_format != "png": return web.Response( status=400, text= "Server-side rendering only available in png, not in {requested_format}" ) if predictions.shape.z > 1: return web.Response( status=400, text="Server-side rendering only available for 2d images") prediction_png_bytes = list(predictions.to_z_slice_pngs())[0] return web.Response( body=prediction_png_bytes.getbuffer(), headers={ "Cache-Control": "no-store, must-revalidate", "Expires": "0", }, content_type="image/png", ) # https://github.com/google/neuroglancer/tree/master/src/neuroglancer/datasource/precomputed#raw-chunk-encoding # "(...) data for the chunk is stored directly in little-endian binary format in [x, y, z, channel] Fortran order" resp = predictions.as_uint8().raw("xyzc").tobytes("F") return web.Response( body=resp, content_type="application/octet-stream", )
def compute(self, roi: DataRoi) -> FeatureData: roi_step: Shape5D = roi.shape.updated( c=1, t=1) # compute features independently for each c and each t if self.axis_2d: roi_step = roi_step.updated(**{self.axis_2d: 1}) # also compute in 2D slices per_channel_results: List[FeatureData] = [] for roi_slice in roi.split(roi_step): haloed_roi = roi_slice.enlarged(self.halo) channel_offset = roi_slice.start.c - roi.start.c if self.presmooth_sigma > 0: gaussian_filter = GaussianSmoothing( preprocessor=self.preprocessor, sigma=self.presmooth_sigma, axis_2d=self.axis_2d, window_size=3.5) source_data = gaussian_filter.compute(haloed_roi) else: source_data = self.preprocessor.compute(haloed_roi) source_axes = "zyx" if self.axis_2d: source_axes = source_axes.replace(self.axis_2d, "") raw_data: numpy.ndarray = source_data.raw(source_axes).astype( numpy.float32) raw_feature_data: numpy.ndarray = self.filter_fn(raw_data) if len(raw_feature_data.shape) > len(source_axes): output_axes = source_axes + "c" channel_multiplier = raw_feature_data.shape[-1] else: output_axes = source_axes channel_multiplier = 1 feature_data = FeatureData( raw_feature_data, axiskeys=output_axes, location=haloed_roi.start.updated(c=channel_offset * channel_multiplier)) per_channel_results.append(feature_data.cut(roi_slice, c=All())) combined_result = Array5D.combine(per_channel_results) out = FeatureData(combined_result.raw(combined_result.axiskeys), axiskeys=combined_result.axiskeys, location=combined_result.location) out.setflags(write=False) return out
def test_n5_datasink(tmp_path: Path, data: Array5D, datasource: DataSource): sink = N5DatasetSink.create(filesystem=OsFs(tmp_path.as_posix()), outer_path=Path("test_n5_datasink.n5"), inner_path=PurePosixPath("/data"), attributes=N5DatasetAttributes( dimensions=datasource.shape, blockSize=Shape5D(x=10, y=10), axiskeys=datasource.axiskeys, dataType=datasource.dtype, compression=RawCompressor(), location=Point5D.zero(x=7, y=13))) for tile in DataRoi(datasource).split(sink.tile_shape): sink.write(tile.retrieve().translated(Point5D.zero(x=7, y=13))) n5ds = N5DataSource(filesystem=sink.filesystem, path=sink.path) saved_data = n5ds.retrieve() assert saved_data.location == Point5D.zero(x=7, y=13) assert saved_data == data
def test_n5_datasink(): tmp_path = create_tmp_dir(prefix="test_n5_datasink") sink = N5DatasetSink( filesystem=OsFs(tmp_path.as_posix()), outer_path=PurePosixPath("test_n5_datasink.n5"), inner_path=PurePosixPath("/data"), attributes=N5DatasetAttributes( dimensions=datasource.shape, blockSize=Shape5D(x=10, y=10), c_axiskeys=data.axiskeys, #FIXME: double check this dataType=datasource.dtype, compression=RawCompressor(), location=Point5D.zero(x=7, y=13))) sink_writer = sink.create() assert not isinstance(sink_writer, Exception) for tile in DataRoi(datasource).split(sink.tile_shape): sink_writer.write(tile.retrieve().translated(Point5D.zero(x=7, y=13))) n5ds = N5DataSource(filesystem=sink.filesystem, path=sink.full_path) saved_data = n5ds.retrieve() assert saved_data.location == Point5D.zero(x=7, y=13) assert saved_data == data
def compute(self, roi: DataRoi) -> Array5D: return roi.retrieve()
def draw(self, operator: Operator[DataRoi, Array5D], datasource: DataSource): data = operator.compute(DataRoi(datasource)) data.show_channels()
def test_pixel_classification_workflow(): brushing_applet = BrushingApplet("brushing_applet") feature_selection_applet = FeatureSelectionApplet( "feature_selection_applet", datasources=brushing_applet.datasources) pixel_classifier_applet = PixelClassificationApplet( "pixel_classifier_applet", feature_extractors=feature_selection_applet.feature_extractors, annotations=brushing_applet.annotations) # wf = PixelClassificationWorkflow( # feature_selection_applet=feature_selection_applet, # brushing_applet=brushing_applet, # pixel_classifier_applet=pixel_classifier_applet, # predictions_export_applet=predictions_export_applet # ) # GUI creates a datasource somewhere... ds = SkimageDataSource(Path("public/images/c_cells_1.png"), filesystem=OsFs("."), tile_shape=Shape5D(x=400, y=400)) # GUI creates some feature extractors feature_selection_applet.feature_extractors.set_value( [ GaussianSmoothing.from_ilp_scale(scale=0.3, axis_2d="z"), HessianOfGaussianEigenvalues.from_ilp_scale(scale=0.7, axis_2d="z"), ], confirmer=dummy_confirmer) # GUI creates some annotations brush_strokes = [ Annotation.interpolate_from_points( voxels=[Point5D.zero(x=140, y=150), Point5D.zero(x=145, y=155)], color=Color(r=np.uint8(0), g=np.uint8(0), b=np.uint8(255)), raw_data=ds), Annotation.interpolate_from_points( voxels=[Point5D.zero(x=238, y=101), Point5D.zero(x=229, y=139)], color=Color(r=np.uint8(0), g=np.uint8(0), b=np.uint8(255)), raw_data=ds), Annotation.interpolate_from_points( voxels=[Point5D.zero(x=283, y=87), Point5D.zero(x=288, y=92)], color=Color(r=np.uint8(255), g=np.uint8(0), b=np.uint8(0)), raw_data=ds), Annotation.interpolate_from_points( voxels=[Point5D.zero(x=274, y=168), Point5D.zero(x=256, y=191)], color=Color(r=np.uint8(255), g=np.uint8(0), b=np.uint8(0)), raw_data=ds), ] brushing_applet.annotations.set_value(brush_strokes, confirmer=dummy_confirmer) # preds = predictions_export_applet.compute(DataRoi(ds)) classifier = pixel_classifier_applet.pixel_classifier() executor = HashingExecutor(num_workers=8) # calculate predictions on an arbitrary data preds = executor.submit(classifier.compute, ds.roi) preds.result().as_uint8().show_channels() # for png_bytes in preds.to_z_slice_pngs(): # path = f"/tmp/junk_test_image_{uuid.uuid4()}.png" # with open(path, "wb") as outfile: # outfile.write(png_bytes.getbuffer()) # os.system(f"gimp {path}") # calculate predictions on just a piece of arbitrary data exported_tile = executor.submit( classifier.compute, DataRoi(datasource=ds, x=(100, 200), y=(100, 200))) exported_tile.result().show_channels() # wf.save_as(Path("/tmp/blas.ilp")) #try removing a brush stroke brushing_applet.annotations.set_value(brush_strokes[1:], confirmer=dummy_confirmer) assert tuple(brushing_applet.annotations()) == tuple(brush_strokes[1:])
def _write_data(tile: DataRoi, sink_writer: DataSinkWriter): print(f"Writing {tile}") sink_writer.write(tile.retrieve())