def similarity_transform(txy=(0, 0), r=0, s=1): """Return a 3x3 similarity transformation with translation tuple txy=(x,y), rotation r (radians, scale=s""" assert istuple(txy) and len(txy) == 2 and isnumber(r) and isnumber( s), "Invalid input" R = np.mat([[np.cos(r), -np.sin(r), 0], [np.sin(r), np.cos(r), 0], [0, 0, 1]]) S = np.mat([[s, 0, 0], [0, s, 0], [0, 0, 1]]) T = np.mat([[0, 0, txy[0]], [0, 0, txy[1]], [0, 0, 0]]) return S * R + T # composition
def affine_transform(txy=(0, 0), r=0, sx=1, sy=1, kx=0, ky=0): """Compose and return a 3x3 affine transformation for translation txy=(0,0), rotation r (radians), scalex=sx, scaley=sy, shearx=kx, sheary=ky""" assert istuple(txy) and len(txy) == 2 and isnumber(r) and isnumber( sx) and isnumber(sy) and isnumber(kx) and isnumber(ky), "Invalid input" R = np.mat([[np.cos(r), -np.sin(r), 0], [np.sin(r), np.cos(r), 0], [0, 0, 1]]) S = np.mat([[sx, 0, 0], [0, sy, 0], [0, 0, 1]]) K = np.mat([[1, ky, 0], [kx, 1, 0], [0, 0, 1]]) T = np.mat([[0, 0, txy[0]], [0, 0, txy[1]], [0, 0, 0]]) return K * S * R + T # composition
def similarity_transform_2x3(c=(0, 0), r=0, s=1): """Return a 2x3 similarity transform with rotation r (radians), scale s and origin c=(x,y)""" assert istuple(c) and len(c) == 2 and isnumber(r) and isnumber( s), "Invalid input" deg = r * 180. / math.pi a = s * np.cos(r) b = s * np.sin(r) (x, y) = (c[0], c[1]) return np.array([[a, b, (1 - a) * x - b * y], [-b, a, b * x + (1 - a) * y]])
def hasoverlap(self, img=None, width=None, height=None): """Does the bounding box intersect with the provided image rectangle?""" if img is not None: assert isnumpy(img), "Invalid image input" (width, height) = (img.shape[1], img.shape[0]) else: assert width is not None and height is not None, "Invalid width and height - both must be provided" assert isnumber(width) and isnumber( height), "Invalid width and height - both must be numbers" return self.area_of_intersection( BoundingBox(xmin=0, ymin=0, width=width, height=height)) > 0
def imclip(self, img=None, width=None, height=None): """Clip bounding box to image rectangle [0,0,width,height] or img.shape=(width, height) and, throw an exception on an invalid box""" if img is not None: assert isnumpy(img), "Invalid numpy image input" (height, width) = (img.shape[0], img.shape[1]) else: assert width is not None and height is not None, "Invalid width and height - both must be provided" assert isnumber(width) and isnumber( height), "Invalid width and height - both must be numbers" return self.intersection(BoundingBox(xmin=0, ymin=0, width=width, height=height), strict=True)
def rescale(self, scale): """Scale ellipse by scale factor""" assert isnumber(scale), "Invalid input" self._major *= scale self._minor *= scale self._xcenter *= scale self._ycenter *= scale return self
def flipud(self, img=None, height=None): """Flip the box up/down consistent with flipud of the provided img (or consistent with the image height)""" if img is not None: assert isnumpy(img), "Invalid numpy image input" height = img.shape[0] else: assert isnumber(height), "Invalid height" (x, y, w, h) = self.xywh() self._ymin = height - self._ymax self._ymax = self._ymin + h return self
def fliplr(self, img=None, width=None): """Flip the box left/right consistent with fliplr of the provided img (or consistent with the image width)""" if img is not None: assert isnumpy(img), "Invalid numpy image input" width = img.shape[1] else: assert isnumber(width), "Invalid width" (x, y, w, h) = self.xywh() self._xmin = width - self._xmax self._xmax = self._xmin + w return self
def dilate(self, scale=1): """Change scale of bounding box keeping centroid constant""" assert isnumber(scale), "Invalid input" w = self.width() h = self.height() c = self.centroid() old_x = self._xmin old_y = self._ymin new_x = (float(w) / 2.0) * scale new_y = (float(h) / 2.0) * scale self._xmin = c[0] - new_x self._ymin = c[1] - new_y self._xmax = c[0] + new_x self._ymax = c[1] + new_y return self
def __init__(self, xmin=None, ymin=None, xmax=None, ymax=None, centroid=None, xcentroid=None, ycentroid=None, width=None, height=None, mask=None, xywh=None, ulbr=None): if xmin is not None and ymin is not None and xmax is not None and ymax is not None: if not (isnumber(xmin) and isnumber(ymin) and isnumber(xmax) and isnumber(ymax)): raise ValueError('Box coordinates must be integers or floats') self._xmin = float(xmin) self._ymin = float(ymin) self._xmax = float(xmax) self._ymax = float(ymax) elif xmin is not None and ymin is not None and width is not None and height is not None: if not (isnumber(xmin) and isnumber(ymin) and isnumber(width) and isnumber(height)): raise ValueError('Box coordinates must be integers or floats') self._xmin = float(xmin) self._ymin = float(ymin) self._xmax = self._xmin + float(width) self._ymax = self._ymin + float(height) elif centroid is not None and width is not None and height is not None: if not (len(centroid) == 2 and isnumber(centroid[0]) and isnumber( centroid[1]) and isnumber(width) and isnumber(height)): raise ValueError('Invalid box coordinates') self._xmin = float(centroid[0]) - float(width) / 2.0 self._ymin = float(centroid[1]) - float(height) / 2.0 self._xmax = float(centroid[0]) + float(width) / 2.0 self._ymax = float(centroid[1]) + float(height) / 2.0 elif xcentroid is not None and ycentroid is not None and width is not None and height is not None: if not (isnumber(xcentroid) and isnumber(ycentroid) and isnumber(width) and isnumber(height)): raise ValueError('Box coordinates must be integers or floats') self._xmin = float(xcentroid) - (float(width) / 2.0) self._ymin = float(ycentroid) - (float(height) / 2.0) self._xmax = float(xcentroid) + (float(width) / 2.0) self._ymax = float(ycentroid) + (float(height) / 2.0) elif xywh is not None: self.xywh(xywh) elif ulbr is not None: self.ulbr(ulbr) elif mask is not None: # Bounding rectangle of non-zero pixels in a binary mask image if not isnumpy(mask) or np.sum(mask) == 0: raise ValueError( 'Mask input must be numpy array with at least one non-zero entry' ) imx = np.sum(mask, axis=0) imy = np.sum(mask, axis=1) self._xmin = np.argwhere(imx > 0)[0] self._ymin = np.argwhere(imy > 0)[0] self._xmax = np.argwhere(imx > 0)[-1] self._ymax = np.argwhere(imy > 0)[-1] else: raise ValueError('invalid constructor input')
def random_positive_semidefinite_matrix(N): """Return a randomly generated float64 positive semidefinite matrix of size NxN""" assert isnumber(N), "Invalid input" "" A = np.random.rand(N, N) return np.dot(A, A.transpose())