Пример #1
0
 def test_01_02_boundary(self):
     # Test an image with pixels at the boundaries.
     image = np.array([[0, 1, 0], [1, 1, 1], [0, 1, 0]], bool)
     a = 5. / 3.
     b = a + 1
     self.assertAlmostEqual(b / 4 + 1, a)
     expected = np.array([[0, a, 0], [a, b, a], [0, a, 0]])
     p = F.poisson_equation(image, convergence=.00001)
     np.testing.assert_almost_equal(p, expected, 4)
Пример #2
0
 def test_01_01_simple(self):
     image = np.array([[0, 0, 0, 0, 0], [0, 0, 1, 0, 0], [0, 1, 1, 1, 0],
                       [0, 0, 1, 0, 0], [0, 0, 0, 0, 0]], bool)
     a = 5. / 3.
     b = a + 1
     self.assertAlmostEqual(b / 4 + 1, a)
     expected = np.array([[0, 0, 0, 0, 0], [0, 0, a, 0, 0], [0, a, b, a, 0],
                          [0, 0, a, 0, 0], [0, 0, 0, 0, 0]])
     p = F.poisson_equation(image, convergence=.00001)
     np.testing.assert_almost_equal(p, expected, 4)
Пример #3
0
 def test_01_03_subsampling(self):
     # Test an image that is large enough to undergo some subsampling
     #
     r = np.random.RandomState()
     r.seed(13)
     image = r.uniform(size=(300, 300)) < .001
     i, j = np.mgrid[-8:9, -8:9]
     kernel = i * i + j * j <= 64
     image = binary_dilation(image, kernel)
     p = F.poisson_equation(image, convergence=.001)
     i, j = np.mgrid[0:p.shape[0], 0:p.shape[1]]
     mask = image & (i > 0) & (i < p.shape[0] - 1) & (j > 0) & (
         j < p.shape[1] - 1)
     i, j = i[mask], j[mask]
     expected = (p[i + 1, j] + p[i - 1, j] + p[i, j + 1] +
                 p[i, j - 1]) / 4 + 1
     np.testing.assert_almost_equal(p[mask], expected, 0)
 def get_skeleton_points(self, obj):
     '''Get points by skeletonizing the objects and decimating'''
     ii = []
     jj = []
     total_skel = np.zeros(obj.shape, bool)
     for labels, indexes in obj.get_labels():
         colors = morph.color_labels(labels)
         for color in range(1, np.max(colors) + 1):
             labels_mask = colors == color
             skel = morph.skeletonize(
                 labels_mask,
                 ordering=distance_transform_edt(labels_mask) *
                 poisson_equation(labels_mask))
             total_skel = total_skel | skel
     n_pts = np.sum(total_skel)
     if n_pts == 0:
         return np.zeros(0, np.int32), np.zeros(0, np.int32)
     i, j = np.where(total_skel)
     if n_pts > self.max_points.value:
         #
         # Decimate the skeleton by finding the branchpoints in the
         # skeleton and propagating from those.
         #
         markers = np.zeros(total_skel.shape, np.int32)
         branchpoints = \
             morph.branchpoints(total_skel) | morph.endpoints(total_skel)
         markers[branchpoints] = np.arange(np.sum(branchpoints)) + 1
         #
         # We compute the propagation distance to that point, then impose
         # a slightly arbitarary order to get an unambiguous ordering
         # which should number the pixels in a skeleton branch monotonically
         #
         ts_labels, distances = propagate(np.zeros(markers.shape), markers,
                                          total_skel, 1)
         order = np.lexsort((j, i, distances[i, j], ts_labels[i, j]))
         #
         # Get a linear space of self.max_points elements with bounds at
         # 0 and len(order)-1 and use that to select the points.
         #
         order = order[np.linspace(0,
                                   len(order) - 1,
                                   self.max_points.value).astype(int)]
         return i[order], j[order]
     return i, j
 def get_skeleton_points(self, obj):
     '''Get points by skeletonizing the objects and decimating'''
     ii = []
     jj = []
     total_skel = np.zeros(obj.shape, bool)
     for labels, indexes in obj.get_labels():
         colors = morph.color_labels(labels)
         for color in range(1, np.max(colors) + 1):
             labels_mask = colors == color
             skel = morph.skeletonize(
                 labels_mask,
                 ordering = distance_transform_edt(labels_mask) *
                 poisson_equation(labels_mask))
             total_skel = total_skel | skel
     n_pts = np.sum(total_skel)
     if n_pts == 0:
         return np.zeros(0, np.int32), np.zeros(0, np.int32)
     i, j = np.where(total_skel)
     if n_pts > self.max_points.value:
         #
         # Decimate the skeleton by finding the branchpoints in the
         # skeleton and propagating from those.
         #
         markers = np.zeros(total_skel.shape, np.int32)
         branchpoints = \
             morph.branchpoints(total_skel) | morph.endpoints(total_skel)
         markers[branchpoints] = np.arange(np.sum(branchpoints))+1
         #
         # We compute the propagation distance to that point, then impose
         # a slightly arbitarary order to get an unambiguous ordering
         # which should number the pixels in a skeleton branch monotonically
         #
         ts_labels, distances = propagate(np.zeros(markers.shape),
                                  markers, total_skel, 1)
         order = np.lexsort((j, i, distances[i, j], ts_labels[i, j]))
         #
         # Get a linear space of self.max_points elements with bounds at
         # 0 and len(order)-1 and use that to select the points.
         #
         order = order[
             np.linspace(0, len(order)-1, self.max_points.value).astype(int)]
         return i[order], j[order]
     return i, j
Пример #6
0
    def run_function(self, function, pixel_data, mask):
        '''Apply the function once to the image, returning the result'''
        count = function.repeat_count
        function_name = function.function.value
        scale = function.scale.value
        custom_repeats = function.custom_repeats.value

        is_binary = pixel_data.dtype.kind == 'b'
        if function.structuring_element == SE_ARBITRARY:
            strel = np.array(function.strel.get_matrix())
        elif function.structuring_element == SE_DISK:
            strel = morph.strel_disk(scale / 2.0)
        elif function.structuring_element == SE_DIAMOND:
            strel = morph.strel_diamond(scale / 2.0)
        elif function.structuring_element == SE_LINE:
            strel = morph.strel_line(scale, function.angle.value)
        elif function.structuring_element == SE_OCTAGON:
            strel = morph.strel_octagon(scale / 2.0)
        elif function.structuring_element == SE_PAIR:
            strel = morph.strel_pair(function.x_offset.value,
                                     function.y_offset.value)
        elif function.structuring_element == SE_PERIODIC_LINE:
            xoff = function.x_offset.value
            yoff = function.y_offset.value
            n = max(scale / 2.0 / np.sqrt(float(xoff * xoff + yoff * yoff)), 1)
            strel = morph.strel_periodicline(xoff, yoff, n)
        elif function.structuring_element == SE_RECTANGLE:
            strel = morph.strel_rectangle(function.width.value,
                                          function.height.value)
        else:
            strel = morph.strel_square(scale)

        if (function_name
                in (F_BRANCHPOINTS, F_BRIDGE, F_CLEAN, F_DIAG, F_CONVEX_HULL,
                    F_DISTANCE, F_ENDPOINTS, F_FILL, F_FILL_SMALL, F_HBREAK,
                    F_LIFE, F_MAJORITY, F_REMOVE, F_SHRINK, F_SKEL, F_SKELPE,
                    F_SPUR, F_THICKEN, F_THIN, F_VBREAK) and not is_binary):
            # Apply a very crude threshold to the image for binary algorithms
            logger.warning("Warning: converting image to binary for %s\n" %
                           function_name)
            pixel_data = pixel_data != 0

        if (function_name
                in (F_BRANCHPOINTS, F_BRIDGE, F_CLEAN, F_DIAG, F_CONVEX_HULL,
                    F_DISTANCE, F_ENDPOINTS, F_FILL, F_FILL_SMALL, F_HBREAK,
                    F_INVERT, F_LIFE, F_MAJORITY, F_REMOVE, F_SHRINK, F_SKEL,
                    F_SKELPE, F_SPUR, F_THICKEN, F_THIN, F_VBREAK, F_OPENLINES)
                or
            (is_binary
             and function_name in (F_CLOSE, F_DILATE, F_ERODE, F_OPEN))):
            # All of these have an iterations argument or it makes no
            # sense to iterate
            if function_name == F_BRANCHPOINTS:
                return morph.branchpoints(pixel_data, mask)
            elif function_name == F_BRIDGE:
                return morph.bridge(pixel_data, mask, count)
            elif function_name == F_CLEAN:
                return morph.clean(pixel_data, mask, count)
            elif function_name == F_CLOSE:
                if mask is None:
                    return scind.binary_closing(pixel_data,
                                                strel,
                                                iterations=count)
                else:
                    return (scind.binary_closing(
                        pixel_data & mask, strel, iterations=count) |
                            (pixel_data & ~mask))
            elif function_name == F_CONVEX_HULL:
                if mask is None:
                    return morph.convex_hull_image(pixel_data)
                else:
                    return morph.convex_hull_image(pixel_data & mask)
            elif function_name == F_DIAG:
                return morph.diag(pixel_data, mask, count)
            elif function_name == F_DILATE:
                return scind.binary_dilation(pixel_data,
                                             strel,
                                             iterations=count,
                                             mask=mask)
            elif function_name == F_DISTANCE:
                image = scind.distance_transform_edt(pixel_data)
                if function.rescale_values.value:
                    image = image / np.max(image)
                return image
            elif function_name == F_ENDPOINTS:
                return morph.endpoints(pixel_data, mask)
            elif function_name == F_ERODE:
                return scind.binary_erosion(pixel_data,
                                            strel,
                                            iterations=count,
                                            mask=mask)
            elif function_name == F_FILL:
                return morph.fill(pixel_data, mask, count)
            elif function_name == F_FILL_SMALL:

                def small_fn(area, foreground):
                    return (not foreground) and (area <= custom_repeats)

                return morph.fill_labeled_holes(pixel_data, mask, small_fn)
            elif function_name == F_HBREAK:
                return morph.hbreak(pixel_data, mask, count)
            elif function_name == F_INVERT:
                if is_binary:
                    if mask is None:
                        return ~pixel_data
                    result = pixel_data.copy()
                    result[mask] = ~result[mask]
                    return result
                elif mask is None:
                    return 1 - pixel_data
                else:
                    result = pixel_data.copy()
                    result[mask] = 1 - result[mask]
                    return result
            elif function_name == F_LIFE:
                return morph.life(pixel_data, count)
            elif function_name == F_MAJORITY:
                return morph.majority(pixel_data, mask, count)
            elif function_name == F_OPEN:
                if mask is None:
                    return scind.binary_opening(pixel_data,
                                                strel,
                                                iterations=count)
                else:
                    return (scind.binary_opening(
                        pixel_data & mask, strel, iterations=count) |
                            (pixel_data & ~mask))
            elif function_name == F_OPENLINES:
                return morph.openlines(pixel_data,
                                       linelength=custom_repeats,
                                       mask=mask)
            elif function_name == F_REMOVE:
                return morph.remove(pixel_data, mask, count)
            elif function_name == F_SHRINK:
                return morph.binary_shrink(pixel_data, count)
            elif function_name == F_SKEL:
                return morph.skeletonize(pixel_data, mask)
            elif function_name == F_SKELPE:
                return morph.skeletonize(
                    pixel_data, mask,
                    scind.distance_transform_edt(pixel_data) *
                    poisson_equation(pixel_data))
            elif function_name == F_SPUR:
                return morph.spur(pixel_data, mask, count)
            elif function_name == F_THICKEN:
                return morph.thicken(pixel_data, mask, count)
            elif function_name == F_THIN:
                return morph.thin(pixel_data, mask, count)
            elif function_name == F_VBREAK:
                return morph.vbreak(pixel_data, mask)
            else:
                raise NotImplementedError(
                    "Unimplemented morphological function: %s" % function_name)
        else:
            for i in range(count):
                if function_name == F_BOTHAT:
                    new_pixel_data = morph.black_tophat(pixel_data,
                                                        mask=mask,
                                                        footprint=strel)
                elif function_name == F_CLOSE:

                    new_pixel_data = morph.closing(pixel_data,
                                                   mask=mask,
                                                   footprint=strel)
                elif function_name == F_DILATE:
                    new_pixel_data = morph.grey_dilation(pixel_data,
                                                         mask=mask,
                                                         footprint=strel)
                elif function_name == F_ERODE:
                    new_pixel_data = morph.grey_erosion(pixel_data,
                                                        mask=mask,
                                                        footprint=strel)
                elif function_name == F_OPEN:
                    new_pixel_data = morph.opening(pixel_data,
                                                   mask=mask,
                                                   footprint=strel)
                elif function_name == F_TOPHAT:
                    new_pixel_data = morph.white_tophat(pixel_data,
                                                        mask=mask,
                                                        footprint=strel)
                else:
                    raise NotImplementedError(
                        "Unimplemented morphological function: %s" %
                        function_name)
                if np.all(new_pixel_data == pixel_data):
                    break
                pixel_data = new_pixel_data
            return pixel_data
Пример #7
0
    def run_function(self, function, pixel_data, mask):
        '''Apply the function once to the image, returning the result'''
        count = function.repeat_count
        function_name = function.function.value
        custom_repeats = function.custom_repeats.value

        is_binary = pixel_data.dtype.kind == 'b'

        if (function_name in (F_BRANCHPOINTS, F_BRIDGE, F_CLEAN, F_DIAG,
                              F_CONVEX_HULL, F_DISTANCE, F_ENDPOINTS, F_FILL,
                              F_HBREAK, F_MAJORITY, F_REMOVE, F_SHRINK,
                              F_SKELPE, F_SPUR, F_THICKEN, F_THIN, F_VBREAK)
                and not is_binary):
            # Apply a very crude threshold to the image for binary algorithms
            logger.warning("Warning: converting image to binary for %s\n" %
                           function_name)
            pixel_data = pixel_data != 0

        if function_name in (F_BRANCHPOINTS, F_BRIDGE, F_CLEAN, F_DIAG,
                             F_CONVEX_HULL, F_DISTANCE, F_ENDPOINTS, F_FILL,
                             F_HBREAK, F_MAJORITY, F_REMOVE, F_SHRINK,
                             F_SKELPE, F_SPUR, F_THICKEN, F_THIN, F_VBREAK,
                             F_OPENLINES):
            # All of these have an iterations argument or it makes no
            # sense to iterate
            if function_name == F_BRANCHPOINTS:
                return morph.branchpoints(pixel_data, mask)
            elif function_name == F_BRIDGE:
                return morph.bridge(pixel_data, mask, count)
            elif function_name == F_CLEAN:
                return morph.clean(pixel_data, mask, count)
            elif function_name == F_CONVEX_HULL:
                if mask is None:
                    return morph.convex_hull_image(pixel_data)
                else:
                    return morph.convex_hull_image(pixel_data & mask)
            elif function_name == F_DIAG:
                return morph.diag(pixel_data, mask, count)
            elif function_name == F_DISTANCE:
                image = scind.distance_transform_edt(pixel_data)
                if function.rescale_values.value:
                    image = image / np.max(image)
                return image
            elif function_name == F_ENDPOINTS:
                return morph.endpoints(pixel_data, mask)
            elif function_name == F_FILL:
                return morph.fill(pixel_data, mask, count)
            elif function_name == F_HBREAK:
                return morph.hbreak(pixel_data, mask, count)
            elif function_name == F_MAJORITY:
                return morph.majority(pixel_data, mask, count)
            elif function_name == F_OPENLINES:
                return morph.openlines(pixel_data,
                                       linelength=custom_repeats,
                                       mask=mask)
            elif function_name == F_REMOVE:
                return morph.remove(pixel_data, mask, count)
            elif function_name == F_SHRINK:
                return morph.binary_shrink(pixel_data, count)
            elif function_name == F_SKELPE:
                return morph.skeletonize(
                    pixel_data, mask,
                    scind.distance_transform_edt(pixel_data) *
                    poisson_equation(pixel_data))
            elif function_name == F_SPUR:
                return morph.spur(pixel_data, mask, count)
            elif function_name == F_THICKEN:
                return morph.thicken(pixel_data, mask, count)
            elif function_name == F_THIN:
                return morph.thin(pixel_data, mask, count)
            elif function_name == F_VBREAK:
                return morph.vbreak(pixel_data, mask)
            else:
                raise NotImplementedError(
                    "Unimplemented morphological function: %s" % function_name)
            return pixel_data
Пример #8
0
 def test_00_01_single(self):
     image = np.zeros((11, 14), bool)
     image[7, 3] = True
     p = F.poisson_equation(image)
     np.testing.assert_array_equal(p[image], 1)
     np.testing.assert_array_equal(p[~image], 0)
Пример #9
0
 def test_00_00_nothing(self):
     image = np.zeros((11, 14), bool)
     p = F.poisson_equation(image)
     np.testing.assert_array_equal(p, 0)
Пример #10
0
 def fn(x):
     d = scind.distance_transform_edt(x)
     pe = cpfilter.poisson_equation(x)
     return cpmorph.skeletonize(x, ordering=pe * d)
Пример #11
0
 def fn(x):
     d = scind.distance_transform_edt(x)
     pe = cpfilter.poisson_equation(x)
     return cpmorph.skeletonize(x, ordering=pe * d)
Пример #12
0
    def run_function(self, function, pixel_data, mask):
        '''Apply the function once to the image, returning the result'''
        count = function.repeat_count
        function_name = function.function.value
        scale = function.scale.value
        custom_repeats = function.custom_repeats.value

        is_binary = pixel_data.dtype.kind == 'b'
        if function.structuring_element == SE_ARBITRARY:
            strel = np.array(function.strel.get_matrix())
        elif function.structuring_element == SE_DISK:
            strel = morph.strel_disk(scale / 2.0)
        elif function.structuring_element == SE_DIAMOND:
            strel = morph.strel_diamond(scale / 2.0)
        elif function.structuring_element == SE_LINE:
            strel = morph.strel_line(scale, function.angle.value)
        elif function.structuring_element == SE_OCTAGON:
            strel = morph.strel_octagon(scale / 2.0)
        elif function.structuring_element == SE_PAIR:
            strel = morph.strel_pair(function.x_offset.value,
                                     function.y_offset.value)
        elif function.structuring_element == SE_PERIODIC_LINE:
            xoff = function.x_offset.value
            yoff = function.y_offset.value
            n = max(scale / 2.0 / np.sqrt(float(xoff * xoff + yoff * yoff)), 1)
            strel = morph.strel_periodicline(
                    xoff, yoff, n)
        elif function.structuring_element == SE_RECTANGLE:
            strel = morph.strel_rectangle(
                    function.width.value, function.height.value)
        else:
            strel = morph.strel_square(scale)

        if (function_name in (F_BRANCHPOINTS, F_BRIDGE, F_CLEAN, F_DIAG,
                              F_CONVEX_HULL, F_DISTANCE, F_ENDPOINTS, F_FILL,
                              F_FILL_SMALL, F_HBREAK, F_LIFE, F_MAJORITY,
                              F_REMOVE, F_SHRINK, F_SKEL, F_SKELPE, F_SPUR,
                              F_THICKEN, F_THIN, F_VBREAK)
            and not is_binary):
            # Apply a very crude threshold to the image for binary algorithms
            logger.warning("Warning: converting image to binary for %s\n" %
                           function_name)
            pixel_data = pixel_data != 0

        if (function_name in (F_BRANCHPOINTS, F_BRIDGE, F_CLEAN, F_DIAG,
                              F_CONVEX_HULL, F_DISTANCE, F_ENDPOINTS, F_FILL,
                              F_FILL_SMALL,
                              F_HBREAK, F_INVERT, F_LIFE, F_MAJORITY, F_REMOVE,
                              F_SHRINK,
                              F_SKEL, F_SKELPE, F_SPUR, F_THICKEN, F_THIN,
                              F_VBREAK, F_OPENLINES) or
                (is_binary and
                         function_name in (F_CLOSE, F_DILATE, F_ERODE, F_OPEN))):
            # All of these have an iterations argument or it makes no
            # sense to iterate
            if function_name == F_BRANCHPOINTS:
                return morph.branchpoints(pixel_data, mask)
            elif function_name == F_BRIDGE:
                return morph.bridge(pixel_data, mask, count)
            elif function_name == F_CLEAN:
                return morph.clean(pixel_data, mask, count)
            elif function_name == F_CLOSE:
                if mask is None:
                    return scind.binary_closing(pixel_data,
                                                strel,
                                                iterations=count)
                else:
                    return (scind.binary_closing(pixel_data & mask,
                                                 strel,
                                                 iterations=count) |
                            (pixel_data & ~ mask))
            elif function_name == F_CONVEX_HULL:
                if mask is None:
                    return morph.convex_hull_image(pixel_data)
                else:
                    return morph.convex_hull_image(pixel_data & mask)
            elif function_name == F_DIAG:
                return morph.diag(pixel_data, mask, count)
            elif function_name == F_DILATE:
                return scind.binary_dilation(pixel_data,
                                             strel,
                                             iterations=count,
                                             mask=mask)
            elif function_name == F_DISTANCE:
                image = scind.distance_transform_edt(pixel_data)
                if function.rescale_values.value:
                    image = image / np.max(image)
                return image
            elif function_name == F_ENDPOINTS:
                return morph.endpoints(pixel_data, mask)
            elif function_name == F_ERODE:
                return scind.binary_erosion(pixel_data, strel,
                                            iterations=count,
                                            mask=mask)
            elif function_name == F_FILL:
                return morph.fill(pixel_data, mask, count)
            elif function_name == F_FILL_SMALL:
                def small_fn(area, foreground):
                    return (not foreground) and (area <= custom_repeats)

                return morph.fill_labeled_holes(pixel_data, mask, small_fn)
            elif function_name == F_HBREAK:
                return morph.hbreak(pixel_data, mask, count)
            elif function_name == F_INVERT:
                if is_binary:
                    if mask is None:
                        return ~ pixel_data
                    result = pixel_data.copy()
                    result[mask] = ~result[mask]
                    return result
                elif mask is None:
                    return 1 - pixel_data
                else:
                    result = pixel_data.copy()
                    result[mask] = 1 - result[mask]
                    return result
            elif function_name == F_LIFE:
                return morph.life(pixel_data, count)
            elif function_name == F_MAJORITY:
                return morph.majority(pixel_data, mask, count)
            elif function_name == F_OPEN:
                if mask is None:
                    return scind.binary_opening(pixel_data,
                                                strel,
                                                iterations=count)
                else:
                    return (scind.binary_opening(pixel_data & mask,
                                                 strel,
                                                 iterations=count) |
                            (pixel_data & ~ mask))
            elif function_name == F_OPENLINES:
                return morph.openlines(pixel_data, linelength=custom_repeats, mask=mask)
            elif function_name == F_REMOVE:
                return morph.remove(pixel_data, mask, count)
            elif function_name == F_SHRINK:
                return morph.binary_shrink(pixel_data, count)
            elif function_name == F_SKEL:
                return morph.skeletonize(pixel_data, mask)
            elif function_name == F_SKELPE:
                return morph.skeletonize(
                        pixel_data, mask,
                        scind.distance_transform_edt(pixel_data) *
                        poisson_equation(pixel_data))
            elif function_name == F_SPUR:
                return morph.spur(pixel_data, mask, count)
            elif function_name == F_THICKEN:
                return morph.thicken(pixel_data, mask, count)
            elif function_name == F_THIN:
                return morph.thin(pixel_data, mask, count)
            elif function_name == F_VBREAK:
                return morph.vbreak(pixel_data, mask)
            else:
                raise NotImplementedError("Unimplemented morphological function: %s" %
                                          function_name)
        else:
            for i in range(count):
                if function_name == F_BOTHAT:
                    new_pixel_data = morph.black_tophat(pixel_data, mask=mask,
                                                        footprint=strel)
                elif function_name == F_CLOSE:

                    new_pixel_data = morph.closing(pixel_data, mask=mask,
                                                   footprint=strel)
                elif function_name == F_DILATE:
                    new_pixel_data = morph.grey_dilation(pixel_data, mask=mask,
                                                         footprint=strel)
                elif function_name == F_ERODE:
                    new_pixel_data = morph.grey_erosion(pixel_data, mask=mask,
                                                        footprint=strel)
                elif function_name == F_OPEN:
                    new_pixel_data = morph.opening(pixel_data, mask=mask,
                                                   footprint=strel)
                elif function_name == F_TOPHAT:
                    new_pixel_data = morph.white_tophat(pixel_data, mask=mask,
                                                        footprint=strel)
                else:
                    raise NotImplementedError("Unimplemented morphological function: %s" %
                                              function_name)
                if np.all(new_pixel_data == pixel_data):
                    break
                pixel_data = new_pixel_data
            return pixel_data
Пример #13
0
    def run_function(self, function, pixel_data, mask):
        '''Apply the function once to the image, returning the result'''
        count = function.repeat_count
        function_name = function.function.value
        custom_repeats = function.custom_repeats.value

        is_binary = pixel_data.dtype.kind == 'b'

        if (function_name in (F_BRANCHPOINTS, F_BRIDGE, F_CLEAN, F_DIAG,
                              F_CONVEX_HULL, F_DISTANCE, F_ENDPOINTS, F_FILL,
                              F_HBREAK, F_MAJORITY,
                              F_REMOVE, F_SHRINK, F_SKELPE, F_SPUR,
                              F_THICKEN, F_THIN, F_VBREAK)
            and not is_binary):
            # Apply a very crude threshold to the image for binary algorithms
            logger.warning("Warning: converting image to binary for %s\n" %
                           function_name)
            pixel_data = pixel_data != 0

        if function_name in (F_BRANCHPOINTS, F_BRIDGE, F_CLEAN, F_DIAG, F_CONVEX_HULL, F_DISTANCE, F_ENDPOINTS, F_FILL,
                             F_HBREAK, F_MAJORITY, F_REMOVE, F_SHRINK, F_SKELPE, F_SPUR, F_THICKEN,
                             F_THIN, F_VBREAK, F_OPENLINES):
            # All of these have an iterations argument or it makes no
            # sense to iterate
            if function_name == F_BRANCHPOINTS:
                return morph.branchpoints(pixel_data, mask)
            elif function_name == F_BRIDGE:
                return morph.bridge(pixel_data, mask, count)
            elif function_name == F_CLEAN:
                return morph.clean(pixel_data, mask, count)
            elif function_name == F_CONVEX_HULL:
                if mask is None:
                    return morph.convex_hull_image(pixel_data)
                else:
                    return morph.convex_hull_image(pixel_data & mask)
            elif function_name == F_DIAG:
                return morph.diag(pixel_data, mask, count)
            elif function_name == F_DISTANCE:
                image = scind.distance_transform_edt(pixel_data)
                if function.rescale_values.value:
                    image = image / np.max(image)
                return image
            elif function_name == F_ENDPOINTS:
                return morph.endpoints(pixel_data, mask)
            elif function_name == F_FILL:
                return morph.fill(pixel_data, mask, count)
            elif function_name == F_HBREAK:
                return morph.hbreak(pixel_data, mask, count)
            elif function_name == F_MAJORITY:
                return morph.majority(pixel_data, mask, count)
            elif function_name == F_OPENLINES:
                return morph.openlines(pixel_data, linelength=custom_repeats, mask=mask)
            elif function_name == F_REMOVE:
                return morph.remove(pixel_data, mask, count)
            elif function_name == F_SHRINK:
                return morph.binary_shrink(pixel_data, count)
            elif function_name == F_SKELPE:
                return morph.skeletonize(
                        pixel_data, mask,
                        scind.distance_transform_edt(pixel_data) *
                        poisson_equation(pixel_data))
            elif function_name == F_SPUR:
                return morph.spur(pixel_data, mask, count)
            elif function_name == F_THICKEN:
                return morph.thicken(pixel_data, mask, count)
            elif function_name == F_THIN:
                return morph.thin(pixel_data, mask, count)
            elif function_name == F_VBREAK:
                return morph.vbreak(pixel_data, mask)
            else:
                raise NotImplementedError("Unimplemented morphological function: %s" %
                                          function_name)
            return pixel_data