统一定义:
- size: (width, height)
- shape: (rows, cols, channels)
检测是否符合grid size的倍数要求
gs = int(max(model.stride)) # grid size (max stride)
def check_img_size(img_size: list, gs):
new_size = img_size.copy() # [width, height]
for i in range(len(img_size)):
new_size[i] = math.ceil(img_size[i] / gs) * gs
if new_size != img_size:
print(f'WARNING: --img-size {img_size} must be multiple of max stride {gs}, updating to {new_size}')
return new_size
Rectangular Padding 矩形填充
def letterbox(img, new_size=(640, 640), color=(114, 114, 114), scaleup=True):
# Resize image to a 32-pixel-multiple rectangle https://github.com/ultralytics/yolov3/issues/232
size = [img.shape[1], img.shape[0]] # [width, height]
if isinstance(new_size, int):
new_size = (new_size, new_size)
# Scale ratio (new / old)
ratio = min(new_size[0] / size[0], new_size[1] / size[1])
# Compute padding
new_size_unpad = int(round(size[0] * ratio)), int(round(size[1] * ratio))
dw, dh = new_size[0] - new_size_unpad[0], new_size[1] - new_size_unpad[1] # wh padding
dw, dh = np.mod(dw, 64), np.mod(dh, 64) # wh padding
dw /= 2 # divide padding into 2 sides
dh /= 2
if size[0] != new_size_unpad[0]: # resize
img = cv2.resize(img, new_size_unpad, interpolation=cv2.INTER_LINEAR)
top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1))
left, right = int(round(dw - 0.1)), int(round(dw + 0.1))
img = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color) # add border
return img
坐标恢复 rescale_coords
def rescale_coords(coords: torch.Tensor, init_size, ratio, dw, dh):
coords[:, [0, 2]] -= dw # x padding
coords[:, [1, 3]] -= dh # y padding
coords[:, :4] /= ratio
# Clip bounding xyxy bounding boxes to image shape (height, width)
coords[:, 0].clamp_(0, init_size[0]) # x1
coords[:, 1].clamp_(0, init_size[1]) # y1
coords[:, 2].clamp_(0, init_size[0]) # x2
coords[:, 3].clamp_(0, init_size[1]) # y2
return coords
测试代码
def main():
img_size = [900, 550]
img = np.ones([img_size[1], img_size[0], 3], dtype="uint8") * 255
m, n, _ = img.shape
cx, cy, r = n // 2, m // 2, 80
cv2.circle(img, (cx, cy), r, (80, 180, 80), -1)
cv2.imshow("img", img)
new_size = check_img_size(img_size, 32)
dst, ratio, (dw, dh) = letterbox(img, new_size)
cv2.imshow("dst", dst)
bbox = cv2.selectROI("dst", dst)
x1, y1, x2, y2 = bbox[0], bbox[1], bbox[0] + bbox[2], bbox[1] + bbox[3]
bboxes = torch.Tensor([[x1, y1, x2, y2]])
bboxes = rescale_coords(bboxes, img_size, ratio, dw, dh)
x1, y1, x2, y2 = bboxes[0]
cv2.rectangle(img, (x1, y1), (x2, y2), (80, 180, 80), 3)
cv2.imshow("dst", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
if __name__ == '__main__':
main()