예제 #1
0
파일: test_io.py 프로젝트: Anodarai/PartSeg
class TestPartSettingsIO:
    @pytest.mark.parametrize("method", analysis_save_dict.values())
    def test_save(self, method, qtbot, part_settings_with_project, tmp_path):
        pi = part_settings_with_project.get_project_info()
        method.save(tmp_path / "data", pi, method.get_default_values())

    def test_load_segmentation(self, part_settings, data_test_dir):
        path = os.path.join(data_test_dir, "stack1_component1_1.tgz")
        pi = LoadProject.load([path])
        part_settings.set_project_info(pi)

    @pytest.mark.parametrize(
        "file_name",
        [
            "Image0003_01.oif",
            "test_czi.czi",
            "test_lsm.lsm",
            "test_lsm.tif",
            "test_lsm2.tif",
            "test_nucleus.tif",
            "test_nucleus_mask.tif",
        ],
    )
    def test_load_images(self, file_name, part_settings, data_test_dir):
        path = os.path.join(data_test_dir, file_name)
        pi = LoadStackImage.load([path])
        assert isinstance(pi, ProjectTuple)
        part_settings.set_project_info(pi)

    def test_load_images_with_mask(self, part_settings, data_test_dir):
        base_path = Path(os.path.join(data_test_dir, "stack1_components"))

        pi = LoadImageMask.load([
            base_path / "stack1_component1.tif",
            base_path / "stack1_component1_mask.tif"
        ])
        assert isinstance(pi, ProjectTuple)
        part_settings.set_project_info(pi)

    def test_load_mask_project(self, part_settings, data_test_dir):
        path = os.path.join(data_test_dir, "test_nucleus.seg")
        pi_l = LoadMaskSegmentation.load([path])
        assert isinstance(pi_l, list)
        for pi in pi_l:
            part_settings.set_project_info(pi)
예제 #2
0
    def __init__(self, settings: PartSettings):
        super().__init__()
        self.settings = settings
        self.save_translate_dict: typing.Dict[str, SaveBase] = {x.get_short_name(): x for x in save_dict.values()}
        self.plan = PlanPreview(self)
        self.save_plan_btn = QPushButton("Save")
        self.clean_plan_btn = QPushButton("Remove all")
        self.remove_btn = QPushButton("Remove")
        self.update_element_chk = QCheckBox("Update element")
        self.change_root = EnumComboBox(RootType)
        self.save_choose = QComboBox()
        self.save_choose.addItem("<none>")
        self.save_choose.addItems(list(self.save_translate_dict.keys()))
        self.save_btn = QPushButton("Save")
        self.segment_profile = SearchableListWidget()
        self.pipeline_profile = SearchableListWidget()
        self.segment_stack = QTabWidget()
        self.segment_stack.addTab(self.segment_profile, "Profile")
        self.segment_stack.addTab(self.pipeline_profile, "Pipeline")
        self.generate_mask_btn = QPushButton("Add mask")
        self.generate_mask_btn.setToolTip("Mask need to have unique name")
        self.mask_name = QLineEdit()
        self.mask_operation = EnumComboBox(MaskOperation)

        self.chanel_num = QSpinBox()
        self.choose_channel_for_measurements = QComboBox()
        self.choose_channel_for_measurements.addItems(
            ["Same as segmentation"] + [str(x + 1) for x in range(MAX_CHANNEL_NUM)]
        )
        self.units_choose = EnumComboBox(Units)
        self.units_choose.set_value(self.settings.get("units_value", Units.nm))
        self.chanel_num.setRange(0, 10)
        self.expected_node_type = None
        self.save_constructor = None

        self.chose_profile_btn = QPushButton("Add Profile")
        self.get_big_btn = QPushButton("Leave the biggest")
        self.get_big_btn.hide()
        self.get_big_btn.setDisabled(True)
        self.measurements_list = SearchableListWidget(self)
        self.measurement_name_prefix = QLineEdit(self)
        self.add_calculation_btn = QPushButton("Add measurement calculation")
        self.information = QTextEdit()
        self.information.setReadOnly(True)

        self.protect = False
        self.mask_set = set()
        self.calculation_plan = CalculationPlan()
        self.plan.set_plan(self.calculation_plan)
        self.segmentation_mask = MaskWidget(settings)
        self.file_mask = FileMask()

        self.change_root.currentIndexChanged.connect(self.change_root_type)
        self.save_choose.currentTextChanged.connect(self.save_changed)
        self.measurements_list.currentTextChanged.connect(self.show_measurement)
        self.segment_profile.currentTextChanged.connect(self.show_segment)
        self.measurements_list.currentTextChanged.connect(self.show_measurement_info)
        self.segment_profile.currentTextChanged.connect(self.show_segment_info)
        self.pipeline_profile.currentTextChanged.connect(self.show_segment_info)
        self.pipeline_profile.currentTextChanged.connect(self.show_segment)
        self.mask_name.textChanged.connect(self.mask_name_changed)
        self.generate_mask_btn.clicked.connect(self.create_mask)
        self.clean_plan_btn.clicked.connect(self.clean_plan)
        self.remove_btn.clicked.connect(self.remove_element)
        self.mask_name.textChanged.connect(self.mask_text_changed)
        self.chose_profile_btn.clicked.connect(self.add_segmentation)
        self.get_big_btn.clicked.connect(self.add_leave_biggest)
        self.add_calculation_btn.clicked.connect(self.add_measurement)
        self.save_plan_btn.clicked.connect(self.add_calculation_plan)
        # self.forgot_mask_btn.clicked.connect(self.forgot_mask)
        # self.cmap_save_btn.clicked.connect(self.save_to_cmap)
        self.save_btn.clicked.connect(self.add_save_to_project)
        self.update_element_chk.stateChanged.connect(self.mask_text_changed)
        self.update_element_chk.stateChanged.connect(self.show_measurement)
        self.update_element_chk.stateChanged.connect(self.show_segment)
        self.update_element_chk.stateChanged.connect(self.update_names)
        self.segment_stack.currentChanged.connect(self.change_segmentation_table)

        plan_box = QGroupBox("Prepare workflow:")
        lay = QVBoxLayout()
        lay.addWidget(self.plan)
        bt_lay = QGridLayout()
        bt_lay.setSpacing(1)
        bt_lay.addWidget(self.save_plan_btn, 0, 0)
        bt_lay.addWidget(self.clean_plan_btn, 0, 1)
        bt_lay.addWidget(self.remove_btn, 1, 0)
        bt_lay.addWidget(self.update_element_chk, 1, 1)
        lay.addLayout(bt_lay)
        plan_box.setLayout(lay)
        plan_box.setStyleSheet(group_sheet)

        other_box = QGroupBox("Other operations:")
        other_box.setContentsMargins(0, 0, 0, 0)
        bt_lay = QVBoxLayout()
        bt_lay.setSpacing(0)
        bt_lay.addWidget(QLabel("Root type:"))
        bt_lay.addWidget(self.change_root)
        bt_lay.addStretch(1)
        bt_lay.addWidget(QLabel("Saving:"))
        bt_lay.addWidget(self.save_choose)
        bt_lay.addWidget(self.save_btn)
        other_box.setLayout(bt_lay)
        other_box.setStyleSheet(group_sheet)

        mask_box = QGroupBox("Use mask from:")
        mask_box.setStyleSheet(group_sheet)
        self.mask_stack = QTabWidget()

        self.mask_stack.addTab(stretch_widget(self.file_mask), "File")
        self.mask_stack.addTab(stretch_widget(self.segmentation_mask), "Current ROI")
        self.mask_stack.addTab(stretch_widget(self.mask_operation), "Operations on masks")
        self.mask_stack.setTabToolTip(2, "Allows to create mask which is based on masks previously added to plan.")

        lay = QGridLayout()
        lay.setSpacing(0)
        lay.addWidget(self.mask_stack, 0, 0, 1, 2)
        label = QLabel("Mask name:")
        label.setToolTip("Needed if you would like to reuse this mask in tab 'Operations on masks'")
        self.mask_name.setToolTip("Needed if you would like to reuse this mask in tab 'Operations on masks'")
        lay.addWidget(label, 1, 0)
        lay.addWidget(self.mask_name, 1, 1)
        lay.addWidget(self.generate_mask_btn, 2, 0, 1, 2)
        mask_box.setLayout(lay)

        segment_box = QGroupBox("ROI extraction:")
        segment_box.setStyleSheet(group_sheet)
        lay = QVBoxLayout()
        lay.setSpacing(0)
        lay.addWidget(self.segment_stack)
        lay.addWidget(self.chose_profile_btn)
        lay.addWidget(self.get_big_btn)
        segment_box.setLayout(lay)

        measurement_box = QGroupBox("Set of measurements:")
        measurement_box.setStyleSheet(group_sheet)
        lay = QGridLayout()
        lay.setSpacing(0)
        lay.addWidget(self.measurements_list, 0, 0, 1, 2)
        lab = QLabel("Name prefix:")
        lab.setToolTip("Prefix added before each column name")
        lay.addWidget(lab, 1, 0)
        lay.addWidget(self.measurement_name_prefix, 1, 1)
        lay.addWidget(QLabel("Channel:"), 2, 0)
        lay.addWidget(self.choose_channel_for_measurements, 2, 1)
        lay.addWidget(QLabel("Units:"), 3, 0)
        lay.addWidget(self.units_choose, 3, 1)
        lay.addWidget(self.add_calculation_btn, 4, 0, 1, 2)
        measurement_box.setLayout(lay)

        info_box = QGroupBox("Information")
        info_box.setStyleSheet(group_sheet)
        lay = QVBoxLayout()
        lay.addWidget(self.information)
        info_box.setLayout(lay)

        layout = QGridLayout()
        fst_col = QVBoxLayout()
        fst_col.addWidget(plan_box, 1)
        fst_col.addWidget(mask_box)
        layout.addWidget(plan_box, 0, 0, 5, 1)
        # layout.addWidget(plan_box, 0, 0, 3, 1)
        # layout.addWidget(mask_box, 3, 0, 2, 1)
        # layout.addWidget(segmentation_mask_box, 1, 1)
        layout.addWidget(mask_box, 0, 2, 1, 2)
        layout.addWidget(other_box, 0, 1)
        layout.addWidget(segment_box, 1, 1, 1, 2)
        layout.addWidget(measurement_box, 1, 3)
        layout.addWidget(info_box, 3, 1, 1, 3)
        self.setLayout(layout)

        self.generate_mask_btn.setDisabled(True)
        self.chose_profile_btn.setDisabled(True)
        self.add_calculation_btn.setDisabled(True)

        self.mask_allow = False
        self.segment_allow = False
        self.file_mask_allow = False
        self.node_type = NodeType.root
        self.node_name = ""
        self.plan_node_changed.connect(self.mask_text_changed)
        self.plan.changed_node.connect(self.node_type_changed)
        self.plan_node_changed.connect(self.show_segment)
        self.plan_node_changed.connect(self.show_measurement)
        self.plan_node_changed.connect(self.mask_stack_change)
        self.mask_stack.currentChanged.connect(self.mask_stack_change)
        self.file_mask.value_changed.connect(self.mask_stack_change)
        self.mask_name.textChanged.connect(self.mask_stack_change)
        self.node_type_changed()
예제 #3
0
class TestCalculationProcess:
    @staticmethod
    def create_calculation_plan():
        parameters = {
            "channel": 1,
            "minimum_size": 200,
            "threshold": {
                "name": "Base/Core",
                "values": {
                    "core_threshold": {
                        "name": "Manual",
                        "values": {
                            "threshold": 30000
                        }
                    },
                    "base_threshold": {
                        "name": "Manual",
                        "values": {
                            "threshold": 13000
                        }
                    },
                },
            },
            "noise_filtering": {
                "name": "Gauss",
                "values": {
                    "dimension_type": DimensionType.Layer,
                    "radius": 1.0
                }
            },
            "side_connection": False,
            "sprawl_type": {
                "name": "Euclidean",
                "values": {}
            },
        }

        segmentation = ROIExtractionProfile(
            name="test",
            algorithm="Lower threshold with watershed",
            values=parameters)
        mask_suffix = MaskSuffix(name="", suffix="_mask")
        chosen_fields = [
            MeasurementEntry(
                name="Segmentation Volume",
                calculation_tree=Leaf(name="Volume",
                                      area=AreaType.ROI,
                                      per_component=PerComponent.No),
            ),
            MeasurementEntry(
                name="Segmentation Volume/Mask Volume",
                calculation_tree=Node(
                    left=Leaf(name="Volume",
                              area=AreaType.ROI,
                              per_component=PerComponent.No),
                    op="/",
                    right=Leaf(name="Volume",
                               area=AreaType.Mask,
                               per_component=PerComponent.No),
                ),
            ),
            MeasurementEntry(
                "Segmentation Components Number",
                calculation_tree=Leaf("Components number",
                                      area=AreaType.ROI,
                                      per_component=PerComponent.No),
            ),
        ]
        statistic = MeasurementProfile(name="base_measure",
                                       chosen_fields=chosen_fields,
                                       name_prefix="")
        statistic_calculate = MeasurementCalculate(
            channel=0,
            units=Units.µm,
            measurement_profile=statistic,
            name_prefix="")
        tree = CalculationTree(
            RootType.Image,
            [
                CalculationTree(mask_suffix, [
                    CalculationTree(segmentation,
                                    [CalculationTree(statistic_calculate, [])])
                ])
            ],
        )
        return CalculationPlan(tree=tree, name="test")

    @staticmethod
    def create_calculation_plan2():
        parameters = {
            "channel": 0,
            "minimum_size": 200,
            "threshold": {
                "name": "Base/Core",
                "values": {
                    "core_threshold": {
                        "name": "Manual",
                        "values": {
                            "threshold": 30000
                        }
                    },
                    "base_threshold": {
                        "name": "Manual",
                        "values": {
                            "threshold": 13000
                        }
                    },
                },
            },
            "noise_filtering": {
                "name": "Gauss",
                "values": {
                    "dimension_type": DimensionType.Layer,
                    "radius": 1.0
                }
            },
            "side_connection": False,
            "sprawl_type": {
                "name": "Euclidean",
                "values": {}
            },
        }

        segmentation = ROIExtractionProfile(
            name="test",
            algorithm="Lower threshold with watershed",
            values=parameters)
        chosen_fields = [
            MeasurementEntry(
                name="Segmentation Volume",
                calculation_tree=Leaf(name="Volume",
                                      area=AreaType.ROI,
                                      per_component=PerComponent.No),
            ),
            MeasurementEntry(
                name="Segmentation Volume/Mask Volume",
                calculation_tree=Node(
                    left=Leaf(name="Volume",
                              area=AreaType.ROI,
                              per_component=PerComponent.No),
                    op="/",
                    right=Leaf(name="Volume",
                               area=AreaType.Mask,
                               per_component=PerComponent.No),
                ),
            ),
            MeasurementEntry(
                "Segmentation Components Number",
                calculation_tree=Leaf("Components number",
                                      area=AreaType.ROI,
                                      per_component=PerComponent.No),
            ),
        ]
        statistic = MeasurementProfile(name="base_measure",
                                       chosen_fields=chosen_fields,
                                       name_prefix="")
        statistic_calculate = MeasurementCalculate(
            channel=0,
            units=Units.µm,
            measurement_profile=statistic,
            name_prefix="")
        tree = CalculationTree(RootType.Mask_project, [
            CalculationTree(segmentation,
                            [CalculationTree(statistic_calculate, [])])
        ])
        return CalculationPlan(tree=tree, name="test2")

    @staticmethod
    def create_simple_plan(root_type: RootType, save: Save):
        parameters = {
            "channel": 0,
            "minimum_size": 200,
            "threshold": {
                "name": "Manual",
                "values": {
                    "threshold": 13000
                }
            },
            "noise_filtering": {
                "name": "Gauss",
                "values": {
                    "dimension_type": DimensionType.Layer,
                    "radius": 1.0
                }
            },
            "side_connection": False,
        }
        segmentation = ROIExtractionProfile(name="test",
                                            algorithm="Lower threshold",
                                            values=parameters)
        chosen_fields = [
            MeasurementEntry(
                name="Segmentation Volume",
                calculation_tree=Leaf(name="Volume",
                                      area=AreaType.ROI,
                                      per_component=PerComponent.No),
            ),
        ]
        statistic = MeasurementProfile(name="base_measure",
                                       chosen_fields=chosen_fields,
                                       name_prefix="")
        statistic_calculate = MeasurementCalculate(
            channel=-1,
            units=Units.µm,
            measurement_profile=statistic,
            name_prefix="")
        tree = CalculationTree(
            root_type,
            [
                CalculationTree(segmentation, [
                    CalculationTree(statistic_calculate, []),
                    CalculationTree(save, [])
                ])
            ],
        )
        return CalculationPlan(tree=tree, name="test")

    @staticmethod
    def create_calculation_plan3():
        parameters = {
            "channel": 1,
            "minimum_size": 200,
            "threshold": {
                "name": "Base/Core",
                "values": {
                    "core_threshold": {
                        "name": "Manual",
                        "values": {
                            "threshold": 30000
                        }
                    },
                    "base_threshold": {
                        "name": "Manual",
                        "values": {
                            "threshold": 13000
                        }
                    },
                },
            },
            "noise_filtering": {
                "name": "Gauss",
                "values": {
                    "dimension_type": DimensionType.Layer,
                    "radius": 1.0
                }
            },
            "side_connection": False,
            "sprawl_type": {
                "name": "Euclidean",
                "values": {}
            },
        }

        segmentation = ROIExtractionProfile(
            name="test",
            algorithm="Lower threshold with watershed",
            values=parameters)
        mask_suffix = MaskSuffix(name="", suffix="_mask")
        chosen_fields = [
            MeasurementEntry(
                name="Segmentation Volume",
                calculation_tree=Leaf(name="Volume",
                                      area=AreaType.ROI,
                                      per_component=PerComponent.No),
            ),
            MeasurementEntry(
                name="Segmentation Volume/Mask Volume",
                calculation_tree=Node(
                    left=Leaf(name="Volume",
                              area=AreaType.ROI,
                              per_component=PerComponent.No),
                    op="/",
                    right=Leaf(name="Volume",
                               area=AreaType.Mask,
                               per_component=PerComponent.No),
                ),
            ),
            MeasurementEntry(
                "Segmentation Components Number",
                calculation_tree=Leaf("Components number",
                                      area=AreaType.ROI,
                                      per_component=PerComponent.No),
            ),
            MeasurementEntry(
                "Segmentation Volume per component",
                calculation_tree=Leaf("Volume",
                                      area=AreaType.ROI,
                                      per_component=PerComponent.Yes),
            ),
        ]
        statistic = MeasurementProfile(name="base_measure",
                                       chosen_fields=chosen_fields,
                                       name_prefix="")
        statistic_calculate = MeasurementCalculate(
            channel=0,
            units=Units.µm,
            measurement_profile=statistic,
            name_prefix="")
        mask_create = MaskCreate(
            "",
            MaskProperty(RadiusType.NO, 0, RadiusType.NO, 0, True, False,
                         False))
        parameters2 = {
            "channel": 1,
            "minimum_size": 200,
            "threshold": {
                "name": "Manual",
                "values": {
                    "threshold": 30000
                }
            },
            "noise_filtering": {
                "name": "Gauss",
                "values": {
                    "dimension_type": DimensionType.Layer,
                    "radius": 1.0
                }
            },
            "side_connection": False,
        }

        segmentation2 = ROIExtractionProfile(name="test",
                                             algorithm="Lower threshold",
                                             values=parameters2)
        chosen_fields = [
            MeasurementEntry(
                name="Segmentation Volume",
                calculation_tree=Leaf(name="Volume",
                                      area=AreaType.ROI,
                                      per_component=PerComponent.No),
            ),
            MeasurementEntry(
                name="Segmentation Volume/Mask Volume",
                calculation_tree=Node(
                    left=Leaf(name="Volume",
                              area=AreaType.ROI,
                              per_component=PerComponent.No),
                    op="/",
                    right=Leaf(name="Volume",
                               area=AreaType.Mask,
                               per_component=PerComponent.No),
                ),
            ),
            MeasurementEntry(
                "Segmentation Components Number",
                calculation_tree=Leaf("Components number",
                                      area=AreaType.ROI,
                                      per_component=PerComponent.No),
            ),
            MeasurementEntry(
                "Mask Volume per component",
                calculation_tree=Leaf("Volume",
                                      area=AreaType.Mask,
                                      per_component=PerComponent.Yes),
            ),
        ]
        statistic = MeasurementProfile(name="base_measure2",
                                       chosen_fields=chosen_fields[:],
                                       name_prefix="aa_")
        statistic_calculate2 = MeasurementCalculate(
            channel=0,
            units=Units.µm,
            measurement_profile=statistic,
            name_prefix="")
        chosen_fields.append(
            MeasurementEntry(
                "Segmentation Volume per component",
                calculation_tree=Leaf("Volume",
                                      area=AreaType.ROI,
                                      per_component=PerComponent.Yes),
            ))
        statistic = MeasurementProfile(name="base_measure3",
                                       chosen_fields=chosen_fields[:],
                                       name_prefix="bb_")
        statistic_calculate3 = MeasurementCalculate(
            channel=0,
            units=Units.µm,
            measurement_profile=statistic,
            name_prefix="")
        tree = CalculationTree(
            RootType.Image,
            [
                CalculationTree(
                    mask_suffix,
                    [
                        CalculationTree(
                            segmentation,
                            [
                                CalculationTree(statistic_calculate, []),
                                CalculationTree(
                                    mask_create,
                                    [
                                        CalculationTree(
                                            segmentation2,
                                            [
                                                CalculationTree(
                                                    statistic_calculate2, []),
                                                CalculationTree(
                                                    statistic_calculate3, []),
                                            ],
                                        ),
                                    ],
                                ),
                            ],
                        )
                    ],
                )
            ],
        )
        return CalculationPlan(tree=tree, name="test")

    @staticmethod
    def create_mask_operation_plan(mask_op):
        parameters = {
            "channel": 0,
            "minimum_size": 200,
            "threshold": {
                "name": "Manual",
                "values": {
                    "threshold": 13000
                }
            },
            "noise_filtering": {
                "name": "Gauss",
                "values": {
                    "dimension_type": DimensionType.Layer,
                    "radius": 1.0
                }
            },
            "side_connection": False,
        }
        parameters2 = dict(**parameters)
        parameters2["channel"] = 1
        segmentation = ROIExtractionProfile(name="test",
                                            algorithm="Lower threshold",
                                            values=parameters)
        segmentation2 = ROIExtractionProfile(name="test2",
                                             algorithm="Lower threshold",
                                             values=parameters2)
        chosen_fields = [
            MeasurementEntry(
                name="Segmentation Volume",
                calculation_tree=Leaf(name="Volume",
                                      area=AreaType.ROI,
                                      per_component=PerComponent.No),
            ),
        ]
        statistic = MeasurementProfile(name="base_measure",
                                       chosen_fields=chosen_fields,
                                       name_prefix="")
        statistic_calculate = MeasurementCalculate(
            channel=-1,
            units=Units.µm,
            measurement_profile=statistic,
            name_prefix="")
        tree = CalculationTree(
            RootType.Image,
            [
                CalculationTree(
                    segmentation,
                    [
                        CalculationTree(
                            MaskCreate(
                                name="test1",
                                mask_property=MaskProperty.simple_mask()), [])
                    ],
                ),
                CalculationTree(
                    segmentation2,
                    [
                        CalculationTree(
                            MaskCreate(
                                name="test2",
                                mask_property=MaskProperty.simple_mask()), [])
                    ],
                ),
                CalculationTree(mask_op, [
                    CalculationTree(segmentation2,
                                    [CalculationTree(statistic_calculate, [])])
                ]),
            ],
        )
        return CalculationPlan(tree=tree, name="test")

    @pytest.mark.parametrize("mask_op", [
        MaskUse("test1"),
        MaskSum("", "test1", "test2"),
        MaskIntersection("", "test1", "test2")
    ])
    def test_mask_op(self, mask_op, data_test_dir, tmpdir):
        plan = self.create_mask_operation_plan(mask_op)
        file_path = os.path.join(data_test_dir, "stack1_components",
                                 "stack1_component1.tif")
        calc = Calculation(
            [file_path],
            base_prefix=os.path.dirname(file_path),
            result_prefix=tmpdir,
            measurement_file_path=os.path.join(tmpdir, "test3.xlsx"),
            sheet_name="Sheet1",
            calculation_plan=plan,
            voxel_size=(1, 1, 1),
        )
        calc_process = CalculationProcess()
        res = calc_process.do_calculation(FileCalculation(file_path, calc))
        assert isinstance(res, list)
        assert isinstance(res[0], ResponseData)

    def test_one_file(self, data_test_dir):
        plan = self.create_calculation_plan()
        process = CalculationProcess()
        file_path = os.path.join(data_test_dir, "stack1_components",
                                 "stack1_component5.tif")
        calc = MocksCalculation(file_path)
        process.calculation = calc
        process.image = TiffImageReader.read_image(file_path)
        process.iterate_over(plan.execution_tree)
        assert len(process.measurement[0]) == 3

    @pytest.mark.filterwarnings("ignore:This method will be removed")
    def test_full_pipeline_base(self, tmpdir, data_test_dir, monkeypatch):
        monkeypatch.setattr(batch_backend, "CalculationProcess",
                            MockCalculationProcess)
        plan = self.create_calculation_plan()
        file_pattern = os.path.join(data_test_dir, "stack1_components",
                                    "stack1_component*[0-9].tif")
        file_paths = sorted(glob(file_pattern))
        assert os.path.basename(file_paths[0]) == "stack1_component1.tif"
        calc = Calculation(
            file_paths,
            base_prefix=data_test_dir,
            result_prefix=data_test_dir,
            measurement_file_path=os.path.join(tmpdir, "test.xlsx"),
            sheet_name="Sheet1",
            calculation_plan=plan,
            voxel_size=(1, 1, 1),
        )
        calc_process = CalculationProcess()
        for file_path in file_paths:
            res = calc_process.do_calculation(FileCalculation(file_path, calc))
            assert isinstance(res, list)
            assert isinstance(res[0], ResponseData)

    @pytest.mark.filterwarnings("ignore:This method will be removed")
    def test_full_pipeline(self, tmpdir, data_test_dir, monkeypatch):
        monkeypatch.setattr(batch_backend, "CalculationProcess",
                            MockCalculationProcess)
        plan = self.create_calculation_plan()
        file_pattern = os.path.join(data_test_dir, "stack1_components",
                                    "stack1_component*[0-9].tif")
        file_paths = sorted(glob(file_pattern))
        assert os.path.basename(file_paths[0]) == "stack1_component1.tif"
        calc = Calculation(
            file_paths,
            base_prefix=data_test_dir,
            result_prefix=data_test_dir,
            measurement_file_path=os.path.join(tmpdir, "test.xlsx"),
            sheet_name="Sheet1",
            calculation_plan=plan,
            voxel_size=(1, 1, 1),
        )

        manager = CalculationManager()
        manager.set_number_of_workers(3)
        manager.add_calculation(calc)

        for _ in range(int(120 / 0.1)):
            manager.get_results()
            if manager.has_work:
                time.sleep(0.1)
            else:
                break
        else:
            manager.kill_jobs()
            pytest.fail("jobs hanged")

        manager.writer.finish()
        if sys.platform == "darwin":
            time.sleep(2)
        else:
            time.sleep(0.4)
        assert os.path.exists(os.path.join(tmpdir, "test.xlsx"))
        df = pd.read_excel(os.path.join(tmpdir, "test.xlsx"),
                           index_col=0,
                           header=[0, 1],
                           engine=ENGINE)
        assert df.shape == (8, 4)
        for i in range(8):
            assert os.path.basename(
                df.name.units[i]) == f"stack1_component{i+1}.tif"

    @pytest.mark.filterwarnings("ignore:This method will be removed")
    def test_full_pipeline_error(self, tmp_path_factory, data_test_dir,
                                 monkeypatch):
        plan = self.create_calculation_plan()
        data_dir = tmp_path_factory.mktemp("data")
        file_pattern_copy = os.path.join(data_test_dir, "stack1_components",
                                         "stack1_component*.tif")
        file_paths = sorted(glob(file_pattern_copy))
        for el in file_paths:
            shutil.copy(el, data_dir)
            shutil.copy(data_dir / "stack1_component1.tif",
                        data_dir / "stack1_component10.tif")
        file_pattern = os.path.join(data_dir, "stack1_component*[0-9].tif")
        file_paths = sorted(glob(file_pattern))
        result_dir = tmp_path_factory.mktemp("result")

        assert os.path.basename(file_paths[0]) == "stack1_component1.tif"
        calc = Calculation(
            file_paths,
            base_prefix=str(data_dir),
            result_prefix=str(data_dir),
            measurement_file_path=os.path.join(result_dir, "test.xlsx"),
            sheet_name="Sheet1",
            calculation_plan=plan,
            voxel_size=(1, 1, 1),
        )

        manager = CalculationManager()
        manager.set_number_of_workers(3)
        manager.add_calculation(calc)

        while manager.has_work:
            time.sleep(0.1)
            manager.get_results()
        manager.writer.finish()
        if sys.platform == "darwin":
            time.sleep(2)
        else:
            time.sleep(0.4)
        assert os.path.exists(os.path.join(result_dir, "test.xlsx"))
        df = pd.read_excel(os.path.join(result_dir, "test.xlsx"),
                           index_col=0,
                           header=[0, 1],
                           engine=ENGINE)
        assert df.shape == (8, 4)
        for i in range(8):
            assert os.path.basename(
                df.name.units[i]) == f"stack1_component{i + 1}.tif"
        df2 = pd.read_excel(os.path.join(result_dir, "test.xlsx"),
                            sheet_name="Errors",
                            index_col=0,
                            engine=ENGINE)
        assert df2.shape == (1, 2)
        str(df2.loc[0]["error description"]).startswith("[Errno 2]")

    @pytest.mark.filterwarnings("ignore:This method will be removed")
    def test_full_pipeline_mask_project(self, tmpdir, data_test_dir):
        plan = self.create_calculation_plan2()
        file_pattern = os.path.join(data_test_dir, "*nucleus.seg")
        file_paths = glob(file_pattern)
        calc = Calculation(
            file_paths,
            base_prefix=data_test_dir,
            result_prefix=data_test_dir,
            measurement_file_path=os.path.join(tmpdir, "test2.xlsx"),
            sheet_name="Sheet1",
            calculation_plan=plan,
            voxel_size=(1, 1, 1),
        )

        manager = CalculationManager()
        manager.set_number_of_workers(2)
        manager.add_calculation(calc)

        while manager.has_work:
            time.sleep(0.1)
            manager.get_results()
        if sys.platform == "darwin":
            time.sleep(2)
        else:
            time.sleep(0.4)
        manager.writer.finish()
        assert os.path.exists(os.path.join(tmpdir, "test2.xlsx"))
        df = pd.read_excel(os.path.join(tmpdir, "test2.xlsx"),
                           index_col=0,
                           header=[0, 1],
                           engine=ENGINE)
        assert df.shape == (2, 4)

    def test_do_calculation(self, tmpdir, data_test_dir):
        plan = self.create_calculation_plan3()
        file_path = os.path.join(data_test_dir, "stack1_components",
                                 "stack1_component1.tif")
        calc = Calculation(
            [file_path],
            base_prefix=data_test_dir,
            result_prefix=data_test_dir,
            measurement_file_path=os.path.join(tmpdir, "test3.xlsx"),
            sheet_name="Sheet1",
            calculation_plan=plan,
            voxel_size=(1, 1, 1),
        )
        index, res = do_calculation((1, file_path), calc)
        assert index == 1
        assert isinstance(res, list)
        assert isinstance(res[0], ResponseData)

    @pytest.mark.parametrize(
        "file_name,root_type",
        [
            (os.path.join("stack1_components",
                          "stack1_component1.tif"), RootType.Image),
            ("stack1_component1_1.tgz", RootType.Image),
            ("stack1_component1_1.tgz", RootType.Project),
            ("test_nucleus_1_1.seg", RootType.Image),
            ("test_nucleus_1_1.seg", RootType.Mask_project),
        ],
    )
    @pytest.mark.parametrize("save_method", save_dict.values())
    def test_do_calculation_save(self, tmpdir, data_test_dir, file_name,
                                 root_type, save_method: SaveBase):
        save_desc = Save("_test", "", save_method.get_name(),
                         save_method.get_short_name(),
                         save_method.get_default_values())
        plan = self.create_simple_plan(root_type, save_desc)
        file_path = os.path.join(data_test_dir, file_name)
        calc = Calculation(
            [file_path],
            base_prefix=os.path.dirname(file_path),
            result_prefix=tmpdir,
            measurement_file_path=os.path.join(tmpdir, "test3.xlsx"),
            sheet_name="Sheet1",
            calculation_plan=plan,
            voxel_size=(1, 1, 1),
        )
        calc_process = CalculationProcess()
        res = calc_process.do_calculation(FileCalculation(file_path, calc))
        assert isinstance(res, list)
        assert isinstance(res[0], ResponseData)

    def test_do_calculation_calculation_process(self, tmpdir, data_test_dir):
        plan = self.create_calculation_plan3()
        file_path = os.path.join(data_test_dir, "stack1_components",
                                 "stack1_component1.tif")
        calc = Calculation(
            [file_path],
            base_prefix=data_test_dir,
            result_prefix=data_test_dir,
            measurement_file_path=os.path.join(tmpdir, "test3.xlsx"),
            sheet_name="Sheet1",
            calculation_plan=plan,
            voxel_size=(1, 1, 1),
        )
        calc_process = CalculationProcess()
        res = calc_process.do_calculation(FileCalculation(file_path, calc))
        assert isinstance(res, list)
        assert isinstance(res[0], ResponseData)

    @pytest.mark.filterwarnings("ignore:This method will be removed")
    def test_full_pipeline_component_split_no_process(self, tmpdir,
                                                      data_test_dir,
                                                      monkeypatch):
        monkeypatch.setattr(batch_backend, "CalculationProcess",
                            MockCalculationProcess)
        plan = self.create_calculation_plan3()
        file_pattern = os.path.join(data_test_dir, "stack1_components",
                                    "stack1_component*[0-9].tif")
        file_paths = sorted(glob(file_pattern))
        assert os.path.basename(file_paths[0]) == "stack1_component1.tif"
        calc = Calculation(
            file_paths,
            base_prefix=data_test_dir,
            result_prefix=data_test_dir,
            measurement_file_path=os.path.join(tmpdir, "test.xlsx"),
            sheet_name="Sheet1",
            calculation_plan=plan,
            voxel_size=(1, 1, 1),
        )
        calc_process = CalculationProcess()
        for file_path in file_paths:
            res = calc_process.do_calculation(FileCalculation(file_path, calc))
            assert isinstance(res, list)
            assert isinstance(res[0], ResponseData)

    @pytest.mark.filterwarnings("ignore:This method will be removed")
    def test_full_pipeline_component_split(self, tmpdir, data_test_dir):
        plan = self.create_calculation_plan3()
        file_pattern = os.path.join(data_test_dir, "stack1_components",
                                    "stack1_component*[0-9].tif")
        file_paths = glob(file_pattern)
        calc = Calculation(
            file_paths,
            base_prefix=data_test_dir,
            result_prefix=data_test_dir,
            measurement_file_path=os.path.join(tmpdir, "test3.xlsx"),
            sheet_name="Sheet1",
            calculation_plan=plan,
            voxel_size=(1, 1, 1),
        )

        manager = CalculationManager()
        manager.set_number_of_workers(2)
        manager.add_calculation(calc)

        while manager.has_work:
            time.sleep(0.1)
            res = manager.get_results()
            if res.errors:
                print(res.errors, file=sys.stderr)
        if sys.platform == "darwin":
            time.sleep(2)
        else:
            time.sleep(0.4)
        manager.writer.finish()
        assert os.path.exists(os.path.join(tmpdir, "test3.xlsx"))
        df = pd.read_excel(os.path.join(tmpdir, "test3.xlsx"),
                           index_col=0,
                           header=[0, 1],
                           engine=ENGINE)
        assert df.shape == (8, 10)
        df2 = pd.read_excel(os.path.join(tmpdir, "test3.xlsx"),
                            sheet_name=1,
                            index_col=0,
                            header=[0, 1],
                            engine=ENGINE)
        assert df2.shape[0] > 8
        assert df2.shape == (
            df["Segmentation Components Number"]["count"].sum(), 6)
        df3 = pd.read_excel(os.path.join(tmpdir, "test3.xlsx"),
                            sheet_name=2,
                            index_col=0,
                            header=[0, 1],
                            engine=ENGINE)
        assert df3.shape == (
            df["Segmentation Components Number"]["count"].sum(), 6)
        df4 = pd.read_excel(os.path.join(tmpdir, "test3.xlsx"),
                            sheet_name=3,
                            index_col=0,
                            header=[0, 1],
                            engine=ENGINE)
        assert df4.shape == (
            df["Segmentation Components Number"]["count"].sum(), 8)