使用 opencv2 和 numpy 旋转图像及其边界框会在 45 度处产生更差的框
Rotating image with its bounding boxes yielding worse boxes at 45 degrees with opencv2 and numpy
我有一些代码,主要取自此 post 底部链接的各种来源,用 Python 编写,采用形状 [height, width]
和一些边界框的图像在 [x_min, y_min, x_max, y_max]
格式中,numpy 数组和 逆时针旋转图像及其边界框 。由于旋转后边界框变得更像“菱形”,即未与轴对齐,因此我执行一些计算以使其与轴对齐。此代码的目的是通过使用旋转数据(水平或垂直翻转很常见)在训练对象检测神经网络时执行数据增强。看起来其他角度的翻转对于图像分类很常见,没有边界框,但是当有框时,如何翻转框以及图像的资源相对sparse/niche。
似乎当我输入 45 度角时,我得到了一些不太“紧”的边界框,因为四个角的注释不是很好,而原始的接近完美。
下图是MS COCO 2014目标检测数据集中的第一张图片(训练图片),以及它的第一个边界框标注。我的代码如下:
import math
import cv2
import numpy as np
# angle assumed to be in degrees
# bbs a list of bounding boxes in x_min, y_min, x_max, y_max format
def rotateImageAndBoundingBoxes(im, bbs, angle):
h, w = im.shape[0], im.shape[1]
(cX, cY) = (w//2, h//2) # original image center
M = cv2.getRotationMatrix2D((cX, cY), angle, 1.0) # 2 by 3 rotation matrix
cos = np.abs(M[0, 0])
sin = np.abs(M[0, 1])
# compute the dimensions of the rotated image
nW = int((h * sin) + (w * cos))
nH = int((h * cos) + (w * sin))
# adjust the rotation matrix to take into account translation of the new centre
M[0, 2] += (nW / 2) - cX
M[1, 2] += (nH / 2) - cY
rotated_im = cv2.warpAffine(im, M, (nW, nH))
rotated_bbs = []
for bb in bbs:
# get the four rotated corners of the bounding box
vec1 = np.matmul(M, np.array([bb[0], bb[1], 1], dtype=np.float64)) # top left corner transformed
vec2 = np.matmul(M, np.array([bb[2], bb[1], 1], dtype=np.float64)) # top right corner transformed
vec3 = np.matmul(M, np.array([bb[0], bb[3], 1], dtype=np.float64)) # bottom left corner transformed
vec4 = np.matmul(M, np.array([bb[2], bb[3], 1], dtype=np.float64)) # bottom right corner transformed
x_vals = [vec1[0], vec2[0], vec3[0], vec4[0]]
y_vals = [vec1[1], vec2[1], vec3[1], vec4[1]]
x_min = math.ceil(np.min(x_vals))
x_max = math.floor(np.max(x_vals))
y_min = math.ceil(np.min(y_vals))
y_max = math.floor(np.max(y_vals))
bb = [x_min, y_min, x_max, y_max]
rotated_bbs.append(bb)
// my function to resize image and bbs to the original image size
rotated_im, rotated_bbs = resizeImageAndBoxes(rotated_im, w, h, rotated_bbs)
return rotated_im, rotated_bbs
好的边界框看起来像:
不太好的边界框看起来像:
我正在尝试确定这是我的代码错误,还是预期的行为?看起来这个问题在 pi/2 弧度(90 度)的整数倍时不太明显,但我想在任何旋转角度实现紧密的边界框。任何见解都表示赞赏。
来源:
[打开 CV2 文档] https://docs.opencv.org/3.4/da/d54/group__imgproc__transform.html#gafbbc470ce83812914a70abfb604f4326
[数据扩充讨论]
https://blog.paperspace.com/data-augmentation-for-object-detection-rotation-and-shearing/
[二维绕任意点旋转的数学]
https://math.stackexchange.com/questions/2093314/rotation-matrix-of-rotation-around-a-point-other-than-the-origin
根据评论,这在很大程度上似乎是预期的行为。对于这个问题,我确实有一种 hacky 解决方案,您可以在其中编写一个函数,如
# assuming box coords = [x_min, y_min, x_max, y_max]
def cropBoxByPercentage(box_coords, image_width, image_height, x_percentage=0.05, y_percentage=0.05):
box_xmin = box_coords[0]
box_ymin = box_coords[1]
box_xmax = box_coords[2]
box_ymax = box_coords[3]
box_width = box_xmax-box_xmin+1
box_height = box_ymax-box_ymin+1
dx = int(x_percentage * box_width)
dy = int(y_percentage * box_height)
box_xmin = max(0, box_xmin-dx)
box_xmax = min(image_width-1, box_xmax+dx)
box_ymin = max(0, box_ymax - dy)
box_ymax = min(image_height - 1, box_ymax + dy)
return np.array([box_xmin, box_xmax, box_ymin, box_ymax])
计算 x_percentage 和 y_percentage 可以使用固定值计算,也可以使用一些启发式计算。
我有一些代码,主要取自此 post 底部链接的各种来源,用 Python 编写,采用形状 [height, width]
和一些边界框的图像在 [x_min, y_min, x_max, y_max]
格式中,numpy 数组和 逆时针旋转图像及其边界框 。由于旋转后边界框变得更像“菱形”,即未与轴对齐,因此我执行一些计算以使其与轴对齐。此代码的目的是通过使用旋转数据(水平或垂直翻转很常见)在训练对象检测神经网络时执行数据增强。看起来其他角度的翻转对于图像分类很常见,没有边界框,但是当有框时,如何翻转框以及图像的资源相对sparse/niche。
似乎当我输入 45 度角时,我得到了一些不太“紧”的边界框,因为四个角的注释不是很好,而原始的接近完美。
下图是MS COCO 2014目标检测数据集中的第一张图片(训练图片),以及它的第一个边界框标注。我的代码如下:
import math
import cv2
import numpy as np
# angle assumed to be in degrees
# bbs a list of bounding boxes in x_min, y_min, x_max, y_max format
def rotateImageAndBoundingBoxes(im, bbs, angle):
h, w = im.shape[0], im.shape[1]
(cX, cY) = (w//2, h//2) # original image center
M = cv2.getRotationMatrix2D((cX, cY), angle, 1.0) # 2 by 3 rotation matrix
cos = np.abs(M[0, 0])
sin = np.abs(M[0, 1])
# compute the dimensions of the rotated image
nW = int((h * sin) + (w * cos))
nH = int((h * cos) + (w * sin))
# adjust the rotation matrix to take into account translation of the new centre
M[0, 2] += (nW / 2) - cX
M[1, 2] += (nH / 2) - cY
rotated_im = cv2.warpAffine(im, M, (nW, nH))
rotated_bbs = []
for bb in bbs:
# get the four rotated corners of the bounding box
vec1 = np.matmul(M, np.array([bb[0], bb[1], 1], dtype=np.float64)) # top left corner transformed
vec2 = np.matmul(M, np.array([bb[2], bb[1], 1], dtype=np.float64)) # top right corner transformed
vec3 = np.matmul(M, np.array([bb[0], bb[3], 1], dtype=np.float64)) # bottom left corner transformed
vec4 = np.matmul(M, np.array([bb[2], bb[3], 1], dtype=np.float64)) # bottom right corner transformed
x_vals = [vec1[0], vec2[0], vec3[0], vec4[0]]
y_vals = [vec1[1], vec2[1], vec3[1], vec4[1]]
x_min = math.ceil(np.min(x_vals))
x_max = math.floor(np.max(x_vals))
y_min = math.ceil(np.min(y_vals))
y_max = math.floor(np.max(y_vals))
bb = [x_min, y_min, x_max, y_max]
rotated_bbs.append(bb)
// my function to resize image and bbs to the original image size
rotated_im, rotated_bbs = resizeImageAndBoxes(rotated_im, w, h, rotated_bbs)
return rotated_im, rotated_bbs
好的边界框看起来像:
不太好的边界框看起来像:
我正在尝试确定这是我的代码错误,还是预期的行为?看起来这个问题在 pi/2 弧度(90 度)的整数倍时不太明显,但我想在任何旋转角度实现紧密的边界框。任何见解都表示赞赏。
来源: [打开 CV2 文档] https://docs.opencv.org/3.4/da/d54/group__imgproc__transform.html#gafbbc470ce83812914a70abfb604f4326
[数据扩充讨论] https://blog.paperspace.com/data-augmentation-for-object-detection-rotation-and-shearing/
[二维绕任意点旋转的数学] https://math.stackexchange.com/questions/2093314/rotation-matrix-of-rotation-around-a-point-other-than-the-origin
根据评论,这在很大程度上似乎是预期的行为。对于这个问题,我确实有一种 hacky 解决方案,您可以在其中编写一个函数,如
# assuming box coords = [x_min, y_min, x_max, y_max]
def cropBoxByPercentage(box_coords, image_width, image_height, x_percentage=0.05, y_percentage=0.05):
box_xmin = box_coords[0]
box_ymin = box_coords[1]
box_xmax = box_coords[2]
box_ymax = box_coords[3]
box_width = box_xmax-box_xmin+1
box_height = box_ymax-box_ymin+1
dx = int(x_percentage * box_width)
dy = int(y_percentage * box_height)
box_xmin = max(0, box_xmin-dx)
box_xmax = min(image_width-1, box_xmax+dx)
box_ymin = max(0, box_ymax - dy)
box_ymax = min(image_height - 1, box_ymax + dy)
return np.array([box_xmin, box_xmax, box_ymin, box_ymax])
计算 x_percentage 和 y_percentage 可以使用固定值计算,也可以使用一些启发式计算。