在使用Python的单色图像的blob周围的矩形边界框

Rectangular bounding box around blobs in a monochrome image using python

我有一些单色图像(黑白而不是灰度),有一些形状怪异的物体。我尝试使用python27、pil、scipy&numpy和以下方法提取每个对象:

  • 在每个连接的对象周围放置一个边界框
  • "提取"每个对象作为数组-每个对象/边界框
  • 我已经看过http://www.scipy.org/cookbook/drawinder和http://scikits-image.org/docs/dev/auto_examples/plot_outlines.html,这些都可以工作,但我特别希望边界框是矩形的,以确保边界框中包含任何"稍微断开的"位。理想情况下,为了处理断开的位(例如左下角的斑点),我需要某种阈值控制。关于什么工具箱最适合这个有什么想法吗?

    unbounded image。氧化镁


    这使用了JoeKington的find_paws函数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    import numpy as np
    import scipy.ndimage as ndimage
    import scipy.spatial as spatial
    import scipy.misc as misc
    import matplotlib.pyplot as plt
    import matplotlib.patches as patches

    class BBox(object):
        def __init__(self, x1, y1, x2, y2):
            '''
            (x1, y1) is the upper left corner,
            (x2, y2) is the lower right corner,
            with (0, 0) being in the upper left corner.
            '''

            if x1 > x2: x1, x2 = x2, x1
            if y1 > y2: y1, y2 = y2, y1
            self.x1 = x1
            self.y1 = y1
            self.x2 = x2
            self.y2 = y2
        def taxicab_diagonal(self):
            '''
            Return the taxicab distance from (x1,y1) to (x2,y2)
            '''

            return self.x2 - self.x1 + self.y2 - self.y1
        def overlaps(self, other):
            '''
            Return True iff self and other overlap.
            '''

            return not ((self.x1 > other.x2)
                        or (self.x2 < other.x1)
                        or (self.y1 > other.y2)
                        or (self.y2 < other.y1))
        def __eq__(self, other):
            return (self.x1 == other.x1
                    and self.y1 == other.y1
                    and self.x2 == other.x2
                    and self.y2 == other.y2)

    def find_paws(data, smooth_radius = 5, threshold = 0.0001):
        # https://stackoverflow.com/questions/4087919/how-can-i-improve-my-paw-detection
       """Detects and isolates contiguous regions in the input array"""
        # Blur the input data a bit so the paws have a continous footprint
        data = ndimage.uniform_filter(data, smooth_radius)
        # Threshold the blurred data (this needs to be a bit > 0 due to the blur)
        thresh = data > threshold
        # Fill any interior holes in the paws to get cleaner regions...
        filled = ndimage.morphology.binary_fill_holes(thresh)
        # Label each contiguous paw
        coded_paws, num_paws = ndimage.label(filled)
        # Isolate the extent of each paw
        # find_objects returns a list of 2-tuples: (slice(...), slice(...))
        # which represents a rectangular box around the object
        data_slices = ndimage.find_objects(coded_paws)
        return data_slices

    def slice_to_bbox(slices):
        for s in slices:
            dy, dx = s[:2]
            yield BBox(dx.start, dy.start, dx.stop+1, dy.stop+1)

    def remove_overlaps(bboxes):
        '''
        Return a set of BBoxes which contain the given BBoxes.
        When two BBoxes overlap, replace both with the minimal BBox that contains both.
        '''

        # list upper left and lower right corners of the Bboxes
        corners = []

        # list upper left corners of the Bboxes
        ulcorners = []

        # dict mapping corners to Bboxes.
        bbox_map = {}

        for bbox in bboxes:
            ul = (bbox.x1, bbox.y1)
            lr = (bbox.x2, bbox.y2)
            bbox_map[ul] = bbox
            bbox_map[lr] = bbox
            ulcorners.append(ul)
            corners.append(ul)
            corners.append(lr)        

        # Use a KDTree so we can find corners that are nearby efficiently.
        tree = spatial.KDTree(corners)
        new_corners = []
        for corner in ulcorners:
            bbox = bbox_map[corner]
            # Find all points which are within a taxicab distance of corner
            indices = tree.query_ball_point(
                corner, bbox_map[corner].taxicab_diagonal(), p = 1)
            for near_corner in tree.data[indices]:
                near_bbox = bbox_map[tuple(near_corner)]
                if bbox != near_bbox and bbox.overlaps(near_bbox):
                    # Expand both bboxes.
                    # Since we mutate the bbox, all references to this bbox in
                    # bbox_map are updated simultaneously.
                    bbox.x1 = near_bbox.x1 = min(bbox.x1, near_bbox.x1)
                    bbox.y1 = near_bbox.y1 = min(bbox.y1, near_bbox.y1)
                    bbox.x2 = near_bbox.x2 = max(bbox.x2, near_bbox.x2)
                    bbox.y2 = near_bbox.y2 = max(bbox.y2, near_bbox.y2)
        return set(bbox_map.values())

    if __name__ == '__main__':
        fig = plt.figure()
        ax = fig.add_subplot(111)

        data = misc.imread('image.png')
        im = ax.imshow(data)    
        data_slices = find_paws(255-data, smooth_radius = 20, threshold = 22)

        bboxes = remove_overlaps(slice_to_bbox(data_slices))
        for bbox in bboxes:
            xwidth = bbox.x2 - bbox.x1
            ywidth = bbox.y2 - bbox.y1
            p = patches.Rectangle((bbox.x1, bbox.y1), xwidth, ywidth,
                                  fc = 'none', ec = 'red')
            ax.add_patch(p)

        plt.show()

    产量enter image description here