使用python在单色图像中围绕斑点的矩形​​边界框 [英] Rectangular bounding box around blobs in a monochrome image using python

查看:26
本文介绍了使用python在单色图像中围绕斑点的矩形​​边界框的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一些单色图像(黑白而非灰度),其中包含一些形状怪异的对象.我正在尝试使用 python27、PIL、scipy & 提取每个对象.numpy 和以下方法:

  1. 在每个连接的对象周围放置一个边界框
  2. 提取"每个对象作为数组 - 对于每个对象/边界框

我看过.

将 numpy 导入为 np将 scipy.ndimage 导入为 ndimage导入 scipy.spatial 作为空间将 scipy.misc 导入为 misc导入 matplotlib.pyplot 作为 plt导入 matplotlib.patches 作为补丁类 BBox(对象):def __init__(self, x1, y1, x2, y2):'''(x1, y1) 是左上角,(x2, y2) 是右下角,(0, 0) 位于左上角.'''如果 x1 >x2: x1, x2 = x2, x1如果 y1 >y2: y1, y2 = y2, y1self.x1 = x1self.y1 = y1self.x2 = x2self.y2 = y2def出租车对角线(自我):'''返回从 (x1,y1) 到 (x2,y2) 的出租车距离'''返回 self.x2 - self.x1 + self.y2 - self.y1定义重叠(自我,其他):'''如果自身和其他重叠,则返回 True.'''return not ((self.x1 > other.x2)或 (self.x2 < other.x1)或 (self.y1 > other.y2)或 (self.y2 < other.y1))def __eq __(自己,其他):返回(self.x1 == other.x1和 self.y1 == other.y1和 self.x2 == other.x2和 self.y2 == other.y2)def find_paws(数据,smooth_radius = 5,阈值 = 0.0001):# https://stackoverflow.com/questions/4087919/how-can-i-improve-my-paw-detection"""检测并隔离输入数组中的连续区域"""# 稍微模糊输入数据,使爪子有连续的足迹数据 = ndimage.uniform_filter(数据,smooth_radius)# 模糊数据的阈值(由于模糊,这需要有点> 0)阈值 = 数据 >临界点# 填充爪子上的任何内部孔以获得更清洁的区域...填充 = ndimage.morphology.binary_fill_holes(thresh)# 标记每个连续的爪子coded_pa​​ws, num_paws = ndimage.label(已填充)# 隔离每个爪子的范围# find_objects 返回一个二元组列表:(slice(...), slice(...))# 代表对象周围的矩形框data_slices = ndimage.find_objects(coded_pa​​ws)返回数据切片def slice_to_bbox(切片):对于切片中的 s:dy, dx = s[:2]产量 BBox(dx.start, dy.start, dx.stop+1, dy.stop+1)def remove_overlaps(bboxes):'''返回一组包含给定 BBox 的 BBox.当两个 BBox 重叠时,将两者替换为包含两者的最小 BBox.'''# 列出Bboxes的左上角和右下角角= []# 列出 Bbox 的左上角ulcorners = []# dict 将角映射到 Bbox.bbox_map = {}对于 bboxes 中的 bbox:ul = (bbox.x1, bbox.y1)lr = (bbox.x2, bbox.y2)bbox_map[ul] = bboxbbox_map[lr] = bboxulcorners.append(ul)角.附加(ul)角.附加(lr)# 使用 KDTree 以便我们可以有效地找到附近的角落.树 = 空间.KDTree(角落)new_corners = []对于 ulcorners 的角落:bbox = bbox_map[角]# 找出拐角处出租车距离内的所有点指数 = tree.query_ball_point(角,bbox_map[角].taxicab_diagonal(),p = 1)对于 tree.data[indices] 中的 Near_corner:Near_bbox = bbox_map[元组(near_corner)]如果 bbox != near_bbox 和 bbox.overlaps(near_bbox):# 展开两个 bbox.# 由于我们改变了 bbox,所有对这个 bbox 的引用都在# bbox_map 同步更新.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 = 最大值(bbox.x2,near_bbox.x2)bbox.y2 = near_bbox.y2 = 最大值(bbox.y2,near_bbox.y2)返回集(bbox_map.values())如果 __name__ == '__main__':fig = plt.figure()ax = fig.add_subplot(111)data = misc.imread('image.png')im = ax.imshow(数据)data_slices = find_paws(255-data,smooth_radius = 20,阈值 = 22)bboxes = remove_overlaps(slice_to_bbox(data_slices))对于 bboxes 中的 bbox:xwidth = bbox.x2 - bbox.x1ywidth = bbox.y2 - bbox.y1p = patch.Rectangle((bbox.x1, bbox.y1), xwidth, ywidth,fc = '无', ec = '红色')ax.add_patch(p)plt.show()

收益

I have a few monochrome images (black and white not greyscale) with a few weirdly shaped objects. I'm trying to extract each object using python27, PIL, scipy & numpy and the following method:

  1. Fit a bounding box around each joined-up object
  2. "Extract" each object as an array - for each object / bounding box

I've had a look at http://www.scipy.org/Cookbook/Watershed and http://scikits-image.org/docs/dev/auto_examples/plot_contours.html and these do work, but I'm particularly keen to have the bounding box be rectangular to make sure that any "slightly disconnected" bits get included in the bounding box. Ideally to deal with the disconnected bits (e.g. bottom left blobs) I'd have some kind of threshold control. Any ideas on what toolbox would best suit this?

解决方案

This uses Joe Kington's find_paws function.

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()

yields

这篇关于使用python在单色图像中围绕斑点的矩形​​边界框的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆