Example #1
0
        def measurement_visibles(m_g):
            if hasattr(m_g, "remover"):
                result = [Divider(line=True)]
            else:
                result = []
            result += [m_g.source_choice]

            if (m_g.source_choice == S_ALL_OBJECTS
                    or m_g.source_choice == S_AVERAGE_OBJECT):
                result += [m_g.object_name]
            if m_g.source_choice == S_RULES or m_g.source_choice == S_CLASSIFIER:
                result += [
                    m_g.rules_directory, m_g.rules_file_name, m_g.rules_class
                ]
                whatami = "Rules" if m_g.source_choice == S_RULES else "Classifier"
                for setting, s in (
                    (m_g.rules_directory, "%s file location"),
                    (m_g.rules_file_name, "%s file name"),
                ):
                    setting.text = s % whatami
            else:
                result += [m_g.measurement, m_g.wants_minimum]
                if m_g.wants_minimum.value:
                    result += [m_g.minimum_value]
                result += [m_g.wants_maximum]
                if m_g.wants_maximum.value:
                    result += [m_g.maximum_value]
            if hasattr(m_g, "remover"):
                result += [m_g.remover, Divider(line=True)]
            return result
Example #2
0
    def visible_settings(self):
        visible_settings = []
        global imagej_process

        # Update the visible settings based on the selected initialization method
        # If ImageJ is already initialized we just want to report how it was initialized
        # Otherwise we show: a string entry for "endpoint", a directory chooser for "local" (and file chooser if on mac),
        # and nothing if "latest"
        if not imagej_process:
            visible_settings += [self.init_choice]
            input_type = self.init_choice.get_value()
            # ImageJ is not initialized yet
            if input_type == INIT_ENDPOINT:
                visible_settings += [self.endpoint_string]
            elif input_type == INIT_LOCAL:
                visible_settings += [self.app_directory]
                if platform == 'darwin':
                    visible_settings += [self.app_file]
        else:
            # ImageJ is initialized
            visible_settings += [self.initialized_method]
        visible_settings += [Divider(line=True)]
        visible_settings += [self.script_directory, self.script_file, self.get_parameters_button, self.convert_types]
        if len(self.script_parameter_list) > 0:
            visible_settings += [Divider(line=True)]
        for script_parameter in self.script_parameter_list:
            visible_settings += [script_parameter.setting]
            visible_settings += [script_parameter.remover]

        return visible_settings
    def add_image_in(self, can_delete=True):
        """Add an image to the image_groups collection
        can_delete - set this to False to keep from showing the "remove"
                     button for images that must be present.
        """
        group = SettingsGroup()
        if can_delete:
            group.append("divider", Divider(line=False))
        group.append(
            "image_name",
            ImageSubscriber('Select an image to send to your macro',
                            "None",
                            doc="Select an image to send to your macro. "))
        group.append(
            "output_filename",
            Text(
                "What should this image temporarily saved as?",
                "None.tiff",
                doc=
                'Enter the filename of the image to be used by the macro. This should be set to the name expected '
                'by the macro file.'),
        )
        if len(self.image_groups_in
               ) == 0:  # Insert space between 1st two images for aesthetics
            group.append("extra_divider", Divider(line=False))

        if can_delete:
            group.append(
                "remover",
                RemoveSettingButton("", "Remove this image",
                                    self.image_groups_in, group))

        self.image_groups_in.append(group)
    def add_macro_variables(self, can_delete=True):
        group = SettingsGroup()
        if can_delete:
            group.append("divider", Divider(line=False))
        group.append(
            "variable_name",
            Text('What variable name is your macro expecting?',
                 "None",
                 doc='Enter the variable name that your macro is expecting. '))
        group.append(
            "variable_value",
            Text("What value should this variable have?",
                 "None",
                 doc="Enter the desire value for this variable."),
        )
        if len(self.macro_variables_list
               ) == 0:  # Insert space between 1st two images for aesthetics
            group.append("extra_divider", Divider(line=False))

        if can_delete:
            group.append(
                "remover",
                RemoveSettingButton("", "Remove this variable",
                                    self.macro_variables_list, group))

        self.macro_variables_list.append(group)
Example #5
0
    def create_settings(self):
        """Create the settings & name the module"""
        self.images_list = ImageListSubscriber(
            "Select images to measure",
            [],
            doc=
            """Select the grayscale images whose intensity you want to measure.""",
        )

        self.divider = Divider(line=False)
        self.wants_objects = Binary(
            "Measure the intensity only from areas enclosed by objects?",
            False,
            doc="""\
        Select *Yes* to measure only those pixels within an object type you
        choose, identified by a prior module. Note that this module will
        aggregate intensities across all objects in the image: to measure each
        object individually, see **MeasureObjectIntensity** instead.
        """,
        )

        self.objects_list = LabelListSubscriber(
            "Select input object sets",
            [],
            doc=
            """Select the object sets whose intensity you want to measure.""",
        )
Example #6
0
    def create_settings(self):
        self.operand_choice = Choice(
            "Measure the area occupied by",
            [O_BINARY_IMAGE, O_OBJECTS, O_BOTH],
            doc="""\
Area occupied can be measured in two ways:

-  *{O_BINARY_IMAGE}:* The area occupied by the foreground in a binary (black and white) image.
-  *{O_OBJECTS}:* The area occupied by previously-identified objects.
                    """.format(**{
                "O_BINARY_IMAGE": O_BINARY_IMAGE,
                "O_OBJECTS": O_OBJECTS
            }),
        )

        self.divider = Divider()

        self.images_list = ImageListSubscriber(
            "Select binary images to measure",
            [],
            doc="""*(Used only if ‘{O_BINARY_IMAGE}’ is to be measured)*

These should be binary images created earlier in the pipeline, where you would
like to measure the area occupied by the foreground in the image.
                    """.format(**{"O_BINARY_IMAGE": O_BINARY_IMAGE}),
        )

        self.objects_list = LabelListSubscriber(
            "Select object sets to measure",
            [],
            doc="""*(Used only if ‘{O_OBJECTS}’ are to be measured)*

Select the previously identified objects you would like to measure.""".format(
                **{"O_OBJECTS": O_OBJECTS}),
        )
Example #7
0
    def create_settings(self):
        super(FindMaxima, self).create_settings()

        self.min_distance = Integer(
            text="Minimum distance between maxima",
            value=5,
            minval=0,
            doc="""Choose the minimum distance between accepted local maxima"""
        )

        self.exclude_mode = Choice("Method for excluding background",
                                   [MODE_THRESHOLD, MODE_MASK, MODE_OBJECTS],
                                   value="Threshold",
                                   doc=f"""\
By default, local maxima will be searched for across the whole image. This means that maxima will be found in 
areas that consist entirely of background. To resolve this we have several methods to exclude background.

**{MODE_THRESHOLD}** allows you to specify a minimum pixel intensity to be considered as a peak. Setting this to 0
effectively uses no threshold.

**{MODE_MASK}** will restrict peaks to areas which are within a provided mask image. This mask will typically come from 
the threshold module or another means of finding background.

**{MODE_OBJECTS}** will restrict peaks to areas within an existing set of objects.
""")

        self.min_intensity = Float("Specify the minimum intensity of a peak",
                                   0,
                                   minval=0,
                                   maxval=99,
                                   doc="""\
Intensity peaks below this threshold value will be excluded. Use this to ensure that your local 
maxima are within objects of interest.""")

        self.mask_image = ImageSubscriber(
            "Select the image to use as a mask",
            doc=
            "Select the image you want to use. This should be a binary image.")

        self.mask_objects = LabelSubscriber(
            "Select the objects to search within",
            doc="Select the objects within which to search for peaks.")

        self.maxima_color = Color(
            "Select maxima preview color",
            "Blue",
            doc="Maxima will be displayed in this color.",
        )

        self.maxima_size = Integer(
            "Select maxima preview size",
            value=1,
            minval=1,
            doc=
            "Size of the markers for each maxima in the preview. Positive pixels will be"
            "expanded by this radius."
            "You may want to increase this when working with large images.",
        )

        self.spacer = Divider(line=True)
Example #8
0
    def add_image(self, can_remove=True):
        group = SettingsGroup()

        if can_remove:
            group.append("divider", Divider(line=False))

        group.append(
            "input_image_name",
            ImageSubscriber(
                "Select the additional image?",
                "None",
                doc="""\
What is the name of the additional image to resize? This image will be
resized with the same settings as the first image.""",
            ),
        )

        group.append(
            "output_image_name",
            ImageName(
                "Name the output image",
                "ResizedBlue",
                doc="What is the name of the additional resized image?",
            ),
        )

        if can_remove:
            group.append(
                "remover",
                RemoveSettingButton(
                    "", "Remove above image", self.additional_images, group
                ),
            )

        self.additional_images.append(group)
Example #9
0
 def flag_visibles(flag):
     if hasattr(flag, "remover"):
         result = [Divider(line=True), Divider(line=True)]
     else:
         result = []
     result += [flag.category, flag.feature_name, flag.wants_skip]
     if len(flag.measurement_settings) > 1:
         result += [flag.combination_choice]
     for measurement_settings in flag.measurement_settings:
         result += measurement_visibles(measurement_settings)
     result += [flag.add_measurement_button]
     if hasattr(flag, "remover"):
         result += [
             flag.remover,
             Divider(line=True),
             Divider(line=True)
         ]
     return result
Example #10
0
    def settings(self):
        result = [self.script_parameter_count, self.init_choice, self.app_directory, self.app_file, self.endpoint_string, self.script_directory, self.script_file, self.get_parameters_button, self.convert_types]
        if len(self.script_parameter_list) > 0:
            result += [Divider(line=True)]
        for script_parameter_group in self.script_parameter_list:
            result += [script_parameter_group.setting]
            result += [script_parameter_group.remover]
            result += [script_parameter_group.name]
            result += [script_parameter_group.type]
            result += [script_parameter_group.io_class]

        return result
    def add_image_out(self, can_delete=True):
        """Add an image to the image_groups collection
        can_delete - set this to False to keep from showing the "remove"
                     button for images that must be present.
        """
        group = SettingsGroup()
        if can_delete:
            group.append("divider", Divider(line=False))
        group.append(
            "input_filename",
            Text(
                "What is the image filename CellProfiler should load?",
                "None.tiff",
                doc=
                "Enter the image filename CellProfiler should load. This should be set to the output filename "
                "written in the macro file. The image written by the macro will be saved in a temporary directory "
                "and read by CellProfiler."),
        )

        group.append(
            "image_name",
            ImageName(
                r'What should CellProfiler call the loaded image?',
                "None",
                doc=
                'Enter a name to assign to the new image loaded by CellProfiler. This image will be added to your '
                'workspace. '))

        if len(self.image_groups_out
               ) == 0:  # Insert space between 1st two images for aesthetics
            group.append("extra_divider", Divider(line=False))

        if can_delete:
            group.append(
                "remover",
                RemoveSettingButton("", "Remove this image",
                                    self.image_groups_out, group))

        self.image_groups_out.append(group)
 def create_settings(self):
     self.images_list = ImageListSubscriber(
         "Select images to measure",
         [],
         doc=
         """Select the grayscale images whose intensity you want to measure.""",
     )
     self.divider = Divider()
     self.objects_list = LabelListSubscriber(
         "Select objects to measure",
         [],
         doc=
         """Select the object sets whose intensity you want to measure.""",
     )
Example #13
0
    def create_settings(self):
        self.flags = []
        self.flag_count = HiddenCount(self.flags)
        self.add_flag_button = DoSomething("", "Add another flag",
                                           self.add_flag)
        self.spacer_1 = Divider()
        self.add_flag(can_delete=False)
        self.ignore_flag_on_last = Binary(
            "Ignore flag skips on last cycle?",
            False,
            doc="""\
When set to *{YES}*, this option allows you to bypass skipping on the last
cycle of an image group.  This behavior is usually not desired, but may be 
useful when using SaveImages 'Save on last cycle' option for an image made
by any other module than MakeProjection, CorrectIlluminationCalculate, and Tile.
""".format(**{"YES": "Yes"}),
        )
Example #14
0
    def create_settings(self):
        """Create the settings & name the module"""
        self.images_list = ImageListSubscriber(
            "Select images to measure",
            [],
            doc="""Select the grayscale images whose intensity you want to measure.""",
        )

        self.divider = Divider(line=False)
        self.wants_objects = Binary(
            "Measure the intensity only from areas enclosed by objects?",
            False,
            doc="""\
        Select *Yes* to measure only those pixels within an object type you
        choose, identified by a prior module. Note that this module will
        aggregate intensities across all objects in the image: to measure each
        object individually, see **MeasureObjectIntensity** instead.
        """,
        )

        self.objects_list = LabelListSubscriber(
            "Select input object sets",
            [],
            doc="""Select the object sets whose intensity you want to measure.""",
        )

        self.wants_percentiles = Binary(
            text="Calculate custom percentiles",
            value=False,
            doc="""Choose whether to enable measurement of custom percentiles.
            
            Note that the Upper and Lower Quartile measurements are automatically calculated by this module,
            representing the 25th and 75th percentiles.
            """,
        )

        self.percentiles = Text(
            text="Specify percentiles to measure",
            value="10,90",
            doc="""Specify the percentiles to measure. Values should range from 0-100 inclusive and be whole integers.
            Multiple values can be specified by seperating them with a comma,
            eg. "10,90" will measure the 10th and 90th percentiles.
            """,
        )
Example #15
0
    def add_image(self, can_remove=True):
        """Add an image + associated questions and buttons"""
        group = SettingsGroup()
        if can_remove:
            group.append("divider", Divider(line=True))

        group.append(
            "input_image_name",
            ImageSubscriber(
                "Select an additional image to tile",
                "None",
                doc="""Select an additional image to tile?""",
            ),
        )
        if can_remove:
            group.append(
                "remover",
                RemoveSettingButton("", "Remove above image",
                                    self.additional_images, group),
            )
        self.additional_images.append(group)
Example #16
0
    def create_settings(self):
        """Create the settings for the module at startup and set the module name

        The module allows for an unlimited number of measured objects, each
        of which has an entry in self.object_groups.
        """
        self.objects_list = LabelListSubscriber(
            "Select object sets to measure",
            [],
            doc=
            """Select the object sets whose size and shape you want to measure.""",
        )
        self.spacer = Divider(line=True)

        self.calculate_advanced = Binary(
            text="Calculate the advanced features?",
            value=False,
            doc="""\
Select *{YES}* to calculate additional statistics for object moments
and intertia tensors in **2D mode**. These features should not require much additional time
to calculate, but do add many additional columns to the resulting output 
files.

In **3D mode** this setting enables the Solidity measurement, which can be time-consuming
to calculate.""".format(**{"YES": "Yes"}),
        )

        self.calculate_zernikes = Binary(
            text="Calculate the Zernike features?",
            value=True,
            doc="""\
Select *{YES}* to calculate the Zernike shape features. Because the
first 10 Zernike polynomials (from order 0 to order 9) are calculated,
this operation can be time consuming if the image contains a lot of
objects. Select *{NO}* if you are measuring 3D objects with this
module.""".format(**{
                "YES": "Yes",
                "NO": "No"
            }),
        )
Example #17
0
    def add_scale(self, removable=True):
        """

        Add a scale to the scale_groups collection

        :param removable: set this to False to keep from showing the "remove" button for scales that must be present.

        """
        group = SettingsGroup()

        if removable:
            group.append("divider", Divider(line=False))

        scale = Integer(
            doc="""\
You can specify the scale of texture to be measured, in pixel units; the
texture scale is the distance between correlated intensities in the
image. A higher number for the scale of texture measures larger patterns
of texture whereas smaller numbers measure more localized patterns of
texture. It is best to measure texture on a scale smaller than your
objects’ sizes, so be sure that the value entered for scale of texture
is smaller than most of your objects. For very small objects (smaller
than the scale of texture you are measuring), the texture cannot be
measured and will result in a undefined value in the output file.
""",
            text="Texture scale to measure",
            value=len(self.scale_groups) + 3,
        )

        group.append("scale", scale)

        if removable:
            remove_setting = RemoveSettingButton(entry=group,
                                                 label="Remove this scale",
                                                 list=self.scale_groups,
                                                 text="")

            group.append("remover", remove_setting)

        self.scale_groups.append(group)
Example #18
0
    def add_additional_object(self):
        group = SettingsGroup()

        group.append(
            "object_name",
            LabelSubscriber("Select additional object to relabel", "None"),
        )

        group.append(
            "target_name", LabelName("Name the relabeled objects", "FilteredGreen"),
        )

        group.append(
            "remover",
            RemoveSettingButton(
                "", "Remove this additional object", self.additional_objects, group
            ),
        )

        group.append("divider", Divider(line=False))

        self.additional_objects.append(group)
    def create_settings(self):
        """Create the settings & name the module"""
        self.images_list = ImageListSubscriber(
            "Select images to measure",
            [],
            doc=
            """Select the grayscale images whose intensity you want to measure.""",
        )

        self.divider = Divider(line=False)
        self.wants_objects = Binary(
            "Measure the intensity only from areas enclosed by objects?",
            False,
            doc="""\
        Select *Yes* to measure only those pixels within an object type you
        choose, identified by a prior module. Note that this module will
        aggregate intensities across all objects in the image: to measure each
        object individually, see **MeasureObjectIntensity** instead.
        """,
        )

        self.objects_list = LabelListSubscriber(
            "Select input object sets",
            [],
            doc=
            """Select the object sets whose intensity you want to measure.""",
        )

        self.nchannels = Integer(
            "How many channels does the image have?",
            1,
            doc="""
            Indicate how many planes this image have. This is needed as
            the cellprofiler pipeline needs to be independent of the actuall
            image data.
            """,
        )
Example #20
0
    def add_outline(self, can_remove=True):
        group = SettingsGroup()
        if can_remove:
            group.append("divider", Divider(line=False))

        group.append(
            "objects_name",
            LabelSubscriber(
                "Select objects to display",
                "None",
                doc="Choose the objects whose outlines you would like to display.",
            ),
        )

        default_color = (
            COLOR_ORDER[len(self.outlines)]
            if len(self.outlines) < len(COLOR_ORDER)
            else COLOR_ORDER[0]
        )

        group.append(
            "color",
            Color(
                "Select outline color",
                default_color,
                doc="Objects will be outlined in this color.",
            ),
        )

        if can_remove:
            group.append(
                "remover",
                RemoveSettingButton("", "Remove this outline", self.outlines, group),
            )

        self.outlines.append(group)
Example #21
0
    def create_settings(self):
        self.images_list = ImageListSubscriber(
            "Select images to measure",
            [],
            doc=
            """Select the grayscale images whose intensity you want to measure.""",
        )
        self.divider = Divider()
        self.objects_list = LabelListSubscriber(
            "Select objects to measure",
            [],
            doc=
            """Select the object sets whose intensity you want to measure.""",
        )

        self.nchannels = Integer(
            "How many channels does the image have?",
            1,
            doc="""
            Indicate how many planes this image have. This is needed as
            the cellprofiler pipeline needs to be independent of the actuall
            image data.
            """,
        )
Example #22
0
    def create_settings(self):
        # XXX needs to use cps.SettingsGroup
        class Operand(object):
            """Represents the collection of settings needed by each operand"""
            def __init__(self, index, operation):
                self.__index = index
                self.__operation = operation
                self.__operand_choice = Choice(
                    self.operand_choice_text(),
                    MC_ALL,
                    doc=
                    """Indicate whether the operand is an image or object measurement.""",
                )

                self.__operand_objects = LabelName(
                    self.operand_objects_text(),
                    "None",
                    doc=
                    """Choose the objects you want to measure for this operation.""",
                )

                self.__operand_measurement = Measurement(
                    self.operand_measurement_text(),
                    self.object_fn,
                    doc="""\
Enter the category that was used to create the measurement. You
will be prompted to add additional information depending on
the type of measurement that is requested.""",
                )

                self.__multiplicand = Float(
                    "Multiply the above operand by",
                    1,
                    doc=
                    """Enter the number by which you would like to multiply the above operand.""",
                )

                self.__exponent = Float(
                    "Raise the power of above operand by",
                    1,
                    doc=
                    """Enter the power by which you would like to raise the above operand.""",
                )

            @property
            def operand_choice(self):
                """Either MC_IMAGE for image measurements or MC_OBJECT for object"""
                return self.__operand_choice

            @property
            def operand_objects(self):
                """Get measurements from these objects"""
                return self.__operand_objects

            @property
            def operand_measurement(self):
                """The measurement providing the value of the operand"""
                return self.__operand_measurement

            @property
            def multiplicand(self):
                """Premultiply the measurement by this value"""
                return self.__multiplicand

            @property
            def exponent(self):
                """Raise the measurement to this power"""
                return self.__exponent

            @property
            def object(self):
                """The name of the object for measurement or "Image\""""
                if self.operand_choice == MC_IMAGE:
                    return IMAGE
                else:
                    return self.operand_objects.value

            def object_fn(self):
                if self.__operand_choice == MC_IMAGE:
                    return IMAGE
                elif self.__operand_choice == MC_OBJECT:
                    return self.__operand_objects.value
                else:
                    raise NotImplementedError(
                        "Measurement type %s is not supported" %
                        self.__operand_choice.value)

            def operand_name(self):
                """A fancy name based on what operation is being performed"""
                if self.__index == 0:
                    return ("first operand" if self.__operation
                            in (O_ADD, O_MULTIPLY) else "minuend"
                            if self.__operation == O_SUBTRACT else "numerator")
                elif self.__index == 1:
                    return ("second operand" if self.__operation
                            in (O_ADD, O_MULTIPLY) else "subtrahend" if
                            self.__operation == O_SUBTRACT else "denominator")

            def operand_choice_text(self):
                return self.operand_text("Select the %s measurement type")

            def operand_objects_text(self):
                return self.operand_text("Select the %s objects")

            def operand_text(self, format):
                return format % self.operand_name()

            def operand_measurement_text(self):
                return self.operand_text("Select the %s measurement")

            def settings(self):
                """The operand settings to be saved in the output file"""
                return [
                    self.operand_choice,
                    self.operand_objects,
                    self.operand_measurement,
                    self.multiplicand,
                    self.exponent,
                ]

            def visible_settings(self):
                """The operand settings to be displayed"""
                self.operand_choice.text = self.operand_choice_text()
                self.operand_objects.text = self.operand_objects_text()
                self.operand_measurement.text = self.operand_measurement_text()
                result = [self.operand_choice]
                result += ([self.operand_objects]
                           if self.operand_choice == MC_OBJECT else [])
                result += [
                    self.operand_measurement, self.multiplicand, self.exponent
                ]
                return result

        self.output_feature_name = Alphanumeric(
            "Name the output measurement",
            "Measurement",
            doc=
            """Enter a name for the measurement calculated by this module.""",
        )

        self.operation = Choice(
            "Operation",
            O_ALL,
            doc="""\
Choose the arithmetic operation you would like to perform. *None* is
useful if you simply want to select some of the later options in the
module, such as multiplying or exponentiating your image by a constant.
""",
        )

        self.operands = (Operand(0,
                                 self.operation), Operand(1, self.operation))

        self.spacer_1 = Divider(line=True)

        self.spacer_2 = Divider(line=True)

        self.spacer_3 = Divider(line=True)

        self.wants_log = Binary(
            "Take log10 of result?",
            False,
            doc="""Select *Yes* if you want the log (base 10) of the result."""
            % globals(),
        )

        self.final_multiplicand = Float(
            "Multiply the result by",
            1,
            doc="""\
*(Used only for operations other than "None")*

Enter the number by which you would like to multiply the result.
""",
        )

        self.final_exponent = Float(
            "Raise the power of result by",
            1,
            doc="""\
*(Used only for operations other than "None")*

Enter the power by which you would like to raise the result.
""",
        )

        self.final_addend = Float(
            "Add to the result",
            0,
            doc="""Enter the number you would like to add to the result.""",
        )

        self.constrain_lower_bound = Binary(
            "Constrain the result to a lower bound?",
            False,
            doc=
            """Select *Yes* if you want the result to be constrained to a lower bound."""
            % globals(),
        )

        self.lower_bound = Float(
            "Enter the lower bound",
            0,
            doc="""Enter the lower bound of the result here.""",
        )

        self.constrain_upper_bound = Binary(
            "Constrain the result to an upper bound?",
            False,
            doc=
            """Select *Yes* if you want the result to be constrained to an upper bound."""
            % globals(),
        )

        self.upper_bound = Float(
            "Enter the upper bound",
            1,
            doc="""Enter the upper bound of the result here.""",
        )

        self.rounding = Choice(
            "How should the output value be rounded?",
            ROUNDING,
            doc="""\
Choose how the values should be rounded- not at all, to a specified number of decimal places, 
to the next lowest integer ("floor rounding"), or to the next highest integer ("ceiling rounding").
Note that for rounding to an arbitrary number of decimal places, Python uses "round to even" rounding,
such that ties round to the nearest even number. Thus, 1.5 and 2.5 both round to to 2 at 0 decimal 
places, 2.45 rounds to 2.4, 2.451 rounds to 2.5, and 2.55 rounds to 2.6 at 1 decimal place. See the 
numpy documentation for more information.  
""",
        )

        self.rounding_digit = Integer(
            "Enter how many decimal places the value should be rounded to",
            0,
            doc="""\
Enter how many decimal places the value should be rounded to. 0 will round to an integer (e.g. 1, 2), 1 to 
one decimal place (e.g. 0.1, 0.2), -1 to one value before the decimal place (e.g. 10, 20), etc.
""",
        )
    def add_image(self, can_delete=True):
        """Add an image and its settings to the list of images"""
        image_name = ImageSubscriber(
            "Select the input image", "None", doc="Select the image to be corrected."
        )

        corrected_image_name = ImageName(
            "Name the output image",
            "CorrBlue",
            doc="Enter a name for the corrected image.",
        )

        illum_correct_function_image_name = ImageSubscriber(
            "Select the illumination function",
            "None",
            doc="""\
Select the illumination correction function image that will be used to
carry out the correction. This image is usually produced by another
module or loaded as a .mat or .npy format image using the **Images** module
or a **LoadData** module.

Note that loading .mat format images is deprecated and will be removed in
a future version of CellProfiler. You can export .mat format images as
.npy format images using **SaveImages** to ensure future compatibility.
""",
        )

        divide_or_subtract = Choice(
            "Select how the illumination function is applied",
            [DOS_DIVIDE, DOS_SUBTRACT],
            doc="""\
This choice depends on how the illumination function was calculated and
on your physical model of the way illumination variation affects the
background of images relative to the objects in images; it is also
somewhat empirical.

-  *%(DOS_SUBTRACT)s:* Use this option if the background signal is
   significant relative to the real signal coming from the cells. If you
   created the illumination correction function using
   *Background*, then you will want to choose
   *%(DOS_SUBTRACT)s* here.
-  *%(DOS_DIVIDE)s:* Choose this option if the signal to background
   ratio is high (the cells are stained very strongly). If you created
   the illumination correction function using *Regular*, then
   you will want to choose *%(DOS_DIVIDE)s* here.
"""
            % globals(),
        )

        image_settings = SettingsGroup()
        image_settings.append("image_name", image_name)
        image_settings.append("corrected_image_name", corrected_image_name)
        image_settings.append(
            "illum_correct_function_image_name", illum_correct_function_image_name
        )
        image_settings.append("divide_or_subtract", divide_or_subtract)
        image_settings.append("rescale_option", RE_NONE)

        if can_delete:
            image_settings.append(
                "remover",
                RemoveSettingButton(
                    "", "Remove this image", self.images, image_settings
                ),
            )
        image_settings.append("divider", Divider())
        self.images.append(image_settings)
Example #24
0
    def create_settings(self):
        self.images_list = ImageListSubscriber(
            "Select images to measure",
            [],
            doc=
            """Select the grayscale images whose intensity you want to measure.""",
        )

        self.objects_list = LabelListSubscriber(
            "Select objects to measure",
            [],
            doc="""\
        Select the objects whose texture you want to measure. If you only want
        to measure the texture for the image overall, you can remove all objects
        using the “Remove this object” button.

        Objects specified here will have their texture measured against *all*
        images specified above, which may lead to image-object combinations that
        are unnecessary. If you do not want this behavior, use multiple
        **MeasureTexture** modules to specify the particular image-object
        measures that you want.
        """,
        )

        self.gray_levels = Integer(
            "Enter how many gray levels to measure the texture at",
            256,
            2,
            256,
            doc="""\
        Enter the number of gray levels (ie, total possible values of intensity) 
        you want to measure texture at.  Measuring at more levels gives you 
        _potentially_ more detailed information about your image, but at the cost
        of somewhat decreased processing speed.  

        Before processing, your image will be rescaled from its current pixel values
        to 0 - [gray levels - 1]. The texture features will then be calculated. 

        In all CellProfiler 2 versions, this value was fixed at 8; in all 
        CellProfiler 3 versions it was fixed at 256.  The minimum number of levels is
        2, the maximum is 256.
        """,
        )

        self.scale_groups = []

        self.scale_count = HiddenCount(self.scale_groups)

        self.image_divider = Divider()

        self.object_divider = Divider()

        self.add_scale(removable=False)

        self.add_scales = DoSomething(
            callback=self.add_scale,
            label="Add another scale",
            text="",
            doc="""\
            Add an additional texture scale to measure. Useful when you
            want to measure texture features of different sizes.
            """,
        )

        self.images_or_objects = Choice(
            "Measure whole images or objects?",
            [IO_IMAGES, IO_OBJECTS, IO_BOTH],
            value=IO_BOTH,
            doc="""\
This setting determines whether the module computes image-wide
measurements, per-object measurements or both.

-  *{IO_IMAGES}:* Select if you only want to measure the texture
   across entire images.
-  *{IO_OBJECTS}:* Select if you want to measure the texture
   on a per-object basis only.
-  *{IO_BOTH}:* Select to make both image and object measurements.
""".format(
                **{
                    "IO_IMAGES": IO_IMAGES,
                    "IO_OBJECTS": IO_OBJECTS,
                    "IO_BOTH": IO_BOTH
                }),
        )
Example #25
0
    def create_settings(self):
        self.blank_image = Binary(
            "Display outlines on a blank image?",
            False,
            doc="""\
Select "*{YES}*" to produce an image of the outlines on a black background.

Select "*{NO}*" to overlay the outlines on an image you choose.
""".format(
                **{"YES": "Yes", "NO": "No"}
            ),
        )

        self.image_name = ImageSubscriber(
            "Select image on which to display outlines",
            "None",
            doc="""\
*(Used only when a blank image has not been selected)*

Choose the image to serve as the background for the outlines. You can
choose from images that were loaded or created by modules previous to
this one.
""",
        )

        self.line_mode = Choice(
            "How to outline",
            ["Inner", "Outer", "Thick"],
            value="Inner",
            doc="""\
Specify how to mark the boundaries around an object:

-  *Inner:* outline the pixels just inside of objects, leaving
   background pixels untouched.
-  *Outer:* outline pixels in the background around object boundaries.
   When two objects touch, their boundary is also marked.
-  *Thick:* any pixel not completely surrounded by pixels of the same
   label is marked as a boundary. This results in boundaries that are 2
   pixels thick.
""",
        )

        self.output_image_name = ImageName(
            "Name the output image",
            "OrigOverlay",
            doc="""\
Enter the name of the output image with the outlines overlaid. This
image can be selected in later modules (for instance, **SaveImages**).
""",
        )

        self.wants_color = Choice(
            "Outline display mode",
            [WANTS_COLOR, WANTS_GRAYSCALE],
            doc="""\
Specify how to display the outline contours around your objects. Color
outlines produce a clearer display for images where the cell borders
have a high intensity, but take up more space in memory. Grayscale
outlines are displayed with either the highest possible intensity or the
same intensity as the brightest pixel in the image.
""",
        )

        self.spacer = Divider(line=False)

        self.max_type = Choice(
            "Select method to determine brightness of outlines",
            [MAX_IMAGE, MAX_POSSIBLE],
            doc="""\
*(Used only when outline display mode is grayscale)*

The following options are possible for setting the intensity
(brightness) of the outlines:

-  *{MAX_IMAGE}:* Set the brightness to the the same as the brightest
   point in the image.
-  *{MAX_POSSIBLE}:* Set to the maximum possible value for this image
   format.

If your image is quite dim, then putting bright white lines onto it may
not be useful. It may be preferable to make the outlines equal to the
maximal brightness already occurring in the image.
""".format(
                **{"MAX_IMAGE": MAX_IMAGE, "MAX_POSSIBLE": MAX_POSSIBLE}
            ),
        )

        self.outlines = []

        self.add_outline(can_remove=False)

        self.add_outline_button = DoSomething(
            "", "Add another outline", self.add_outline
        )
Example #26
0
    def add_flag(self, can_delete=True):
        group = SettingsGroup()
        group.append("divider1", Divider(line=False))
        group.append("measurement_settings", [])
        group.append("measurement_count",
                     HiddenCount(group.measurement_settings))
        group.append(
            "category",
            Text(
                "Name the flag's category",
                "Metadata",
                doc="""\
Name a measurement category by which to categorize the flag. The
*Metadata* category is the default used in CellProfiler to store
information about images (referred to as *metadata*).

The flag is stored as a per-image measurement whose name is a
combination of the flag’s category and the flag name that you choose, separated by
underscores. For instance, if the measurement category is *Metadata* and
the flag name is *QCFlag*, then the default measurement name would be
*Metadata_QCFlag*.
""",
            ),
        )

        group.append(
            "feature_name",
            Text(
                "Name the flag",
                "QCFlag",
                doc="""\
The flag is stored as a per-image measurement whose name is a
combination of the flag’s category and the flag name that you choose, separated by
underscores. For instance, if the measurement category is *Metadata* and
the flag name is *QCFlag*, then the default measurement name would be
*Metadata_QCFlag*.
""",
            ),
        )

        group.append(
            "combination_choice",
            Choice(
                "How should measurements be linked?",
                [C_ANY, C_ALL],
                doc="""\
For combinations of measurements, you can set the criteria under which
an image set is flagged:

-  *%(C_ANY)s:* An image set will be flagged if any of its measurements
   fail. This can be useful for flagging images possessing multiple QC
   flaws; for example, you can flag all bright images and all out of
   focus images with one flag.
-  *%(C_ALL)s:* A flag will only be assigned if all measurements fail.
   This can be useful for flagging images that possess only a
   combination of QC flaws; for example, you can flag only images that
   are both bright and out of focus.
""" % globals(),
            ),
        )

        group.append(
            "wants_skip",
            Binary(
                "Skip image set if flagged?",
                False,
                doc="""\
Select *Yes* to skip the remainder of the pipeline for image sets
that are flagged. CellProfiler will not run subsequent modules in the
pipeline on the images for any image set that is flagged. Select *No*
for CellProfiler to continue to process the pipeline regardless of
flagging.

You may want to skip processing in order to filter out unwanted images.
For instance, you may want to exclude out of focus images when running
**CorrectIllumination_Calculate**. You can do this with a pipeline that
measures image quality and flags inappropriate images before it runs
**CorrectIllumination_Calculate**.
""" % globals(),
            ),
        )

        group.append(
            "add_measurement_button",
            DoSomething(
                "",
                "Add another measurement",
                self.add_measurement,
                group,
                doc="""Add another measurement as a criteria.""",
            ),
        )
        self.add_measurement(group, False if not can_delete else True)
        if can_delete:
            group.append(
                "remover",
                RemoveSettingButton("", "Remove this flag", self.flags, group),
            )
        group.append("divider2", Divider(line=True))
        self.flags.append(group)
Example #27
0
    def add_measurement(self, flag_settings, can_delete=True):
        measurement_settings = flag_settings.measurement_settings

        group = SettingsGroup()
        group.append("divider1", Divider(line=False))
        group.append(
            "source_choice",
            Choice(
                "Flag is based on",
                S_ALL,
                doc="""\
-  *%(S_IMAGE)s:* A per-image measurement, such as intensity or
   granularity.
-  *%(S_AVERAGE_OBJECT)s:* The average of all object measurements in
   the image.
-  *%(S_ALL_OBJECTS)s:* All the object measurements in an image,
   without averaging. In other words, if *any* of the objects meet the
   criteria, the image will be flagged.
-  *%(S_RULES)s:* Use a text file of rules produced by CellProfiler
   Analyst. With this option, you will have to ensure that this pipeline
   produces every measurement in the rules file upstream of this module.
-  *%(S_CLASSIFIER)s:* Use a classifier built by CellProfiler Analyst.
""" % globals(),
            ),
        )

        group.append(
            "object_name",
            LabelSubscriber(
                "Select the object to be used for flagging",
                "None",
                doc="""\
*(Used only when flag is based on an object measurement)*

Select the objects whose measurements you want to use for flagging.
""",
            ),
        )

        def object_fn():
            if group.source_choice == S_IMAGE:
                return IMAGE
            return group.object_name.value

        group.append(
            "rules_directory",
            Directory(
                "Rules file location",
                doc="""\
*(Used only when flagging using "{rules}")*

Select the location of the rules file that will be used for flagging images.
{folder_choice}
""".format(rules=S_RULES, folder_choice=IO_FOLDER_CHOICE_HELP_TEXT),
            ),
        )

        def get_directory_fn():
            """Get the directory for the rules file name"""
            return group.rules_directory.get_absolute_path()

        def set_directory_fn(path):
            dir_choice, custom_path = group.rules_directory.get_parts_from_path(
                path)
            group.rules_directory.join_parts(dir_choice, custom_path)

        group.append(
            "rules_file_name",
            Filename(
                "Rules file name",
                "rules.txt",
                get_directory_fn=get_directory_fn,
                set_directory_fn=set_directory_fn,
                doc="""\
*(Used only when flagging using "%(S_RULES)s")*

The name of the rules file, most commonly from CellProfiler Analyst's
Classifier. This file should be a plain text file
containing the complete set of rules.

Each line of this file should be a rule naming a measurement to be made
on an image, for instance:

    IF (Image_ImageQuality_PowerLogLogSlope_DNA < -2.5, [0.79, -0.79], [-0.94, 0.94])

The above rule will score +0.79 for the positive category and -0.94
for the negative category for images whose power log slope is less
than -2.5 pixels and will score the opposite for images whose slope is
larger. The filter adds positive and negative and flags the images
whose positive score is higher than the negative score.
""" % globals(),
            ),
        )

        def get_rules_class_choices(group=group):
            """Get the available choices from the rules file"""
            try:
                if group.source_choice == S_CLASSIFIER:
                    return self.get_bin_labels(group)
                elif group.source_choice == S_RULES:
                    rules = self.get_rules(group)
                    nclasses = len(rules.rules[0].weights[0])
                    return [str(i) for i in range(1, nclasses + 1)]
                else:
                    return ["None"]
                rules = self.get_rules(group)
                nclasses = len(rules.rules[0].weights[0])
                return [str(i) for i in range(1, nclasses + 1)]
            except:
                return [str(i) for i in range(1, 3)]

        group.append(
            "rules_class",
            MultiChoice(
                "Class number",
                choices=["1", "2"],
                doc="""\
*(Used only when flagging using "%(S_RULES)s")*

Select which classes to flag when filtering. The CellProfiler Analyst
Classifier user interface lists the names of the classes in order. By
default, these are the positive (class 1) and negative (class 2)
classes. **FlagImage** uses the first class from CellProfiler Analyst
if you choose “1”, etc.

Please note the following:

-  The flag is set if the image falls into the selected class.
-  You can make multiple class selections. If you do so, the module will
   set the flag if the image falls into any of the selected classes.
""" % globals(),
            ),
        )

        group.rules_class.get_choices = get_rules_class_choices

        group.append(
            "measurement",
            Measurement(
                "Which measurement?",
                object_fn,
                doc="""Choose the measurement to be used as criteria.""",
            ),
        )

        group.append(
            "wants_minimum",
            Binary(
                "Flag images based on low values?",
                True,
                doc="""\
Select *Yes* to flag images with measurements below the specified
cutoff. If the measurement evaluates to Not-A-Number (NaN), then the
image is not flagged.
""" % globals(),
            ),
        )

        group.append(
            "minimum_value",
            Float("Minimum value", 0, doc="""Set a value as a lower limit."""),
        )

        group.append(
            "wants_maximum",
            Binary(
                "Flag images based on high values?",
                True,
                doc="""\
Select *Yes* to flag images with measurements above the specified
cutoff. If the measurement evaluates to Not-A-Number (NaN), then the
image is not flagged.
""" % globals(),
            ),
        )

        group.append(
            "maximum_value",
            Float("Maximum value", 1,
                  doc="""Set a value as an upper limit."""),
        )

        if can_delete:
            group.append(
                "remover",
                RemoveSettingButton("", "Remove this measurement",
                                    measurement_settings, group),
            )

        group.append("divider2", Divider(line=True))
        measurement_settings.append(group)
Example #28
0
    def add_image(self, removable=True):
        # The text for these settings will be replaced in renumber_settings()
        group = SettingsGroup()
        group.removable = removable
        group.append(
            "image_or_measurement",
            Choice(
                "Image or measurement?",
                [IM_IMAGE, IM_MEASUREMENT],
                doc="""\
You can perform math operations using two images or you can use a
measurement for one of the operands. For instance, to divide the
intensity of one image by another, choose *%(IM_IMAGE)s* for both and
pick the respective images. To divide the intensity of an image by its
median intensity, use **MeasureImageIntensity** prior to this module to
calculate the median intensity, then select *%(IM_MEASUREMENT)s* and
use the median intensity measurement as the denominator.
""" % globals(),
            ),
        )

        group.append(
            "image_name",
            ImageSubscriber(
                "Select the image",
                "None",
                doc="""\
Select the image that you want to use for this operation.""",
            ),
        )

        group.append(
            "measurement",
            Measurement(
                "Measurement",
                lambda: "Image",
                "",
                doc="""\
Select a measurement made on the image. The value of the
measurement is used for the operand for all of the pixels of the
other operand's image.""",
            ),
        )

        group.append(
            "factor",
            Float(
                "Multiply the image by",
                1,
                doc="""\
Enter the number that you would like to multiply the above image by. This multiplication
is applied before other operations.""",
            ),
        )

        if removable:
            group.append(
                "remover",
                RemoveSettingButton("", "Remove this image", self.images,
                                    group),
            )

        group.append("divider", Divider())
        self.images.append(group)
Example #29
0
    def create_settings(self):
        # the list of per image settings (name & scaling factor)
        self.images = []
        # create the first two images (the default number)
        self.add_image(False)
        self.add_image(False)

        # other settings
        self.operation = Choice(
            "Operation",
            [
                O_ADD,
                O_SUBTRACT,
                O_DIFFERENCE,
                O_MULTIPLY,
                O_DIVIDE,
                O_AVERAGE,
                O_MINIMUM,
                O_MAXIMUM,
                O_INVERT,
                O_LOG_TRANSFORM,
                O_LOG_TRANSFORM_LEGACY,
                O_AND,
                O_OR,
                O_NOT,
                O_EQUALS,
                O_NONE,
            ],
            doc="""\
Select the operation to perform. Note that if more than two images are
chosen, then operations will be performed sequentially from first to
last, e.g., for “Divide”, (Image1 / Image2) / Image3

-  *%(O_ADD)s:* Adds the first image to the second, and so on.
-  *%(O_SUBTRACT)s:* Subtracts the second image from the first.
-  *%(O_DIFFERENCE)s:* The absolute value of the difference between the
   first and second images.
-  *%(O_MULTIPLY)s:* Multiplies the first image by the second.
-  *%(O_DIVIDE)s:* Divides the first image by the second.
-  *%(O_AVERAGE)s:* Calculates the mean intensity of the images loaded
   in the module. This is equivalent to the Add option divided by the
   number of images loaded by this module. If you would like to average
   all of the images in an entire pipeline, i.e., across cycles, you
   should instead use the **CorrectIlluminationCalculate** module and
   choose the *All* (vs. *Each*) option.
-  *%(O_MINIMUM)s:* Returns the element-wise minimum value at each
   pixel location.
-  *%(O_MAXIMUM)s:* Returns the element-wise maximum value at each
   pixel location.
-  *%(O_INVERT)s:* Subtracts the image intensities from 1. This makes
   the darkest color the brightest and vice-versa. Note that if a
   mask has been applied to the image, the mask will also be inverted.
-  *%(O_LOG_TRANSFORM)s:* Log transforms each pixel’s intensity. The
   actual function is log\ :sub:`2`\ (image + 1), transforming values
   from 0 to 1 into values from 0 to 1.
-  *%(O_LOG_TRANSFORM_LEGACY)s:* Log\ :sub:`2` transform for backwards
   compatibility.
-  *%(O_NONE)s:* This option is useful if you simply want to select some
   of the later options in the module, such as adding, multiplying, or
   exponentiating your image by a constant.

The following are operations that produce binary images. In a binary
image, the foreground has a truth value of “true” (ones) and the background has
a truth value of “false” (zeros). The operations, *%(O_OR)s, %(O_AND)s and
%(O_NOT)s* will convert the input images to binary by changing all zero
values to background (false) and all other values to foreground (true).

-  *%(O_AND)s:* a pixel in the output image is in the foreground only
   if all corresponding pixels in the input images are also in the
   foreground.
-  *%(O_OR)s:* a pixel in the output image is in the foreground if a
   corresponding pixel in any of the input images is also in the
   foreground.
-  *%(O_NOT)s:* the foreground of the input image becomes the
   background of the output image and vice-versa.
-  *%(O_EQUALS)s:* a pixel in the output image is in the foreground if
   the corresponding pixels in the input images have the same value.

Note that *%(O_INVERT)s*, *%(O_LOG_TRANSFORM)s*,
*%(O_LOG_TRANSFORM_LEGACY)s* and *%(O_NONE)s* operate on only a
single image.
""" % globals(),
        )
        self.divider_top = Divider(line=False)

        self.exponent = Float(
            "Raise the power of the result by",
            1,
            doc="""\
Enter an exponent to raise the result to *after* the chosen operation.""",
        )

        self.after_factor = Float(
            "Multiply the result by",
            1,
            doc="""\
Enter a factor to multiply the result by *after* the chosen operation.""",
        )

        self.addend = Float(
            "Add to result",
            0,
            doc="""\
Enter a number to add to the result *after* the chosen operation.""",
        )

        self.truncate_low = Binary(
            "Set values less than 0 equal to 0?",
            True,
            doc="""\
Values outside the range 0 to 1 might not be handled well by other
modules. Select *Yes* to set negative values to 0.
""" % globals(),
        )

        self.truncate_high = Binary(
            "Set values greater than 1 equal to 1?",
            True,
            doc="""\
Values outside the range 0 to 1 might not be handled well by other
modules. Select *Yes* to set values greater than 1 to a maximum
value of 1.
""" % globals(),
        )

        self.replace_nan = Binary(
            "Replace invalid values with 0?",
            True,
            doc="""\
        Certain operations are mathematically invalid (divide by zero, 
        raise a negative number to the power of a fraction, etc.).
        This setting will set pixels with invalid values to zero.
        Disabling this setting will represent these pixels as "nan" 
        ("Not A Number"). "nan" pixels cannot be displayed properly and 
        may cause errors in other modules.
        """ % globals(),
        )

        self.ignore_mask = Binary(
            "Ignore the image masks?",
            False,
            doc="""\
Select *Yes* to set equal to zero all previously masked pixels and
operate on the masked images as if no mask had been applied. Otherwise,
the smallest image mask is applied after image math has been completed.
""" % globals(),
        )

        self.output_image_name = ImageName(
            "Name the output image",
            "ImageAfterMath",
            doc="""\
Enter a name for the resulting image.""",
        )

        self.add_button = DoSomething("", "Add another image", self.add_image)

        self.divider_bottom = Divider(line=False)
Example #30
0
    def create_settings(self):
        super(Resize, self).create_settings()

        self.size_method = Choice(
            "Resizing method",
            R_ALL,
            doc="""\
The following options are available:

-  *Resize by a fraction or multiple of the original size:* Enter a single value which specifies the scaling.
-  *Resize by specifying desired final dimensions:* Enter the new height and width of the resized image, in units of pixels.""",
        )

        self.resizing_factor = Float(
            "Resizing factor",
            0.25,
            minval=0,
            doc="""\
*(Used only if resizing by a fraction or multiple of the original size)*

Numbers less than one (that is, fractions) will shrink the image;
numbers greater than one (that is, multiples) will enlarge the image.""",
        )

        self.use_manual_or_image = Choice(
            "Method to specify the dimensions",
            C_ALL,
            doc="""\
*(Used only if resizing by specifying the dimensions)*

You have two options on how to resize your image:

-  *{C_MANUAL}:* Specify the height and width of the output image.
-  *{C_IMAGE}:* Specify an image and the input image will be resized to the same dimensions.
            """.format(
                **{"C_IMAGE": C_IMAGE, "C_MANUAL": C_MANUAL}
            ),
        )

        self.specific_width = Integer(
            "Width of the final image",
            100,
            minval=1,
            doc="""\
*(Used only if resizing by specifying desired final dimensions)*

Enter the desired width of the final image, in pixels.""",
        )

        self.specific_height = Integer(
            "Height of the final image",
            100,
            minval=1,
            doc="""\
*(Used only if resizing by specifying desired final dimensions)*

Enter the desired height of the final image, in pixels.""",
        )

        self.specific_image = ImageSubscriber(
            "Select the image with the desired dimensions",
            "None",
            doc="""\
*(Used only if resizing by specifying desired final dimensions using an image)*

The input image will be resized to the dimensions of the specified image.""",
        )

        self.interpolation = Choice(
            "Interpolation method",
            I_ALL,
            doc="""\
-  *Nearest Neighbor:* Each output pixel is given the intensity of the
   nearest corresponding pixel in the input image.
-  *Bilinear:* Each output pixel is given the intensity of the weighted
   average of the 2x2 neighborhood at the corresponding position in the
   input image.
-  *Bicubic:* Each output pixel is given the intensity of the weighted
   average of the 4x4 neighborhood at the corresponding position in the
   input image.""",
        )

        self.separator = Divider(line=False)

        self.additional_images = []

        self.additional_image_count = HiddenCount(
            self.additional_images, "Additional image count"
        )

        self.add_button = DoSomething("", "Add another image", self.add_image)