300 lines
7.8 KiB
Python
300 lines
7.8 KiB
Python
import cv2
|
|
import numpy as np
|
|
|
|
|
|
def draw_rect(im, cords, color = None):
|
|
"""Draw the rectangle on the image
|
|
|
|
Parameters
|
|
----------
|
|
|
|
im : numpy.ndarray
|
|
numpy image
|
|
|
|
cords: numpy.ndarray
|
|
Numpy array containing bounding boxes of shape `N X 4` where N is the
|
|
number of bounding boxes and the bounding boxes are represented in the
|
|
format `x1 y1 x2 y2`
|
|
|
|
Returns
|
|
-------
|
|
|
|
numpy.ndarray
|
|
numpy image with bounding boxes drawn on it
|
|
|
|
"""
|
|
|
|
im = im.copy()
|
|
|
|
cords = cords[:,:4]
|
|
cords = cords.reshape(-1,4)
|
|
if not color:
|
|
color = [255,255,255]
|
|
for cord in cords:
|
|
|
|
pt1, pt2 = (cord[0], cord[1]) , (cord[2], cord[3])
|
|
|
|
pt1 = int(pt1[0]), int(pt1[1])
|
|
pt2 = int(pt2[0]), int(pt2[1])
|
|
|
|
im = cv2.rectangle(im.copy(), pt1, pt2, color, int(max(im.shape[:2])/200))
|
|
return im
|
|
|
|
def bbox_area(bbox):
|
|
return (bbox[:,2] - bbox[:,0])*(bbox[:,3] - bbox[:,1])
|
|
|
|
def clip_box(bbox, clip_box, alpha):
|
|
"""Clip the bounding boxes to the borders of an image
|
|
|
|
Parameters
|
|
----------
|
|
|
|
bbox: numpy.ndarray
|
|
Numpy array containing bounding boxes of shape `N X 4` where N is the
|
|
number of bounding boxes and the bounding boxes are represented in the
|
|
format `x1 y1 x2 y2`
|
|
|
|
clip_box: numpy.ndarray
|
|
An array of shape (4,) specifying the diagonal co-ordinates of the image
|
|
The coordinates are represented in the format `x1 y1 x2 y2`
|
|
|
|
alpha: float
|
|
If the fraction of a bounding box left in the image after being clipped is
|
|
less than `alpha` the bounding box is dropped.
|
|
|
|
Returns
|
|
-------
|
|
|
|
numpy.ndarray
|
|
Numpy array containing **clipped** bounding boxes of shape `N X 4` where N is the
|
|
number of bounding boxes left are being clipped and the bounding boxes are represented in the
|
|
format `x1 y1 x2 y2`
|
|
|
|
"""
|
|
ar_ = (bbox_area(bbox))
|
|
x_min = np.maximum(bbox[:,0], clip_box[0]).reshape(-1,1)
|
|
y_min = np.maximum(bbox[:,1], clip_box[1]).reshape(-1,1)
|
|
x_max = np.minimum(bbox[:,2], clip_box[2]).reshape(-1,1)
|
|
y_max = np.minimum(bbox[:,3], clip_box[3]).reshape(-1,1)
|
|
|
|
bbox = np.hstack((x_min, y_min, x_max, y_max, bbox[:,4:]))
|
|
|
|
delta_area = ((ar_ - bbox_area(bbox))/ar_)
|
|
|
|
mask = (delta_area < (1 - alpha)).astype(int)
|
|
|
|
bbox = bbox[mask == 1,:]
|
|
|
|
|
|
return bbox
|
|
|
|
|
|
def rotate_im(image, angle):
|
|
"""Rotate the image.
|
|
|
|
Rotate the image such that the rotated image is enclosed inside the tightest
|
|
rectangle. The area not occupied by the pixels of the original image is colored
|
|
black.
|
|
|
|
Parameters
|
|
----------
|
|
|
|
image : numpy.ndarray
|
|
numpy image
|
|
|
|
angle : float
|
|
angle by which the image is to be rotated
|
|
|
|
Returns
|
|
-------
|
|
|
|
numpy.ndarray
|
|
Rotated Image
|
|
|
|
"""
|
|
# grab the dimensions of the image and then determine the
|
|
# centre
|
|
(h, w) = image.shape[:2]
|
|
(cX, cY) = (w // 2, h // 2)
|
|
|
|
# grab the rotation matrix (applying the negative of the
|
|
# angle to rotate clockwise), then grab the sine and cosine
|
|
# (i.e., the rotation components of the matrix)
|
|
M = cv2.getRotationMatrix2D((cX, cY), angle, 1.0)
|
|
cos = np.abs(M[0, 0])
|
|
sin = np.abs(M[0, 1])
|
|
|
|
# compute the new bounding dimensions of the image
|
|
nW = int((h * sin) + (w * cos))
|
|
nH = int((h * cos) + (w * sin))
|
|
|
|
# adjust the rotation matrix to take into account translation
|
|
M[0, 2] += (nW / 2) - cX
|
|
M[1, 2] += (nH / 2) - cY
|
|
|
|
# perform the actual rotation and return the image
|
|
image = cv2.warpAffine(image, M, (nW, nH))
|
|
|
|
# image = cv2.resize(image, (w,h))
|
|
return image
|
|
|
|
def get_corners(bboxes):
|
|
|
|
"""Get corners of bounding boxes
|
|
|
|
Parameters
|
|
----------
|
|
|
|
bboxes: numpy.ndarray
|
|
Numpy array containing bounding boxes of shape `N X 4` where N is the
|
|
number of bounding boxes and the bounding boxes are represented in the
|
|
format `x1 y1 x2 y2`
|
|
|
|
returns
|
|
-------
|
|
|
|
numpy.ndarray
|
|
Numpy array of shape `N x 8` containing N bounding boxes each described by their
|
|
corner co-ordinates `x1 y1 x2 y2 x3 y3 x4 y4`
|
|
|
|
"""
|
|
width = (bboxes[:,2] - bboxes[:,0]).reshape(-1,1)
|
|
height = (bboxes[:,3] - bboxes[:,1]).reshape(-1,1)
|
|
|
|
x1 = bboxes[:,0].reshape(-1,1)
|
|
y1 = bboxes[:,1].reshape(-1,1)
|
|
|
|
x2 = x1 + width
|
|
y2 = y1
|
|
|
|
x3 = x1
|
|
y3 = y1 + height
|
|
|
|
x4 = bboxes[:,2].reshape(-1,1)
|
|
y4 = bboxes[:,3].reshape(-1,1)
|
|
|
|
corners = np.hstack((x1,y1,x2,y2,x3,y3,x4,y4))
|
|
|
|
return corners
|
|
|
|
def rotate_box(corners,angle, cx, cy, h, w):
|
|
|
|
"""Rotate the bounding box.
|
|
|
|
|
|
Parameters
|
|
----------
|
|
|
|
corners : numpy.ndarray
|
|
Numpy array of shape `N x 8` containing N bounding boxes each described by their
|
|
corner co-ordinates `x1 y1 x2 y2 x3 y3 x4 y4`
|
|
|
|
angle : float
|
|
angle by which the image is to be rotated
|
|
|
|
cx : int
|
|
x coordinate of the center of image (about which the box will be rotated)
|
|
|
|
cy : int
|
|
y coordinate of the center of image (about which the box will be rotated)
|
|
|
|
h : int
|
|
height of the image
|
|
|
|
w : int
|
|
width of the image
|
|
|
|
Returns
|
|
-------
|
|
|
|
numpy.ndarray
|
|
Numpy array of shape `N x 8` containing N rotated bounding boxes each described by their
|
|
corner co-ordinates `x1 y1 x2 y2 x3 y3 x4 y4`
|
|
"""
|
|
|
|
corners = corners.reshape(-1,2)
|
|
corners = np.hstack((corners, np.ones((corners.shape[0],1), dtype = type(corners[0][0]))))
|
|
|
|
M = cv2.getRotationMatrix2D((cx, cy), angle, 1.0)
|
|
|
|
|
|
cos = np.abs(M[0, 0])
|
|
sin = np.abs(M[0, 1])
|
|
|
|
nW = int((h * sin) + (w * cos))
|
|
nH = int((h * cos) + (w * sin))
|
|
# adjust the rotation matrix to take into account translation
|
|
M[0, 2] += (nW / 2) - cx
|
|
M[1, 2] += (nH / 2) - cy
|
|
# Prepare the vector to be transformed
|
|
calculated = np.dot(M,corners.T).T
|
|
|
|
calculated = calculated.reshape(-1,8)
|
|
|
|
return calculated
|
|
|
|
|
|
def get_enclosing_box(corners):
|
|
"""Get an enclosing box for ratated corners of a bounding box
|
|
|
|
Parameters
|
|
----------
|
|
|
|
corners : numpy.ndarray
|
|
Numpy array of shape `N x 8` containing N bounding boxes each described by their
|
|
corner co-ordinates `x1 y1 x2 y2 x3 y3 x4 y4`
|
|
|
|
Returns
|
|
-------
|
|
|
|
numpy.ndarray
|
|
Numpy array containing enclosing bounding boxes of shape `N X 4` where N is the
|
|
number of bounding boxes and the bounding boxes are represented in the
|
|
format `x1 y1 x2 y2`
|
|
|
|
"""
|
|
x_ = corners[:,[0,2,4,6]]
|
|
y_ = corners[:,[1,3,5,7]]
|
|
|
|
xmin = np.min(x_,1).reshape(-1,1)
|
|
ymin = np.min(y_,1).reshape(-1,1)
|
|
xmax = np.max(x_,1).reshape(-1,1)
|
|
ymax = np.max(y_,1).reshape(-1,1)
|
|
|
|
final = np.hstack((xmin, ymin, xmax, ymax,corners[:,8:]))
|
|
|
|
return final
|
|
|
|
|
|
def letterbox_image(img, inp_dim):
|
|
'''resize image with unchanged aspect ratio using padding
|
|
|
|
Parameters
|
|
----------
|
|
|
|
img : numpy.ndarray
|
|
Image
|
|
|
|
inp_dim: tuple(int)
|
|
shape of the reszied image
|
|
|
|
Returns
|
|
-------
|
|
|
|
numpy.ndarray:
|
|
Resized image
|
|
|
|
'''
|
|
|
|
inp_dim = (inp_dim, inp_dim)
|
|
img_w, img_h = img.shape[1], img.shape[0]
|
|
w, h = inp_dim
|
|
new_w = int(img_w * min(w/img_w, h/img_h))
|
|
new_h = int(img_h * min(w/img_w, h/img_h))
|
|
resized_image = cv2.resize(img, (new_w,new_h))
|
|
|
|
canvas = np.full((inp_dim[1], inp_dim[0], 3), 0)
|
|
|
|
canvas[(h-new_h)//2:(h-new_h)//2 + new_h,(w-new_w)//2:(w-new_w)//2 + new_w, :] = resized_image
|
|
|
|
return canvas |