def test_01_01_default(self): s = cps.FloatRange("foo", (1, 15)) self.assertEquals(s.min, 1) self.assertEquals(s.max, 15) self.assertEquals(s.min_text, "1.0") self.assertEquals(s.max_text, "15.0") s.test_valid(None)
def test_02_03_good_max(self): s = cps.FloatRange("foo", (1, 15), maxval = 20) for test_case in ("18", "20.00"): s.value_text = s.compose_max_text(test_case) self.assertEquals(s.max, float(test_case)) self.assertEquals(s.max_text, test_case) s.test_valid(None)
def test_00_00_init(self): x = cps.FloatRange("text",(1,2),1,5) x.test_valid(None) self.assertEqual(x.text,"text") self.assertEqual(x.value,(1,2)) self.assertEqual(x.min,1) self.assertEqual(x.max,2) x.test_valid(None)
def test_01_05_set_max_bad(self): s = cps.FloatRange("foo", (1, 15)) s.value_text = s.compose_max_text("a2") self.assertEquals(s.min, 1) self.assertEquals(s.max, 15) self.assertEquals(s.min_text, "1.0") self.assertEquals(s.max_text, "a2") self.assertRaises(cps.ValidationError, (lambda :s.test_valid(None)))
def test_01_04_set_max(self): s = cps.FloatRange("foo", (1, 15)) s.value_text = s.compose_max_text("016") self.assertEquals(s.min, 1) self.assertEquals(s.max, 16) self.assertEquals(s.min_text, "1.0") self.assertEquals(s.max_text, "016") s.test_valid(None)
def test_01_02_set_min(self): s = cps.FloatRange("foo", (1, 15)) s.value_text = s.compose_min_text("2.10") self.assertEquals(s.min, 2.1) self.assertEquals(s.max, 15) self.assertEquals(s.min_text, "2.10") self.assertEquals(s.max_text, "15.0") s.test_valid(None)
def test_01_07_display_float_range(self): v = cps.FloatRange("text",value=(1.5,2.5)) app,text_control,panel = self.set_setting(v) min_control = self.get_min_control(app,v) self.assertAlmostEqual(float(min_control.Value),1.5) max_control = self.get_max_control(app,v) self.assertAlmostEqual(float(max_control.Value),2.5) min_control.SetValue("0.5") app.ProcessPendingEvents() self.assertAlmostEqual(v.min,0.5) max_control = self.get_max_control(app,v) max_control.SetValue("3.5") app.ProcessPendingEvents() self.assertAlmostEqual(v.max,3.5) app.frame.Destroy() app.ProcessPendingEvents() app.ProcessIdle()
def test_02_02_neg_max(self): x = cps.FloatRange("text",(1,2),1,5) x.value = (1,6) self.assertRaises(ValueError,x.test_valid,None)
def test_01_02_assign_string(self): x = cps.FloatRange("text",(1,2),1,5) x.value = "2,5" self.assertEqual(x.min,2) self.assertEqual(x.max,5) x.test_valid(None)
def test_01_01_assign_tuple(self): x = cps.FloatRange("text",(1,2),1,5) x.value = (2,5) self.assertEqual(x.min,2) self.assertEqual(x.max,5) x.test_valid(None)
def test_02_04_bad_max(self): s = cps.FloatRange("foo", (1, 15), maxval = 20) s.value_text = s.compose_max_text("21") self.assertEquals(s.max, 20) self.assertEquals(s.max_text, "21") self.assertRaises(cps.ValidationError, (lambda :s.test_valid(None)))
def create_settings(self): """Create your settings by subclassing this function create_settings is called at the end of initialization. You should create the setting variables for your module here: # Ask the user for the input image self.image_name = cellprofiler.settings.ImageNameSubscriber(...) # Ask the user for the name of the output image self.output_image = cellprofiler.settings.ImageNameProvider(...) # Ask the user for a parameter self.smoothing_size = cellprofiler.settings.Float(...) """ self.objects_or_image = cps.Choice( "Display object or image measurements?", [OI_OBJECTS, OI_IMAGE],doc = """ <ul> <li><i>%(OI_OBJECTS)s</i> displays measurements made on objects.</li> <li><i>%(OI_IMAGE)s</i> displays a single measurement made on an image.</li> </ul>"""%globals()) self.objects_name = cps.ObjectNameSubscriber( "Select the input objects", cps.NONE, doc = """ <i>(Used only when displaying object measurements)</i><br> Choose the name of objects identified by some previous module (such as <b>IdentifyPrimaryObjects</b> or <b>IdentifySecondaryObjects</b>).""") def object_fn(): if self.objects_or_image == OI_OBJECTS: return self.objects_name.value else: return cpmeas.IMAGE self.measurement = cps.Measurement( "Measurement to display", object_fn,doc=""" Choose the measurement to display. This will be a measurement made by some previous module on either the whole image (if displaying a single image measurement) or on the objects you selected.""") self.wants_image = cps.Binary( "Display background image?", True, doc="""Choose whether or not to display the measurements on a background image. Usually, you will want to see the image context for the measurements, but it may be useful to save just the overlay of the text measurements and composite the overlay image and the original image later. Choose "Yes" to display the measurements on top of a background image or "No" to display the measurements on a black background.""") self.image_name = cps.ImageNameSubscriber( "Select the image on which to display the measurements", cps.NONE, doc=""" Choose the image to be displayed behind the measurements. This can be any image created or loaded by a previous module. If you have chosen not to display the background image, the image will only be used to determine the dimensions of the displayed image""") self.color_or_text = cps.Choice( "Display mode", [CT_TEXT, CT_COLOR], doc = """<i>(Used only when displaying object measurements)</i><br> Choose how to display the measurement information. If you choose %(CT_TEXT)s, <b>DisplayDataOnImage</b> will display the numeric value on top of each object. If you choose %(CT_COLOR)s, <b>DisplayDataOnImage</b> will convert the image to grayscale, if necessary, and display the portion of the image within each object using a hue that indicates the measurement value relative to the other objects in the set using the default color map. """ % globals() ) self.colormap = cps.Colormap( "Color map", doc = """<i>(Used only when displaying object measurements)</i><br> This is the color map used as the color gradient for coloring the objects by their measurement values. """) self.text_color = cps.Color( "Text color","red",doc=""" This is the color that will be used when displaying the text. """) self.display_image = cps.ImageNameProvider( "Name the output image that has the measurements displayed","DisplayImage",doc=""" The name that will be given to the image with the measurements superimposed. You can use this name to refer to the image in subsequent modules (such as <b>SaveImages</b>).""") self.font_size = cps.Integer( "Font size (points)", 10, minval=1) self.decimals = cps.Integer( "Number of decimals", 2, minval=0) self.saved_image_contents = cps.Choice( "Image elements to save", [E_IMAGE, E_FIGURE, E_AXES],doc=""" This setting controls the level of annotation on the image: <ul> <li><i>%(E_IMAGE)s:</i> Saves the image with the overlaid measurement annotations.</li> <li><i>%(E_AXES)s:</i> Adds axes with tick marks and image coordinates.</li> <li><i>%(E_FIGURE)s:</i> Adds a title and other decorations.</li></ul>"""%globals()) self.offset = cps.Integer( "Annotation offset (in pixels)",0,doc=""" Add a pixel offset to the measurement. Normally, the text is placed at the object (or image) center, which can obscure relevant features of the object. This setting adds a specified offset to the text, in a random direction.""") self.color_map_scale_choice = cps.Choice( "Color map scale", [CMS_USE_MEASUREMENT_RANGE, CMS_MANUAL], doc = """<i>(Used only when displaying object measurements as a colormap)</i><br> <b>DisplayDataOnImage</b> assigns a color to each object's measurement value from a colormap when in colormap-mode, mapping the value to a color along the colormap's continuum. This mapping has implicit upper and lower bounds to its range which are the extremes of the colormap. This setting determines whether the extremes are the minimum and maximum values of the measurement from among the objects in the current image or manually-entered extremes. Choose <i>%(CMS_USE_MEASUREMENT_RANGE)s</i> to use the full range of colors to get the maximum contrast within the image. Choose <i>%(CMS_MANUAL)s</i> to manually set the upper and lower bounds so that images with different maxima and minima can be compared by a uniform color mapping. """ % globals()) self.color_map_scale = cps.FloatRange( "Color map range", value = (0.0, 1.0), doc = """<i>(Used only when setting a manual colormap range)</i><br> This setting determines the lower and upper bounds of the values for the color map. """)
def create_settings(self): self.image_name = cps.ImageNameSubscriber( "Select the input image", cps.NONE, doc='''Select the image to be rescaled.''') self.rescaled_image_name = cps.ImageNameProvider( "Name the output image", "RescaledBlue", doc='''Enter the name of output rescaled image.''') self.rescale_method = cps.Choice('Rescaling method', choices=M_ALL, doc=''' There are a number of options for rescaling the input image: <ul> <li><i>%(M_STRETCH)s:</i> Find the minimum and maximum values within the unmasked part of the image (or the whole image if there is no mask) and rescale every pixel so that the minimum has an intensity of zero and the maximum has an intensity of one.</li> <li><i>%(M_MANUAL_INPUT_RANGE)s:</i> Pixels are scaled from their user-specified original range to the range 0 to 1. Options are available to handle values outside of the original range.<br> To convert 12-bit images saved in 16-bit format to the correct range, use the range 0 to 0.0625. The value 0.0625 is equivalent to 2<sup>12</sup> divided by 2<sup>16</sup>, so it will convert a 16 bit image containing only 12 bits of data to the proper range.</li> <li><i>%(M_MANUAL_IO_RANGE)s:</i> Pixels are scaled from their original range to the new target range. Options are available to handle values outside of the original range.</li> <li><i>%(M_DIVIDE_BY_IMAGE_MINIMUM)s:</i> Divide the intensity value of each pixel by the image's minimum intensity value so that all pixel intensities are equal to or greater than 1. The rescaled image can serve as an illumination correction function in <b>CorrectIlluminationApply</b>.</li> <li><i>%(M_DIVIDE_BY_IMAGE_MAXIMUM)s:</i> Divide the intensity value of each pixel by the image's maximum intensity value so that all pixel intensities are less than or equal to 1.</li> <li><i>%(M_DIVIDE_BY_VALUE)s:</i> Divide the intensity value of each pixel by the value entered.</li> <li><i>%(M_DIVIDE_BY_MEASUREMENT)s:</i> The intensity value of each pixel is divided by some previously calculated measurement. This measurement can be the output of some other module or can be a value loaded by the <b>Metadata</b> module.</li> <li><i>%(M_SCALE_BY_IMAGE_MAXIMUM)s:</i> Scale an image so that its maximum value is the same as the maximum value within the reference image.</li> <li><i>%(M_CONVERT_TO_8_BIT)s:</i> Images in CellProfiler are normally stored as a floating point number in the range of 0 to 1. This option converts these images to class uint8, meaning an 8 bit integer in the range of 0 to 255, reducing the amount of memory required to store the image. <i>Warning:</i> Most CellProfiler modules require the incoming image to be in the standard 0 to 1 range, so this conversion may cause downstream modules to behave in unexpected ways.</li> </ul>''' % globals()) self.wants_automatic_low = cps.Choice( 'Method to calculate the minimum intensity', LOW_ALL, doc=""" <i>(Used only if "%(M_MANUAL_IO_RANGE)s" is selected)</i><br> This setting controls how the minimum intensity is determined. <ul> <li><i>%(CUSTOM_VALUE)s:</i> Enter the minimum intensity manually below.</li> <li><i>%(LOW_EACH_IMAGE)s</i>: use the lowest intensity in this image as the minimum intensity for rescaling</li> <li><i>%(LOW_ALL_IMAGES)s</i>: use the lowest intensity from all images in the image group or the experiment if grouping is not being used. <b>Note:</b> Choosing this option may have undesirable results for a large ungrouped experiment split into a number of batches. Each batch will open all images from the chosen channel at the start of the run. This sort of synchronized action may have a severe impact on your network file system.</li> </ul> """ % globals()) self.wants_automatic_high = cps.Choice( 'Method to calculate the maximum intensity', HIGH_ALL, doc=""" <i>(Used only if "%(M_MANUAL_IO_RANGE)s" is selected)</i><br> This setting controls how the maximum intensity is determined. <ul> <li><i>%(CUSTOM_VALUE)s</i>: Enter the maximum intensity manually below.</li> <li><i>%(HIGH_EACH_IMAGE)s</i>: Use the highest intensity in this image as the maximum intensity for rescaling</li> <li><i>%(HIGH_ALL_IMAGES)s</i>: Use the highest intensity from all images in the image group or the experiment if grouping is not being used. <b>Note:</b> Choosing this option may have undesirable results for a large ungrouped experiment split into a number of batches. Each batch will open all images from the chosen channel at the start of the run. This sort of synchronized action may have a severe impact on your network file system.</li> </ul> """ % globals()) self.source_low = cps.Float( 'Lower intensity limit for the input image', 0) self.source_high = cps.Float( 'Upper intensity limit for the input image', 1) self.source_scale = cps.FloatRange( 'Intensity range for the input image', (0, 1)) self.dest_scale = cps.FloatRange( 'Intensity range for the output image', (0, 1)) self.low_truncation_choice = cps.Choice( 'Method to rescale pixels below the lower limit', [R_MASK, R_SET_TO_ZERO, R_SET_TO_CUSTOM, R_SCALE], doc=''' <i>(Used only if "%(M_MANUAL_IO_RANGE)s" is selected)</i><br> There are several ways to handle values less than the lower limit of the intensity range: <ul> <li><i>%(R_MASK)s:</i> Creates a mask for the output image. All pixels below the lower limit will be masked out.</li> <li><i>%(R_SET_TO_ZERO)s:</i> Sets all pixels below the lower limit to zero.</li> <li><i>%(R_SET_TO_CUSTOM)s:</i> Sets all pixels below the lower limit to a custom value.</li> <li><i>%(R_SCALE)s:</i> Scales pixels with values below the lower limit using the same offset and divisor as other pixels. The results will be less than zero.</li> </ul>''' % globals()) self.custom_low_truncation = cps.Float( "Custom value for pixels below lower limit", 0, doc=""" <i>(Used only if "%(M_MANUAL_IO_RANGE)s" and "%(R_SET_TO_CUSTOM)s are selected)</i><br> enter the custom value to be assigned to pixels with values below the lower limit.""" % globals()) self.high_truncation_choice = cps.Choice( 'Method to rescale pixels above the upper limit', [R_MASK, R_SET_TO_ONE, R_SET_TO_CUSTOM, R_SCALE], doc=""" <i>(Used only if "%(M_MANUAL_IO_RANGE)s" is selected)</i><br> There are several ways to handle values greater than the upper limit of the intensity range; Options are described in the Help for the equivalent lower limit question.""" % globals()) self.custom_high_truncation = cps.Float( "Custom value for pixels above upper limit", 0, doc=""" <i>(Used only if "%(M_MANUAL_IO_RANGE)s" and "%(R_SET_TO_CUSTOM)s are selected)</i><br> Enter the custom value to be assigned to pixels with values above the upper limit.""" % globals()) self.matching_image_name = cps.ImageNameSubscriber( "Select image to match in maximum intensity", cps.NONE, doc=""" <i>(Used only if "%(M_SCALE_BY_IMAGE_MAXIMUM)s" is selected)</i><br> Select the image whose maximum you want the rescaled image to match.""" % globals()) self.divisor_value = cps.Float("Divisor value", 1, minval=np.finfo(float).eps, doc=""" <i>(Used only if "%(M_DIVIDE_BY_VALUE)s" is selected)</i><br> Enter the value to use as the divisor for the final image.""" % globals()) self.divisor_measurement = cps.Measurement("Divisor measurement", lambda: cpmeas.IMAGE, doc=""" <i>(Used only if "%(M_DIVIDE_BY_MEASUREMENT)s" is selected)</i><br> Select the measurement value to use as the divisor for the final image.""" % globals())
def test_02_01_good_min(self): s = cps.FloatRange("foo", (1, 15), minval = 0) for test_case in ("2", "0"): s.value_text = s.compose_min_text(test_case) s.test_valid(None)
def test_02_03_neg_order(self): x = cps.FloatRange("text",(1,2),1,5) x.value = (2,1) self.assertRaises(ValueError,x.test_valid,None)
def test_03_01_no_range(self): """Regression test a bug where the variable throws an exception if there is no range""" x=cps.FloatRange("text",(1,2)) x.test_valid(None)
def test_02_02_bad_min(self): s = cps.FloatRange("foo", (1, 15), minval = 0) s.value_text = s.compose_min_text("-1") self.assertEquals(s.min, 0) self.assertEquals(s.min_text, "-1") self.assertRaises(cps.ValidationError, (lambda :s.test_valid(None)))
def create_settings(self): """Create the module settings create_settings is called at the end of initialization. """ self.object = cps.ObjectNameSubscriber( 'Select the object whose measurements will be displayed', cps.NONE, doc=''' Choose the name of objects identified by some previous module (such as <b>IdentifyPrimaryObjects</b> or <b>IdentifySecondaryObjects</b>) whose measurements are to be displayed.''' ) self.x_axis = cps.Measurement('Select the object measurement to plot', self.get_object, cps.NONE, doc=''' Choose the object measurement made by a previous module to plot.''') self.bins = cps.Integer('Number of bins', 100, 1, 1000, doc=''' Enter the number of equally-spaced bins that you want used on the X-axis.''') self.xscale = cps.Choice( 'Transform the data prior to plotting along the X-axis?', ['no', 'log'], None, doc=''' The measurement data can be scaled with either a linear scale (<i>No</i>) or a <i>log</i> (base 10) scaling. <p>Log scaling is useful when one of the measurements being plotted covers a large range of values; a log scale can bring out features in the measurements that would not easily be seen if the measurement is plotted linearly.<p>''') self.yscale = cps.Choice('How should the Y-axis be scaled?', ['linear', 'log'], None, doc=''' The Y-axis can be scaled either with either a <i>linear</i> scale or a <i>log</i> (base 10) scaling. <p>Log scaling is useful when one of the measurements being plotted covers a large range of values; a log scale can bring out features in the measurements that would not easily be seen if the measurement is plotted linearly.</p>''') self.title = cps.Text('Enter a title for the plot, if desired', '', doc=''' Enter a title for the plot. If you leave this blank, the title will default to <i>(cycle N)</i> where <i>N</i> is the current image cycle being executed.''') self.wants_xbounds = cps.Binary( 'Specify min/max bounds for the X-axis?', False, doc=''' Select <i>%(YES)s</i> to specify minimum and maximum values for the plot on the X-axis. This is helpful if an outlier bin skews the plot such that the bins of interest are no longer visible.''' % globals()) self.xbounds = cps.FloatRange('Minimum/maximum values for the X-axis')
def create_threshold_settings(self, methods=TM_METHODS): '''Create settings related to thresholding''' self.threshold_method = cps.Choice('Select the thresholding method', methods, doc=""" The intensity threshold affects the decision of whether each pixel will be considered foreground (regions of interest) or background. A stringent threshold will result in only bright regions being identified, with tight lines around them, whereas a lenient threshold will include dim regions and the lines between regions and background will be more loose. You can have the threshold automatically calculated using several methods, or you can enter an absolute number between 0 and 1 for the threshold. To help determine the choice of threshold manually, you can inspect the pixel intensities in an image of your choice. %(HELP_ON_PIXEL_INTENSITIES)s""" % globals() + """ Both options have advantages. An absolute number treats every image identically, but is not robust with regard to slight changes in lighting/staining conditions between images. An automatically calculated threshold adapts to changes in lighting/staining conditions between images and is usually more robust/accurate, but it can occasionally produce a poor threshold for unusual/artifactual images. It also takes a small amount of time to calculate. <p>The threshold that is used for each image is recorded as a measurement in the output file, so if you are surprised by unusual measurements from one of your images, you might check whether the automatically calculated threshold was unusually high or low compared to the other images. <p>There are seven methods for finding thresholds automatically: <ul><li><i>Otsu:</i> This method is probably best if you are not able to make certain assumptions about every images in your experiment, especially if the percentage of the image covered by regions of interest varies substantially from image to image. Our implementation takes into account the maximum and minimum values in the image and log-transforming the image prior to calculating the threshold. For this reason, please note that negative-valued pixels are ignored in this computation, so caution should be used in using image offsets (such as by using <b>ImageMath</b>). <p>If you know that the percentage of each image that is foreground does not vary much from image to image, the MoG method can be better, especially if the foreground percentage is not near 50%.</li> <li><i>Mixture of Gaussian (MoG):</i>This function assumes that the pixels in the image belong to either a background class or a foreground class, using an initial guess of the fraction of the image that is covered by foreground. This method is our own version of a Mixture of Gaussians algorithm (<i>O. Friman, unpublished</i>). Essentially, there are two steps: <ol><li>First, a number of Gaussian distributions are estimated to match the distribution of pixel intensities in the image. Currently three Gaussian distributions are fitted, one corresponding to a background class, one corresponding to a foreground class, and one distribution for an intermediate class. The distributions are fitted using the Expectation-Maximization algorithm, a procedure referred to as Mixture of Gaussians modeling. </li> <li>When the three Gaussian distributions have been fitted, a decision is made whether the intermediate class more closely models the background pixels or foreground pixels, based on the estimated fraction provided by the user.</li></ol></li> <li><i>Background:</i> This method is simple and appropriate for images in which most of the image is background. It finds the mode of the histogram of the image, which is assumed to be the background of the image, and chooses a threshold at twice that value (which you can adjust with a Threshold Correction Factor; see below). The calculation includes those pixels between 2% and 98% of the intensity range. This thresholding method can be helpful if your images vary in overall brightness, but the objects of interest are consistently N times brighter than the background level of the image. </li> <li><i>Robust background:</i> Much like the Background method, this method is also simple and assumes that the background distribution approximates a Gaussian by trimming the brightest and dimmest 5% of pixel intensities. It then calculates the mean and standard deviation of the remaining pixels and calculates the threshold as the mean + 2 times the standard deviation. This thresholding method can be helpful if the majority of the image is background, and the results are often comparable or better than the Background method.</li> <li><i>Ridler-Calvard:</i> This method is simple and its results are often very similar to Otsu's. According to Sezgin and Sankur's paper (<i>Journal of Electronic Imaging</i>, 2004), Otsu's overall quality on testing 40 nondestructive testing images is slightly better than Ridler's (average error: Otsu, 0.318; Ridler, 0.401). Ridler-Calvard chooses an initial threshold and then iteratively calculates the next one by taking the mean of the average intensities of the background and foreground pixels determined by the first threshold, repeating this until the threshold converges.</li> <li><i>Kapur:</i> This method computes the threshold of an image by log-transforming its values, then searching for the threshold that maximizes the sum of entropies of the foreground and background pixel values, when treated as separate distributions.</li> <li><i>Maximum correlation:</i>This is an implementation of the method described in Padmanabhan et al, 2010. It computes the maximum correlation between the binary mask created by thresholding and the thresholded image and is somewhat similar mathematically to Otsu. The authors claim superior results when thresholding images of neurites and other images that have sparse foreground densities.</li> </ul> <p>You can also choose between <i>Global</i>, <i>Adaptive</i>, and <i>Per-object</i> thresholding for the automatic methods: <ul> <li><i>Global:</i> One threshold is calculated for the entire image (fast)</li> <li><i>Adaptive:</i> The calculated threshold varies across the image. This method is a bit slower but may be more accurate near edges of regions of interest, or where illumination variation is significant (though in the latter case, using the <b>CorrectIllumination</b> modules is preferable).</li> <li><i>Per-object:</i> If you are using this module to find child objects located <i>within</i> parent objects, the per-object method will calculate a distinct threshold for each parent object. This is especially helpful, for example, when the background brightness varies substantially among the parent objects. <br><i>Important:</i> the per-object method requires that you run an <b>IdentifyPrimaryObjects</b> module to identify the parent objects upstream in the pipeline. After the parent objects are identified in the pipeline, you must then also run a <b>Crop</b> module with the following inputs: <ul> <li>The input image is the image containing the sub-objects to be identified.</li> <li>Select <i>Objects</i> as the shape to crop into.</li> <li>Select the parent objects (e.g., <i>Nuclei</i>) as the objects to use as a cropping mask.</li> </ul> Finally, in the <b>IdentifyPrimaryObjects</b> module, select the cropped image as input image.</li></ul> <p>Selecting <i>manual thresholding</i> allows you to enter a single value between 0 and 1 as the threshold value. This setting can be useful when you are certain what the cutoff should be and it does not vary from image to image in the experiment. If you are using this module to find objects in an image that is already binary (where the foreground is 1 and the background is 0), a manual value of 0.5 will identify the objects. <p>Selecting thresholding via a <i>binary image</i> will use a selected binary image as a mask for the input image. The most typical approach to produce a binary image is to use the <b>ApplyThreshold</b> module (image as input, image as output) or the <b>ConvertObjectsToImage</b> module (objects as input, image as output); both have options to produce a binary image. Note that unlike <b>MaskImage</b>, the binary image will not be stored permanently as a mask. Also, even though no algorithm is actually used to find the threshold in this case, the final threshold value is reported as the Otsu threshold calculated for the foreground region. <p>Selecting thresholding via <i>measurement</i> will use an image measurement previously calculated in order to threshold the image. Like manual thresholding, this setting can be useful when you are certain what the cutoff should be. The difference in this case is that the desired threshold does vary from image to image in the experiment but can be measured using a Measurement module.</p> <p>References <ul> <li>Sezgin M, Sankur B (2004) "Survey over image thresholding techniques and quantitative performance evaluation." <i>Journal of Electronic Imaging</i>, 13(1), 146-165</li> <li>Padmanabhan K, Eddy WF, Crowley JC (2010) "A novel algorithm for optimal image thresholding of biological data" <i>Journal of Neuroscience Methods</i> 193, 380-384.</li> </ul></p> """) self.threshold_correction_factor = cps.Float( 'Threshold correction factor', 1, doc="""\ When the threshold is calculated automatically, it may consistently be too stringent or too lenient. You may need to enter an adjustment factor that you empirically determine is suitable for your images. The number 1 means no adjustment, 0 to 1 makes the threshold more lenient and greater than 1 (e.g., 1.3) makes the threshold more stringent. For example, the Otsu automatic thresholding inherently assumes that 50% of the image is covered by objects. If a larger percentage of the image is covered, the Otsu method will give a slightly biased threshold that may have to be corrected using this setting.""") self.threshold_range = cps.FloatRange( 'Lower and upper bounds on threshold', (0, 1), minval=0, maxval=1, doc="""\ Enter the minimum and maximum allowable threshold, in the range [0,1]. This is helpful as a safety precaution when the threshold is calculated automatically. For example, if there are no objects in the field of view, the automatic threshold might be calculated as unreasonably low. In such cases, the lower bound you enter here will override the automatic threshold.""" ) self.object_fraction = cps.CustomChoice( 'Approximate fraction of image covered by objects?', [ '0.01', '0.1', '0.2', '0.3', '0.4', '0.5', '0.6', '0.7', '0.8', '0.9', '0.99' ], doc="""\ <i>(Used only when applying the MoG thresholding method)</i><br> Enter an estimate of how much of the image is covered with objects, which is used to estimate the distribution of pixel intensities.""") self.manual_threshold = cps.Float("Manual threshold", value=0.0, minval=0.0, maxval=1.0, doc="""\ <i>(Used only if Manual selected for thresholding method)</i><br> Enter the value that will act as an absolute threshold for the images, in the range of [0,1].""" ) self.thresholding_measurement = cps.Measurement( "Select the measurement to threshold with", lambda: cpmeas.IMAGE, doc=""" <i>(Used only if Measurement is selected for thresholding method)</i><br> Choose the image measurement that will act as an absolute threshold for the images.""" ) self.binary_image = cps.ImageNameSubscriber("Select binary image", "None", doc=""" <i>(Used only if Binary image selected for thresholding method)</i><br> What is the binary thresholding image?""") self.two_class_otsu = cps.Choice( 'Two-class or three-class thresholding?', [O_TWO_CLASS, O_THREE_CLASS], doc=""" <i>(Used only for the Otsu thresholding method)</i> <br> Select <i>Two</i> if the grayscale levels are readily distinguishable into only two classes: foreground (i.e., objects) and background. Select <i>Three</i> if the grayscale levels fall instead into three classes. You will then be asked whether the middle intensity class should be added to the foreground or background class in order to generate the final two-class output. Note that whether two- or three-class thresholding is chosen, the image pixels are always finally assigned two classes: foreground and background. <p>For example, three-class thresholding may be useful for images in which you have nuclear staining along with low-intensity non-specific cell staining. Where two-class thresholding might incorrectly assign this intermediate staining to the nuclei objects for some cells, three-class thresholding allows you to assign it to the foreground or background as desired. However, in extreme cases where either there are almost no objects or the entire field of view is covered with objects, three-class thresholding may perform worse than two-class.""" ) self.use_weighted_variance = cps.Choice( 'Minimize the weighted variance or the entropy?', [O_WEIGHTED_VARIANCE, O_ENTROPY]) self.assign_middle_to_foreground = cps.Choice( 'Assign pixels in the middle intensity class to the foreground ' 'or the background?', [O_FOREGROUND, O_BACKGROUND], doc=""" <i>(Used only for three-class thresholding)</i><br> Choose whether you want the pixels with middle grayscale intensities to be assigned to the foreground class or the background class.""") self.adaptive_window_method = cps.Choice( "Method to calculate adaptive window size", [FI_IMAGE_SIZE, FI_CUSTOM], doc=""" <i>(Used only if an adaptive thresholding method is used)</i><br> The adaptive method breaks the image into blocks, computing the threshold for each block. There are two ways to compute the block size: <ul> <li><i>%(FI_IMAGE_SIZE)s:</i> The block size is one-tenth of the image dimensions, or 50 x 50 pixels, whichever is bigger.</li> <li><i>%(FI_CUSTOM)s:</i> The block size is specified by the user.</li> </ul>""" % globals()) self.adaptive_window_size = cps.Integer('Size of adaptive window', 10, doc=""" <i>(Used only if an adaptive thresholding method with a %(FI_CUSTOM)s window size are selected)</i><br> Enter the window for the adaptive method. For example, you may want to use a multiple of the largest expected object size.""" % globals())