4
A Bounding Box object and assorted utilities , subclassed from a numpy array
10
class BBox(N.ndarray):
12
A Bounding Box object:
14
Takes Data as an array. Data is any python sequence that can be turned into a
15
2x2 numpy array of floats:
20
It is a subclass of numpy.ndarray, so for the most part it can be used as
21
an array, and arrays that fit the above description can be used in its place.
23
Usually created by the factory functions:
32
def __new__(subtype, data):
34
Takes Data as an array. Data is any python sequence that can be turned into a
35
2x2 numpy array of floats:
40
You don't usually call this directly. BBox objects are created with the factory functions:
49
arr = N.array(data, N.float)
51
if arr[0,0] > arr[1,0] or arr[0,1] > arr[1,1]:
52
# note: zero sized BB OK.
53
raise ValueError("BBox values not aligned: \n minimum values must be less that maximum values")
54
return N.ndarray.__new__(subtype, shape=arr.shape, dtype=arr.dtype, buffer=arr)
56
def Overlaps(self, BB):
60
Tests if the given Bounding Box overlaps with this one.
61
Returns True is the Bounding boxes overlap, False otherwise
62
If they are just touching, returns True
65
if N.isinf(self).all() or N.isinf(BB).all():
67
if ( (self[1,0] >= BB[0,0]) and (self[0,0] <= BB[1,0]) and
68
(self[1,1] >= BB[0,1]) and (self[0,1] <= BB[1,1]) ):
77
Tests if the given Bounding Box is entirely inside this one.
79
Returns True if it is entirely inside, or touching the
82
Returns False otherwise
84
if ( (BB[0,0] >= self[0,0]) and (BB[1,0] <= self[1,0]) and
85
(BB[0,1] >= self[0,1]) and (BB[1,1] <= self[1,1]) ):
90
def PointInside(self, Point):
94
Tests if the given Point is entirely inside this one.
96
Returns True if it is entirely inside, or touching the
99
Returns False otherwise
101
Point is any length-2 sequence (tuple, list, array) or two numbers
103
if Point[0] >= self[0,0] and \
104
Point[0] <= self[1,0] and \
105
Point[1] <= self[1,1] and \
106
Point[1] >= self[0,1]:
113
Joins this bounding box with the one passed in, maybe making this one bigger
118
elif N.isnan(BB).all(): ## BB may be a regular array, so I can't use IsNull
121
if BB[0,0] < self[0,0]: self[0,0] = BB[0,0]
122
if BB[0,1] < self[0,1]: self[0,1] = BB[0,1]
123
if BB[1,0] > self[1,0]: self[1,0] = BB[1,0]
124
if BB[1,1] > self[1,1]: self[1,1] = BB[1,1]
129
return N.isnan(self).all()
131
## fixme: it would be nice to add setter, too.
134
Left = property(_getLeft)
137
Right = property(_getRight)
138
def _getBottom(self):
140
Bottom = property(_getBottom)
143
Top = property(_getTop)
146
return self[1,0] - self[0,0]
147
Width = property(_getWidth)
149
def _getHeight(self):
150
return self[1,1] - self[0,1]
151
Height = property(_getHeight)
153
def _getCenter(self):
154
return self.sum(0) / 2.0
155
Center = property(_getCenter)
156
### This could be used for a make BB from a bunch of BBs
158
#~ def _getboundingbox(bboxarray): # lrk: added this
159
#~ # returns the bounding box of a bunch of bounding boxes
160
#~ upperleft = N.minimum.reduce(bboxarray[:,0])
161
#~ lowerright = N.maximum.reduce(bboxarray[:,1])
162
#~ return N.array((upperleft, lowerright), N.float)
163
#~ _getboundingbox = staticmethod(_getboundingbox)
166
## Save the ndarray __eq__ for internal use.
167
Array__eq__ = N.ndarray.__eq__
168
def __eq__(self, BB):
170
__eq__(BB) The equality operator
172
A == B if and only if all the entries are the same
175
if self.IsNull() and N.isnan(BB).all(): ## BB may be a regular array, so I can't use IsNull
178
return self.Array__eq__(BB).all()
183
returns a BBox object.
185
If object is a BBox, it is returned unaltered
187
If object is a numpy array, a BBox object is returned that shares a
188
view of the data with that array. The numpy array should be of the correct
189
format: a 2x2 numpy array of floats:
196
if isinstance(data, BBox):
198
arr = N.asarray(data, N.float)
199
return N.ndarray.__new__(BBox, shape=arr.shape, dtype=arr.dtype, buffer=arr)
201
def fromPoints(Points):
205
reruns the bounding box of the set of points in Points. Points can
206
be any python object that can be turned into a numpy NX2 array of Floats.
208
If a single point is passed in, a zero-size Bounding Box is returned.
211
Points = N.asarray(Points, N.float).reshape(-1,2)
213
arr = N.vstack( (Points.min(0), Points.max(0)) )
214
return N.ndarray.__new__(BBox, shape=arr.shape, dtype=arr.dtype, buffer=arr)
216
def fromBBArray(BBarray):
218
Builds a BBox object from an array of Bounding Boxes.
219
The resulting Bounding Box encompases all the included BBs.
221
The BBarray is in the shape: (Nx2x2) where BBarray[n] is a 2x2 array that represents a BBox
224
#upperleft = N.minimum.reduce(BBarray[:,0])
225
#lowerright = N.maximum.reduce(BBarray[:,1])
227
# BBarray = N.asarray(BBarray, N.float).reshape(-1,2)
228
# arr = N.vstack( (BBarray.min(0), BBarray.max(0)) )
229
BBarray = N.asarray(BBarray, N.float).reshape(-1,2,2)
230
arr = N.vstack( (BBarray[:,0,:].min(0), BBarray[:,1,:].max(0)) )
232
#return asBBox( (upperleft, lowerright) ) * 2
236
Returns a BBox object with all NaN entries.
238
This represents a Null BB box;
240
BB merged with it will return BB.
242
Nothing is inside it.
246
arr = N.array(((N.nan, N.nan),(N.nan, N.nan)), N.float)
247
return N.ndarray.__new__(BBox, shape=arr.shape, dtype=arr.dtype, buffer=arr)
251
Returns a BBox object with all -inf and inf entries
255
arr = N.array(((-N.inf, -N.inf),(N.inf, N.inf)), N.float)
256
return N.ndarray.__new__(BBox, shape=arr.shape, dtype=arr.dtype, buffer=arr)
258
class RectBBox(BBox):
260
subclass of a BBox that can be used for a rotated Rectangle
262
contributed by MArco Oster (marco.oster@bioquant.uni-heidelberg.de)
266
def __new__(self, data, edges=None):
267
return BBox.__new__(self, data)
269
def __init__(self, data, edges=None):
270
''' assume edgepoints are ordered such you can walk along all edges with left rotation sense
280
self.edges = np.asarray(edges)
282
print "new rectbbox created"
285
def ac_leftOf_ab(self, a, b, c):
286
ab = np.array(b) - np.array(a)
287
ac = np.array(c) - np.array(a)
289
return (ac[0]*ab[1] - ac[1]*ab[0]) <= 0
291
def PointInside(self, point):
292
print "point inside called"
294
for edge in xrange(4):
295
if self.ac_leftOf_ab(self.edges[edge],
296
self.edges[(edge+1)%4],
b'\\ No newline at end of file'