赞
踩
LetterBox是一种数据增强方法,它可以将图像缩放到指定大小(长和宽等比例缩放),然后在图像的两侧添加黑色边框,使其大小和要调整的大小一致。这种方法可以保留原始图像的纵横比,同时还可以使图像更加适合目标检测算法的输入。
首先将图片按比例缩放,将较长的边缩放到设定的尺寸以后,再将较短的边进行填充,最终短边的长度为stride的倍数即可。
import cv2
import numpy as np
class LetterBox:
"""Resize image and padding for detection, instance segmentation, pose"""
def __init__(self, new_shape=(640, 640), auto=False, scaleFill=False, scaleup=True, stride=32):
self.new_shape = new_shape
# 是否按调整前后长和宽比值的最小值进行长和宽等比缩放,然后进行填充
self.auto = auto
# 是否进行完全缩放
self.scaleFill = scaleFill
# 是否进行放大操作
self.scaleup = scaleup
# 步长
self.stride = stride
def __call__(self, labels=None, image=None):
if labels is None:
labels = {}
# 获取图片数据
img = labels.get("img") if image is None else image
# 原图片的高度和宽度
shape = img.shape[:2] # current shape [height, width]
# 图片要调整到大小
new_shape = labels.pop("rect_shape", self.new_shape)
# 图片的大小应为(height, width)的形式,如果不是进行调整
if isinstance(new_shape, int):
new_shape = (new_shape, new_shape)
# 调整前后图像的 长和宽的比值 中的最小值(只把最长边调整到new_shape的大小)
r = min(new_shape[0] / shape[0], new_shape[1] / shape[1])
# 是否只缩小
if not self.scaleup: # only scale down, do not scale up (for better val mAP)
r = min(r, 1.0)
# 长和宽调整的比率
ratio = r, r
# 根据长和宽调整的比率,调整后图像的大小
new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r))
# 根据长和宽调整的比率,调整后图像边的大小 小于 new_shape, 需要进行填充
dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1] # wh padding
if self.auto: # minimum rectangle
# 每个边进行最小的填充, 填充值为dw除以stride的余数
dw, dh = np.mod(dw, self.stride), np.mod(dh, self.stride) # wh padding
# 完全根据调整前后的长和宽的比率调整图像,不进行填充操作
elif self.scaleFill: # stretch
dw, dh = 0.0, 0.0
new_unpad = (new_shape[1], new_shape[0])
ratio = new_shape[1] / shape[1], new_shape[0] / shape[0] # width, height ratios
# 将填充分配在图像两面
dw /= 2 # divide padding into 2 sides
dh /= 2
if labels.get("ratio_pad"):
labels["ratio_pad"] = (labels["ratio_pad"], (dw, dh)) # for evaluation
# 如果当前图像的大小和调整后大小(不是new_shape而是前面求出的new_unpad)不相等,则进行图像大小调整
if shape[::-1] != new_unpad: # resize
img = cv2.resize(img, new_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=(114, 114, 114)) # add border
# 进行图像位置信息的调整
if len(labels):
labels = self._update_labels(labels, ratio, dw, dh)
labels["img"] = img
labels["resized_shape"] = new_shape
return labels
else:
return img
def _update_labels(self, labels, ratio, padw, padh):
"""Update labels"""
labels["instances"].convert_bbox(format="xyxy")
labels["instances"].denormalize(*labels["img"].shape[:2][::-1])
labels["instances"].scale(*ratio)
labels["instances"].add_padding(padw, padh)
return labels
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。