def transform( cls, image: Image, arguments: dict, callback_function: Optional[Callable[[str, int], None]] = None) -> Image: keys = [x for x in arguments if x.startswith("scale")] keys_order = Image.axis_order.lower() scale_factor = [1.0] * len(keys_order) if len(keys) == 1 and keys[0] == "scale": for letter in image.get_dimension_letters().lower(): scale_factor[keys_order.index(letter)] = arguments["scale"] spacing = [x / arguments["scale"] for x in image.spacing] else: # assume that all keys are in format scale_{} for key in keys: letter = key[-1] scale_factor[keys_order.index(letter)] = arguments[key] spacing = [ x / arguments[f"scale_{y}"] for x, y in zip(image.spacing, image.get_dimension_letters().lower()) ] array = zoom(image.get_data(), scale_factor, mode="mirror") if image.mask is not None: mask = zoom(image.mask, scale_factor[:-1], mode="mirror") else: mask = None return image.substitute(data=array, image_spacing=spacing, mask=mask)
def test_mask_property_combinations(self, dilate, radius, fill_holes, max_holes_size, save_components, clip_to_mask, reversed_mask, old_mask): mask = np.zeros((1, 6, 6, 15), dtype=np.uint8) im = Image(data=mask.copy(), image_spacing=(3, 1, 1), file_path="", axes_order="TZYX") mask[:, 1:-1, 1:-1, 2:5] = 1 mask[:, 2:-2, 2:-2, 3:4] = 0 mask[:, 1:-1, 1:-1, 6:9] = 2 mask[:, 2:-2, 2:-2, 7:8] = 0 mask[:, 1:-1, 1:-1, 10:13] = 3 mask[:, 2:-2, 2:-2, 11:12] = 0 mask = im.fit_mask_to_image(mask) assert np.all(np.unique(mask.flat) == [0, 1, 2, 3]) _old_mask = np.zeros(mask.shape, dtype=mask.dtype) if old_mask else None mp = MaskProperty( dilate=dilate, dilate_radius=radius, fill_holes=fill_holes, max_holes_size=max_holes_size, save_components=save_components, clip_to_mask=clip_to_mask, reversed_mask=reversed_mask, ) mask1 = calculate_mask(mp, mask, _old_mask, im.spacing, time_axis=im.time_pos) assert mask1.shape == mask.shape
def analysis_project() -> ProjectTuple: data = np.zeros((1, 50, 100, 100, 1), dtype=np.uint16) data[0, 10:40, 10:40, 10:90] = 50 data[0, 10:40, 50:90, 10:90] = 50 data[0, 15:35, 15:35, 15:85] = 70 data[0, 15:35, 55:85, 15:85] = 60 data[0, 10:40, 40:50, 10:90] = 40 image = Image( data, (10 / UNIT_SCALE[Units.nm.value], 5 / UNIT_SCALE[Units.nm.value], 5 / UNIT_SCALE[Units.nm.value]), "" ) mask = data[0, ..., 0] > 0 segmentation = np.zeros(data.shape, dtype=np.uint8) segmentation[data == 70] = 1 segmentation[data == 60] = 2 algorithm_parameters = { "algorithm_name": "Lower Threshold", "values": { "threshold": 60, "channel": 0, "noise_filtering": {"name": "None", "values": {}}, "minimum_size": 10, "side_connection": False, }, } segmentation = image.fit_array_to_image(segmentation.squeeze()) return ProjectTuple( file_path="test_data.tiff", image=image, roi=segmentation, additional_layers={"denoised image": AdditionalLayerDescription(data=segmentation, layer_type="layer")}, mask=mask, algorithm_parameters=algorithm_parameters, )
def save_components( image: Image, components: list, segmentation: np.ndarray, dir_path: str, segmentation_info: typing.Optional[SegmentationInfo] = None, range_changed=None, step_changed=None, ): if range_changed is None: range_changed = empty_fun if step_changed is None: step_changed = empty_fun segmentation = image.fit_array_to_image(segmentation) if segmentation_info is None: segmentation_info = SegmentationInfo(segmentation) os.makedirs(dir_path, exist_ok=True) file_name = os.path.splitext(os.path.basename(image.file_path))[0] range_changed(0, 2 * len(components)) for i in components: slices = segmentation_info.bound_info[i].get_slices() cut_segmentation = segmentation[tuple(slices)] cut_image = image.cut_image(slices) im = cut_image.cut_image(cut_segmentation == i, replace_mask=True) # print(f"[run] {im}") ImageWriter.save( im, os.path.join(dir_path, f"{file_name}_component{i}.tif")) step_changed(2 * i + 1) ImageWriter.save_mask( im, os.path.join(dir_path, f"{file_name}_component{i}_mask.tif")) step_changed(2 * i + 2)
def calculate_pipeline(image: Image, mask: typing.Optional[np.ndarray], pipeline: SegmentationPipeline, report_fun): history = [] report_fun("max", 2 * len(pipeline.mask_history) + 1) for i, el in enumerate(pipeline.mask_history): result, _ = calculate_segmentation_step(el.segmentation, image, mask) segmentation = image.fit_array_to_image(result.roi) report_fun("step", 2 * i + 1) new_mask = calculate_mask( mask_description=el.mask_property, segmentation=segmentation, old_mask=mask, spacing=image.spacing, time_axis=image.time_pos, ) segmentation_parameters = { "algorithm_name": el.segmentation.name, "values": el.segmentation.values } history.append( HistoryElement.create(segmentation, mask, segmentation_parameters, el.mask_property)) report_fun("step", 2 * i + 2) mask = image.fit_array_to_image(new_mask) result, text = calculate_segmentation_step(pipeline.segmentation, image, mask) report_fun("step", 2 * len(pipeline.mask_history) + 1) return PipelineResult(result.roi, result.additional_layers, mask, history, text)
def test_merge_fail(self): image1 = Image(data=np.zeros((4, 10, 10), dtype=np.uint8), axes_order="ZXY", image_spacing=(1, 1, 1)) image2 = Image(data=np.zeros((3, 10, 10), dtype=np.uint8), axes_order="ZXY", image_spacing=(1, 1, 1)) with pytest.raises(ValueError): image1.merge(image2, "C")
def test_channels(self, tmp_path, qtbot): settings = BaseSettings(tmp_path) assert not settings.has_channels assert settings.channels == 0 settings.image = Image(np.zeros((10, 10, 2), dtype=np.uint8), (1, 1), axes_order="XYC") assert settings.has_channels assert settings.channels == 2 settings.image = Image(np.zeros((10, 10, 1), dtype=np.uint8), (1, 1), axes_order="XYC") assert not settings.has_channels assert settings.channels == 1
def test_reorder_axes(self): fixed_array = self.image_class.reorder_axes(np.zeros((10, 20)), axes="XY") assert fixed_array.shape == self.image_shape((10, 20), "XY") fixed_image = self.image_class(np.zeros((10, 20)), image_spacing=(1, 1, 1), axes_order="XY") assert fixed_image.shape == self.image_shape((10, 20), "XY") with pytest.raises(ValueError): Image.reorder_axes(np.zeros((10, 20)), axes="XYZ")
def test_merge_chanel(self, chanel_mark, check_dtype): image1 = Image(data=np.zeros((3, 10, 10), dtype=np.uint8), axes_order="ZXY", image_spacing=(1, 1, 1)) image2 = Image(data=np.ones((3, 10, 10), dtype=check_dtype), axes_order="ZXY", image_spacing=(1, 1, 1)) res_image = image1.merge(image2, chanel_mark) assert res_image.channels == 2 assert np.all(res_image.get_channel(0) == 0) assert np.all(res_image.get_channel(1) == 1) assert res_image.dtype == check_dtype
def test_different_axes_order(self): image1 = Image(data=np.zeros((3, 10, 10), dtype=np.uint8), axes_order="ZXY", image_spacing=(1, 1, 1)) image2 = ChangeChannelPosImage(data=np.zeros((3, 10, 10), dtype=np.uint8), axes_order="ZXY", image_spacing=(1, 1, 1)) res_image = image1.merge(image2, "C") assert res_image.channels == 2 assert isinstance(res_image, Image) assert isinstance(image2.merge(image1, "C"), ChangeChannelPosImage)
def test_verify_image(self): assert BaseSettings.verify_image(Image(np.zeros((10, 10, 2), dtype=np.uint8), (1, 1), axes_order="XYC")) with pytest.raises(SwapTimeStackException): BaseSettings.verify_image( Image(np.zeros((2, 10, 10), dtype=np.uint8), (1, 1, 1), axes_order="TXY"), silent=False ) im = BaseSettings.verify_image(Image(np.zeros((2, 10, 10), dtype=np.uint8), (1, 1, 1), axes_order="TXY")) assert not im.is_time assert im.times == 1 assert im.is_stack assert im.layers == 2 with pytest.raises(TimeAndStackException): BaseSettings.verify_image(Image(np.zeros((2, 2, 10, 10), dtype=np.uint8), (1, 1, 1), axes_order="TZXY"))
def test_load_files(self, part_settings, qtbot, monkeypatch, tmp_path): widget = MultipleFileWidget( part_settings, {LoadStackImage.get_name(): LoadStackImage}) qtbot.add_widget(widget) for i in range(5): ImageWriter.save( Image(np.random.random((10, 10)), image_spacing=(1, 1), axes_order="XY"), tmp_path / f"img_{i}.tif") file_list = [[[str(tmp_path / f"img_{i}.tif")], LoadStackImage.get_name()] for i in range(5)] load_property = LoadProperty( [str(tmp_path / f"img_{i}.tif") for i in range(5)], LoadStackImage.get_name(), LoadStackImage) with qtbot.waitSignal(widget._add_state, check_params_cb=self.check_load_files): widget.execute_load_files(load_property, lambda x, y: True, lambda x: True) assert widget.file_view.topLevelItemCount() == 5 assert part_settings.get_last_files_multiple() == file_list widget.file_view.clear() widget.state_dict.clear() widget.file_list.clear() monkeypatch.setattr(MultipleLoadDialog, "exec_", lambda x: True) monkeypatch.setattr(MultipleLoadDialog, "get_result", lambda x: load_property) with qtbot.waitSignal(widget._add_state, check_params_cb=self.check_load_files): widget.load_files() assert widget.file_view.topLevelItemCount() == 5 assert part_settings.get_last_files_multiple() == file_list part_settings.dump() part_settings.load() assert part_settings.get_last_files_multiple() == file_list
def __init__(self, image: Image, transform_dict: Dict[str, TransformBase] = None): super().__init__() if transform_dict is None: transform_dict = image_transform_dict self.choose = QComboBox() self.stacked = QStackedWidget() for key, val in transform_dict.items(): self.choose.addItem(key) initial_values = val.calculate_initial(image) form_widget = FormWidget( val.get_fields_per_dimension(image.get_dimension_letters()), initial_values) self.stacked.addWidget(form_widget) self.choose.currentIndexChanged.connect(self.stacked.setCurrentIndex) self.cancel_btn = QPushButton("Cancel") self.cancel_btn.clicked.connect(self.reject) self.process_btn = QPushButton("Process") self.process_btn.clicked.connect(self.process) self.transform_dict = transform_dict self.result_val: ImageAdjustTuple = None layout = QGridLayout() layout.addWidget(self.choose, 0, 0, 1, 3) layout.addWidget(self.stacked, 1, 0, 1, 3) layout.addWidget(self.cancel_btn, 2, 0) layout.addWidget(self.process_btn, 2, 2) self.setLayout(layout)
def napari_write_image(path: str, data: Any, meta: dict) -> Optional[str]: ext = os.path.splitext(path)[1] if not isinstance(data, numpy.ndarray) or ext not in { ".tiff", ".tif", ".TIFF", ".TIF" }: return scale_shift = min(data.ndim, 3) axes = "TZXY" channel_names = [meta["name"]] if data.shape[-1] < 6: axes += "C" scale_shift -= 1 channel_names = [ f'{meta["name"]} {i}' for i in range(1, data.shape[-1] + 1) ] image = Image( data, numpy.divide(meta["scale"], DEFAULT_SCALE_FACTOR)[-scale_shift:], axes_order=axes[-data.ndim:], channel_names=channel_names, shift=numpy.divide(meta["translate"], DEFAULT_SCALE_FACTOR)[-scale_shift:], name="Image", ) ImageWriter.save(image, path) return path
def test_load_recent(self, part_settings, qtbot, monkeypatch, tmp_path): widget = MultipleFileWidget( part_settings, {LoadStackImage.get_name(): LoadStackImage}) qtbot.add_widget(widget) for i in range(5): ImageWriter.save( Image(np.random.random((10, 10)), image_spacing=(1, 1), axes_order="XY"), tmp_path / f"img_{i}.tif") file_list = [[ [ tmp_path / f"img_{i}.tif", ], LoadStackImage.get_name(), ] for i in range(5)] with qtbot.waitSignal(widget._add_state, check_params_cb=self.check_load_files): widget.load_recent_fun(file_list, lambda x, y: True, lambda x: True) assert part_settings.get_last_files_multiple() == file_list assert widget.file_view.topLevelItemCount() == 5 widget.file_view.clear() widget.state_dict.clear() widget.file_list.clear() monkeypatch.setattr(LoadRecentFiles, "exec_", lambda x: True) monkeypatch.setattr(LoadRecentFiles, "get_files", lambda x: file_list) with qtbot.waitSignal(widget._add_state, check_params_cb=self.check_load_files): widget.load_recent() assert part_settings.get_last_files_multiple() == file_list assert widget.file_view.topLevelItemCount() == 5
def calculate_initial(cls, image: Image): min_val = min(image.spacing) return { f"scale_{l}": x / min_val for x, l in zip(image.spacing, image.get_dimension_letters().lower()) }
def verify_image(image: Image, silent=True) -> Union[Image, bool]: if image.is_time: if image.is_stack: raise TimeAndStackException() if silent: return image.swap_time_and_stack() raise SwapTimeStackException() return True
def image(tmp_path): data = np.zeros([20, 20, 20, 2], dtype=np.uint8) data[10:-1, 1:-1, 1:-1, 0] = 20 data[1:10, 1:-1, 1:-1, 1] = 20 data[1:-1, 1:5, 1:-1, 1] = 20 data[1:-1, -5:-1, 1:-1, 1] = 20 return Image(data, (10 ** -3, 10 ** -3, 10 ** -3), axes_order="ZYXC", file_path=str(tmp_path / "test.tiff"))
def image(): data = np.zeros([20, 20, 20, 2], dtype=np.uint8) data[10:-1, 1:-1, 1:-1, 0] = 20 data[1:10, 1:-1, 1:-1, 1] = 20 data[1:-1, 1:5, 1:-1, 1] = 20 data[1:-1, -5:-1, 1:-1, 1] = 20 return Image(data, (10**-3, 10**-3, 10**-3), axes_order="ZYXC")
def analysis_project_reversed() -> ProjectTuple: data = np.zeros((1, 50, 100, 100, 1), dtype=np.uint16) data[0, 10:40, 10:40, 10:90] = 50 data[0, 10:40, 50:90, 10:90] = 50 data[0, 15:35, 15:35, 15:85] = 70 data[0, 15:35, 55:85, 15:85] = 60 data[0, 10:40, 40:50, 10:90] = 40 mask = data[0] > 0 segmentation = np.zeros(data.shape, dtype=np.uint8) segmentation[data == 70] = 1 segmentation[data == 60] = 2 data = 100 - data image = Image( data, (10 / UNIT_SCALE[Units.nm.value], 5 / UNIT_SCALE[Units.nm.value], 5 / UNIT_SCALE[Units.nm.value]), "" ) segmentation = image.fit_array_to_image(segmentation.squeeze()) return ProjectTuple("test_data.tiff", image, segmentation, mask=mask)
def test_run_return_none(self, qtbot): algorithm = ROIExtractionAlgorithmForTest(return_none=True) thr = segmentation_thread.SegmentationThread(algorithm) image = Image(np.zeros((10, 10), dtype=np.uint8), image_spacing=(1, 1), axes_order="XY") algorithm.set_image(image) with qtbot.assertNotEmitted(thr.execution_done): thr.run()
def test_run(self, qtbot): algorithm = ROIExtractionAlgorithmForTest() thr = segmentation_thread.SegmentationThread(algorithm) image = Image(np.zeros((10, 10), dtype=np.uint8), image_spacing=(1, 1), axes_order="XY") algorithm.set_image(image) with qtbot.waitSignals([thr.execution_done, thr.progress_signal]): thr.run()
def stack_image(): data = np.zeros([20, 40, 40], dtype=np.uint8) for x, y in itertools.product([0, 20], repeat=2): data[1:-1, x + 2 : x + 18, y + 2 : y + 18] = 100 for x, y in itertools.product([0, 20], repeat=2): data[3:-3, x + 4 : x + 16, y + 4 : y + 16] = 120 for x, y in itertools.product([0, 20], repeat=2): data[5:-5, x + 6 : x + 14, y + 6 : y + 14] = 140 return MaskProjectTuple("test_path", Image(data, (2, 1, 1), axes_order="ZYX", file_path="test_path"))
def test_run_exception(self, qtbot): algorithm = ROIExtractionAlgorithmForTest(raise_=True) thr = segmentation_thread.SegmentationThread(algorithm) image = Image(np.zeros((10, 10), dtype=np.uint8), image_spacing=(1, 1), axes_order="XY") algorithm.set_image(image) with qtbot.assertNotEmitted(thr.execution_done), qtbot.waitSignal( thr.exception_occurred): thr.run()
def test_image_info(): image_info = ImageInfo( Image(np.zeros((10, 10)), image_spacing=(1, 1), axes_order="XY"), []) assert not image_info.coords_in([1, 1]) assert np.all(image_info.translated_coords([1, 1]) == [1, 1]) image_info.layers.append( NapariImage(image_info.image.get_channel(0), scale=(1, 1, 10, 10))) assert image_info.coords_in([0.5, 0.5, 1, 1]) assert np.all(image_info.translated_coords([1, 1, 1, 1]) == [1, 1, 1, 1])
def test_base_settings_verify_image(self): assert base_settings.BaseSettings.verify_image( Image(np.zeros((10, 10)), (10, 10), axes_order="YX")) assert base_settings.BaseSettings.verify_image( Image(np.zeros((10, 10, 10)), (10, 10, 10), axes_order="ZYX")) with pytest.raises(base_settings.SwapTimeStackException): base_settings.BaseSettings.verify_image(Image(np.zeros( (10, 10, 10)), (10, 10, 10), axes_order="TYX"), silent=False) new_image = base_settings.BaseSettings.verify_image(Image( np.zeros((10, 10, 10)), (10, 10, 10), axes_order="TYX"), silent=True) assert new_image.is_stack assert not new_image.is_time with pytest.raises(base_settings.TimeAndStackException): base_settings.BaseSettings.verify_image(Image(np.zeros( (2, 10, 10, 10)), (10, 10, 10), axes_order="TZYX"), silent=True)
def create_test_data(tmpdir): # for future use spacing = tuple(x / UNIT_SCALE[Units.nm.value] for x in (210, 70, 70)) res = [] for i in range(8): mask_data = np.zeros((10, 20, 20 + i), dtype=np.uint8) mask_data[1:-1, 2:-2, 2:-2] = 1 data = np.zeros(mask_data.shape + (2, ), dtype=np.uint16) data[1:-1, 2:-2, 2:-2] = 15000 data[2:-2, 3:-3, 3:7] = 33000 data[2:-2, 3:-3, -7:-3] = 33000 image = Image(data, spacing, "", mask=mask_data, axes_order="ZYXC") ImageWriter.save(image, os.path.join(str(tmpdir), f"file_{i}.tif")) res.append(os.path.join(str(tmpdir), f"file_{i}.tif")) ImageWriter.save_mask(image, os.path.join(str(tmpdir), f"file_{i}_mask.tif")) return res
def napari_write_labels(path: str, data: Any, meta: dict) -> Optional[str]: ext = os.path.splitext(path)[1] if not isinstance(data, numpy.ndarray) or ext not in { ".tiff", ".tif", ".TIFF", ".TIF" }: return scale_shift = min(data.ndim, 3) image = Image( data, numpy.divide(meta["scale"], DEFAULT_SCALE_FACTOR)[-scale_shift:], axes_order="TZXY"[-data.ndim:], channel_names=[meta["name"]], shift=numpy.divide(meta["translate"], DEFAULT_SCALE_FACTOR)[-scale_shift:], name="ROI", ) ImageWriter.save(image, path) return path
def generate_image(viewer: Viewer, *layer_names): axis_order = Image.axis_order.replace("C", "") image_list = [] for name in dict.fromkeys(layer_names): image_layer = viewer.layers[name] data_scale = image_layer.scale[-3:] / UNIT_SCALE[Units.nm.value] image_list.append( Image( image_layer.data, data_scale, axes_order=axis_order[-image_layer.data.ndim:], channel_names=[image_layer.name], )) res_image = image_list[0] for image in image_list[1:]: res_image = res_image.merge(image, "C") return res_image
def analysis_project_reversed() -> ProjectTuple: data = np.zeros((1, 50, 100, 100, 1), dtype=np.uint16) data[0, 10:40, 10:40, 10:90] = 50 data[0, 10:40, 50:90, 10:90] = 50 data[0, 15:35, 15:35, 15:85] = 70 data[0, 15:35, 55:85, 15:85] = 60 data[0, 10:40, 40:50, 10:90] = 40 mask = data[0] > 0 roi = np.zeros(data.shape, dtype=np.uint8) roi[data == 70] = 1 roi[data == 60] = 2 data = 100 - data image = Image( data, (10 / UNIT_SCALE[Units.nm.value], 5 / UNIT_SCALE[Units.nm.value], 5 / UNIT_SCALE[Units.nm.value]), "" ) roi_info = ROIInfo(roi.squeeze()).fit_to_image(image) return ProjectTuple("test_data.tiff", image, roi_info=roi_info, mask=mask)