Skip to content

advanced_config

A dialog widget allowing modifying advanced settings.

AdvancedConfigurationWindow #

Bases: QDialog

A dialog widget allowing modifying advanced settings.

Parameters:

Name Type Description Default
parent QWidget

Parent widget.

required
careamics_config BaseConfig
The configuration for the CAREamics algorithm.
required
Source code in src/careamics_napari/widgets/advanced_config.py
class AdvancedConfigurationWindow(QDialog):
    """A dialog widget allowing modifying advanced settings.

    Parameters
    ----------
    parent : QWidget
        Parent widget.
    careamics_config : BaseConfig
            The configuration for the CAREamics algorithm.
    """

    def __init__(
        self,
        parent: QWidget | None,
        careamics_config: BaseConfig,
        algorithm_config: AdvancedConfig | None = None,
    ) -> None:
        """Initialize the widget.

        Parameters
        ----------
        parent : QWidget
            Parent widget.
        careamics_config : BaseConfig
            The configuration for the CAREamics algorithm.
        """
        super().__init__(parent)
        self.setWindowModality(Qt.ApplicationModal)  # type: ignore

        self.configuration = careamics_config
        self.advanced_configuration = algorithm_config

        self.tabs = QTabWidget()
        self.add_common_tab()

        self.save_button = QPushButton("Save")
        self.save_button.setMaximumWidth(120)
        self.save_button.clicked.connect(self.save)

        vbox = QVBoxLayout()
        vbox.addWidget(self.tabs)
        vbox.addWidget(self.save_button)
        self.setLayout(vbox)

        # self.bind_properties()

    def add_common_tab(self) -> None:
        """Add common advanced setting tab."""
        # tab widget
        tab_widget = QWidget()

        # experiment name text box
        label = QLabel("Experiment Name:")
        self.experiment_txtbox = QLineEdit()
        self.experiment_txtbox.setText(self.configuration.experiment_name)
        self.experiment_txtbox.setToolTip(
            "Name of the experiment. It will be used to save the model\n"
            "and the training history."
        )

        # validation
        val_grpbox = QGroupBox("Validation")
        self.val_perc_spin = create_double_spinbox(
            0.01, 1, self.configuration.val_percentage, 0.01, n_decimal=2
        )
        self.val_perc_spin.setToolTip(
            "Percentage of the training data used for validation."
        )
        self.val_split_spin = create_int_spinbox(
            1, 100, self.configuration.val_minimum_split, 1
        )
        self.val_split_spin.setToolTip(
            "Minimum number of patches or images in the validation set."
        )

        # augmentations group box, with x_flip, y_flip and rotations
        _x_flip = True
        _y_flip = True
        _rotations = True
        if self.advanced_configuration is not None:
            _x_flip = self.advanced_configuration.x_flip
            _y_flip = self.advanced_configuration.y_flip
            _rotations = self.advanced_configuration.rotations

        augment_grpbox = QGroupBox("Augmentations")
        self.x_flip_chkbox = QCheckBox("X Flip")
        self.x_flip_chkbox.setChecked(_x_flip)
        self.x_flip_chkbox.setToolTip(
            "Check to add augmentation that flips the image\nalong the x-axis"
        )
        self.y_flip_chkbox = QCheckBox("Y Flip")
        self.y_flip_chkbox.setChecked(_y_flip)
        self.y_flip_chkbox.setToolTip(
            "Check to add augmentation that flips the image\nalong the y-axis"
        )
        self.rotations_chkbox = QCheckBox("90 Rotations")
        self.rotations_chkbox.setChecked(_rotations)
        self.rotations_chkbox.setToolTip(
            "Check to add augmentation that rotates the image\n"
            "in 90 degree increments in XY"
        )

        # model params
        _depth = 2
        _num_filters = 32
        _indi_channels = True
        if isinstance(self.configuration.algorithm_config.model, UNetModel):
            _depth = self.configuration.algorithm_config.model.depth
            _num_filters = self.configuration.algorithm_config.model.num_channels_init
            _indi_channels = (
                self.configuration.algorithm_config.model.independent_channels
            )

        model_grpbox = QGroupBox("UNet parameters")
        self.model_depth_spin = create_int_spinbox(2, 5, _depth, 1)
        self.model_depth_spin.setToolTip("Depth of the U-Net model.")
        self.num_conv_filters_spin = create_int_spinbox(8, 1024, _num_filters, 8)
        self.num_conv_filters_spin.setToolTip(
            "Number of convolutional filters in the first layer."
        )

        # independent channels checkbox
        self.indi_channels_chkbox = QCheckBox("Independent Channels")
        self.indi_channels_chkbox.setChecked(_indi_channels)
        self.indi_channels_chkbox.setToolTip(
            "Check to treat the channels independently during\ntraining."
        )
        self.indi_channels_chkbox.setEnabled(
            "C" in self.configuration.data_config.axes  # type: ignore
        )

        # layout
        layout = QVBoxLayout()
        hbox = QHBoxLayout()
        hbox.addWidget(label)
        hbox.addWidget(self.experiment_txtbox)
        layout.addLayout(hbox)
        layout.addSpacing(10)

        validation_layout = QFormLayout()
        validation_layout.setFormAlignment(Qt.AlignLeft | Qt.AlignTop)  # type: ignore
        validation_layout.setFieldGrowthPolicy(
            QFormLayout.AllNonFixedFieldsGrow  # type: ignore
        )
        validation_layout.addRow("Percentage", self.val_perc_spin)
        validation_layout.addRow("Minimum split", self.val_split_spin)
        val_grpbox.setLayout(validation_layout)
        layout.addWidget(val_grpbox)
        layout.addSpacing(10)

        augmentations_layout = QFormLayout()
        augmentations_layout.setFormAlignment(Qt.AlignLeft | Qt.AlignTop)  # type: ignore
        augmentations_layout.setFieldGrowthPolicy(
            QFormLayout.AllNonFixedFieldsGrow  # type: ignore
        )
        augmentations_layout.addRow(self.x_flip_chkbox)
        augmentations_layout.addRow(self.y_flip_chkbox)
        augmentations_layout.addRow(self.rotations_chkbox)
        augment_grpbox.setLayout(augmentations_layout)
        layout.addWidget(augment_grpbox)
        layout.addSpacing(10)

        model_params_layout = QFormLayout()
        model_params_layout.setFormAlignment(Qt.AlignLeft | Qt.AlignTop)  # type: ignore
        model_params_layout.setFieldGrowthPolicy(
            QFormLayout.AllNonFixedFieldsGrow  # type: ignore
        )
        model_params_layout.addRow("Depth", self.model_depth_spin)
        model_params_layout.addRow("# Filters", self.num_conv_filters_spin)
        model_grpbox.setLayout(model_params_layout)
        layout.addWidget(model_grpbox)
        layout.addSpacing(10)

        layout.addWidget(self.indi_channels_chkbox)

        tab_widget.setLayout(layout)
        self.tabs.addTab(tab_widget, "Common")

    def add_algorithm_specific_tab(self) -> None:
        """Add algorithm specific advanced settings tab."""
        raise NotImplementedError("Should be implemented by subclasses.")

    def save(self) -> None:
        """Save and close the window."""
        self.update_config()
        # self.close()  # should be closed in a subclass

    def bind_properties(self) -> None:
        """Create and bind class properties to the UI elements."""
        # type(self) returns the class of the instance, so we are adding
        # properties to the class itself, not the instance.
        type(self).experiment_name = bind(self.experiment_txtbox, "text")
        type(self).val_percentage = bind(self.val_perc_spin, "value")
        type(self).val_split = bind(self.val_split_spin, "value")
        type(self).x_flip = bind(self.x_flip_chkbox, "checked")
        type(self).y_flip = bind(self.y_flip_chkbox, "checked")
        type(self).rotation = bind(self.rotations_chkbox, "checked")
        type(self).model_depth = bind(self.model_depth_spin, "value")
        type(self).num_conv_filters = bind(self.num_conv_filters_spin, "value")
        type(self).indi_channels = bind(self.indi_channels_chkbox, "checked")

    def update_config(self) -> None:
        """Update the configuration object from UI elements."""
        self.configuration.experiment_name = self.experiment_name

        self.configuration.val_percentage = self.val_percentage
        self.configuration.val_minimum_split = self.val_split

        if isinstance(self.configuration.algorithm_config.model, UNetModel):
            self.configuration.algorithm_config.model.depth = self.model_depth
            self.configuration.algorithm_config.model.num_channels_init = (
                self.num_conv_filters
            )

            self.configuration.algorithm_config.model.independent_channels = (
                self.indi_channels
            )

        # update augmentations
        augs: list[Union[XYFlipModel, XYRandomRotate90Model]] = []
        if self.x_flip or self.y_flip:
            augs.append(XYFlipModel(flip_x=self.x_flip, flip_y=self.y_flip, p=0.5))
        if self.rotation:
            augs.append(XYRandomRotate90Model(p=0.5))
        self.configuration.data_config.transforms = augs  # type: ignore
        # update advanced config as well
        if self.advanced_configuration is not None:
            self.advanced_configuration.x_flip = self.x_flip
            self.advanced_configuration.y_flip = self.y_flip
            self.advanced_configuration.rotations = self.rotation

__init__(parent, careamics_config, algorithm_config=None) #

Initialize the widget.

Parameters:

Name Type Description Default
parent QWidget

Parent widget.

required
careamics_config BaseConfig

The configuration for the CAREamics algorithm.

required
Source code in src/careamics_napari/widgets/advanced_config.py
def __init__(
    self,
    parent: QWidget | None,
    careamics_config: BaseConfig,
    algorithm_config: AdvancedConfig | None = None,
) -> None:
    """Initialize the widget.

    Parameters
    ----------
    parent : QWidget
        Parent widget.
    careamics_config : BaseConfig
        The configuration for the CAREamics algorithm.
    """
    super().__init__(parent)
    self.setWindowModality(Qt.ApplicationModal)  # type: ignore

    self.configuration = careamics_config
    self.advanced_configuration = algorithm_config

    self.tabs = QTabWidget()
    self.add_common_tab()

    self.save_button = QPushButton("Save")
    self.save_button.setMaximumWidth(120)
    self.save_button.clicked.connect(self.save)

    vbox = QVBoxLayout()
    vbox.addWidget(self.tabs)
    vbox.addWidget(self.save_button)
    self.setLayout(vbox)

add_algorithm_specific_tab() #

Add algorithm specific advanced settings tab.

Source code in src/careamics_napari/widgets/advanced_config.py
def add_algorithm_specific_tab(self) -> None:
    """Add algorithm specific advanced settings tab."""
    raise NotImplementedError("Should be implemented by subclasses.")

add_common_tab() #

Add common advanced setting tab.

Source code in src/careamics_napari/widgets/advanced_config.py
def add_common_tab(self) -> None:
    """Add common advanced setting tab."""
    # tab widget
    tab_widget = QWidget()

    # experiment name text box
    label = QLabel("Experiment Name:")
    self.experiment_txtbox = QLineEdit()
    self.experiment_txtbox.setText(self.configuration.experiment_name)
    self.experiment_txtbox.setToolTip(
        "Name of the experiment. It will be used to save the model\n"
        "and the training history."
    )

    # validation
    val_grpbox = QGroupBox("Validation")
    self.val_perc_spin = create_double_spinbox(
        0.01, 1, self.configuration.val_percentage, 0.01, n_decimal=2
    )
    self.val_perc_spin.setToolTip(
        "Percentage of the training data used for validation."
    )
    self.val_split_spin = create_int_spinbox(
        1, 100, self.configuration.val_minimum_split, 1
    )
    self.val_split_spin.setToolTip(
        "Minimum number of patches or images in the validation set."
    )

    # augmentations group box, with x_flip, y_flip and rotations
    _x_flip = True
    _y_flip = True
    _rotations = True
    if self.advanced_configuration is not None:
        _x_flip = self.advanced_configuration.x_flip
        _y_flip = self.advanced_configuration.y_flip
        _rotations = self.advanced_configuration.rotations

    augment_grpbox = QGroupBox("Augmentations")
    self.x_flip_chkbox = QCheckBox("X Flip")
    self.x_flip_chkbox.setChecked(_x_flip)
    self.x_flip_chkbox.setToolTip(
        "Check to add augmentation that flips the image\nalong the x-axis"
    )
    self.y_flip_chkbox = QCheckBox("Y Flip")
    self.y_flip_chkbox.setChecked(_y_flip)
    self.y_flip_chkbox.setToolTip(
        "Check to add augmentation that flips the image\nalong the y-axis"
    )
    self.rotations_chkbox = QCheckBox("90 Rotations")
    self.rotations_chkbox.setChecked(_rotations)
    self.rotations_chkbox.setToolTip(
        "Check to add augmentation that rotates the image\n"
        "in 90 degree increments in XY"
    )

    # model params
    _depth = 2
    _num_filters = 32
    _indi_channels = True
    if isinstance(self.configuration.algorithm_config.model, UNetModel):
        _depth = self.configuration.algorithm_config.model.depth
        _num_filters = self.configuration.algorithm_config.model.num_channels_init
        _indi_channels = (
            self.configuration.algorithm_config.model.independent_channels
        )

    model_grpbox = QGroupBox("UNet parameters")
    self.model_depth_spin = create_int_spinbox(2, 5, _depth, 1)
    self.model_depth_spin.setToolTip("Depth of the U-Net model.")
    self.num_conv_filters_spin = create_int_spinbox(8, 1024, _num_filters, 8)
    self.num_conv_filters_spin.setToolTip(
        "Number of convolutional filters in the first layer."
    )

    # independent channels checkbox
    self.indi_channels_chkbox = QCheckBox("Independent Channels")
    self.indi_channels_chkbox.setChecked(_indi_channels)
    self.indi_channels_chkbox.setToolTip(
        "Check to treat the channels independently during\ntraining."
    )
    self.indi_channels_chkbox.setEnabled(
        "C" in self.configuration.data_config.axes  # type: ignore
    )

    # layout
    layout = QVBoxLayout()
    hbox = QHBoxLayout()
    hbox.addWidget(label)
    hbox.addWidget(self.experiment_txtbox)
    layout.addLayout(hbox)
    layout.addSpacing(10)

    validation_layout = QFormLayout()
    validation_layout.setFormAlignment(Qt.AlignLeft | Qt.AlignTop)  # type: ignore
    validation_layout.setFieldGrowthPolicy(
        QFormLayout.AllNonFixedFieldsGrow  # type: ignore
    )
    validation_layout.addRow("Percentage", self.val_perc_spin)
    validation_layout.addRow("Minimum split", self.val_split_spin)
    val_grpbox.setLayout(validation_layout)
    layout.addWidget(val_grpbox)
    layout.addSpacing(10)

    augmentations_layout = QFormLayout()
    augmentations_layout.setFormAlignment(Qt.AlignLeft | Qt.AlignTop)  # type: ignore
    augmentations_layout.setFieldGrowthPolicy(
        QFormLayout.AllNonFixedFieldsGrow  # type: ignore
    )
    augmentations_layout.addRow(self.x_flip_chkbox)
    augmentations_layout.addRow(self.y_flip_chkbox)
    augmentations_layout.addRow(self.rotations_chkbox)
    augment_grpbox.setLayout(augmentations_layout)
    layout.addWidget(augment_grpbox)
    layout.addSpacing(10)

    model_params_layout = QFormLayout()
    model_params_layout.setFormAlignment(Qt.AlignLeft | Qt.AlignTop)  # type: ignore
    model_params_layout.setFieldGrowthPolicy(
        QFormLayout.AllNonFixedFieldsGrow  # type: ignore
    )
    model_params_layout.addRow("Depth", self.model_depth_spin)
    model_params_layout.addRow("# Filters", self.num_conv_filters_spin)
    model_grpbox.setLayout(model_params_layout)
    layout.addWidget(model_grpbox)
    layout.addSpacing(10)

    layout.addWidget(self.indi_channels_chkbox)

    tab_widget.setLayout(layout)
    self.tabs.addTab(tab_widget, "Common")

bind_properties() #

Create and bind class properties to the UI elements.

Source code in src/careamics_napari/widgets/advanced_config.py
def bind_properties(self) -> None:
    """Create and bind class properties to the UI elements."""
    # type(self) returns the class of the instance, so we are adding
    # properties to the class itself, not the instance.
    type(self).experiment_name = bind(self.experiment_txtbox, "text")
    type(self).val_percentage = bind(self.val_perc_spin, "value")
    type(self).val_split = bind(self.val_split_spin, "value")
    type(self).x_flip = bind(self.x_flip_chkbox, "checked")
    type(self).y_flip = bind(self.y_flip_chkbox, "checked")
    type(self).rotation = bind(self.rotations_chkbox, "checked")
    type(self).model_depth = bind(self.model_depth_spin, "value")
    type(self).num_conv_filters = bind(self.num_conv_filters_spin, "value")
    type(self).indi_channels = bind(self.indi_channels_chkbox, "checked")

save() #

Save and close the window.

Source code in src/careamics_napari/widgets/advanced_config.py
def save(self) -> None:
    """Save and close the window."""
    self.update_config()

update_config() #

Update the configuration object from UI elements.

Source code in src/careamics_napari/widgets/advanced_config.py
def update_config(self) -> None:
    """Update the configuration object from UI elements."""
    self.configuration.experiment_name = self.experiment_name

    self.configuration.val_percentage = self.val_percentage
    self.configuration.val_minimum_split = self.val_split

    if isinstance(self.configuration.algorithm_config.model, UNetModel):
        self.configuration.algorithm_config.model.depth = self.model_depth
        self.configuration.algorithm_config.model.num_channels_init = (
            self.num_conv_filters
        )

        self.configuration.algorithm_config.model.independent_channels = (
            self.indi_channels
        )

    # update augmentations
    augs: list[Union[XYFlipModel, XYRandomRotate90Model]] = []
    if self.x_flip or self.y_flip:
        augs.append(XYFlipModel(flip_x=self.x_flip, flip_y=self.y_flip, p=0.5))
    if self.rotation:
        augs.append(XYRandomRotate90Model(p=0.5))
    self.configuration.data_config.transforms = augs  # type: ignore
    # update advanced config as well
    if self.advanced_configuration is not None:
        self.advanced_configuration.x_flip = self.x_flip
        self.advanced_configuration.y_flip = self.y_flip
        self.advanced_configuration.rotations = self.rotation