【yolo】图像预处理方法letterbox

yolov5 图像预处理letterbox

letterbox被用于LoadImages类的__next__方法中, 用于输入图像尺寸的预处理。

letterbox实现了图像调整技术,主要用于深度学习领域中的物体检测任务,确保不同尺寸的输入图像能被模型接受。

Letterbox的原理很简单。当输入图像的纵横比与模型所需不一致时,Letterbox会在图像的顶部和底部添加填充(通常是黑色或与背景色相同),以保持纵横比的一致性。填充的宽度会根据纵横比差异进行调整。这种方法可以确保模型能够处理各种纵横比的图像,并减少因纵横比差异导致的问题。

letterbox方法解析

这里函数传参如下(yolo使用的默认值)

im = letterbox(im0, new_shape=(640,640), stride=32, scaleup=True, scaleFill=False, auto=True)

方法解析如下:

  1. 计算缩放比例:首先计算图像高宽分别与目标尺寸的缩放比例,取其中较小的一个以保持原图的宽高比。如果scaleup=False,则限制缩放比例不超过1,即不允许放大图像。
  2. 计算无填充的新尺寸:根据缩放比例计算调整后图像的无填充尺寸。
  3. 计算填充量:计算需要在图像的宽和高方向上分别填充多少像素,以达到目标尺寸。如果auto=True,则确保填充后的尺寸是stride的整数倍;如果scaleFill=True,则直接拉伸图像至目标尺寸,无需额外填充。
  4. 调整图像尺寸和填充
    • 根据计算出的无填充尺寸对原图进行缩放。
    • 使用cv2.copyMakeBorder函数在图像四周添加指定颜色的边框,实现填充操作。这里通过微调dh和dw(减去0.1再加回0.1)来确保精确填充,尽管这种做法在数学上等效于直接使用dh和dw,是为了代码的可读性或历史遗留。
  5. 返回结果:函数最后返回调整尺寸后的图像、原始图像与新图像的比例以及实际的填充宽度和高度。
def letterbox(im, new_shape=(640, 640), color=(114, 114, 114), auto=True, scaleFill=False, scaleup=True, stride=32):
    """
    图像调整技术,通过在图像的边缘填充一定数量的像素,使图像的尺寸满足特定的要求
    :param im: 原始图片numpy
    :param new_shape: 目标尺寸,默认为(640, 640)
    :param color: 填充颜色,默认为灰色(114, 114, 114)
    :param auto: 自动最小外接矩形调整,保持原始比例且两边留有等宽的padding
    :param scaleFill: 是否拉伸图像以完全填充新尺寸,忽略原始比例
    :param scaleup: 是否允许放大图像,若为False,则只缩小不放大,有利于验证集mAP表现
    :param stride: 确保最终尺寸是该值的倍数
    :return:
    """
    shape = im.shape[:2]  # current shape [height, width] (2516,3770)
    if isinstance(new_shape, int):
        new_shape = (new_shape, new_shape)

    # 计算缩放比例, 取其中较小的一个以保持原图的宽高比, 按默认值就是让图像宽或者高为640
    r = min(new_shape[0] / shape[0], new_shape[1] / shape[1]) # min(0.2543,0.1697) = 0.5925
    # 限制缩放比例不超过1,即不允许放大图像
    if not scaleup:  # only scale down, do not scale up (for better val mAP)
        r = min(r, 1.0)

    # Compute padding 根据缩放比例计算调整后图像的无填充尺寸
    ratio = r, r  # width, height ratios (0.1697,0.1697)
    new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r)) # (640,427)
    dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1]  # 0 213
    if auto:  # 确保填充后的尺寸是stride的整数倍
        dw, dh = np.mod(dw, stride), np.mod(dh, stride)  # wh padding 0 21
    elif scaleFill:  # 直接拉伸图像至目标尺寸,无需额外填充
        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 shape[::-1] != new_unpad:  # resize
        # 根据计算出的无填充尺寸对原图进行缩放
        im = cv2.resize(im, 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))
    # 图像四周添加指定颜色的边框,实现填充操作
    im = cv2.copyMakeBorder(im, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color)  # add border
    return im, ratio, (dw, dh)

多种参数测试样例对比差异

我们将多种参数进行搭配, 通过对比看出差异

import cv2
import numpy as np
import matplotlib.pyplot as plt

from utils.augmentations import letterbox

path = r'.\data\images\img_1.png'

num = 8
point = 1

plt.figure(dpi=300,figsize=(8,24))
def get_img(new_shape=[640, 640], stride=32, scaleFill=False, scaleup=True, auto=True):
    global point
    im0 = cv2.imread(path)  # BGR
    im_row = im0[:, :, ::-1]
    im = letterbox(im0, new_shape=new_shape, stride=stride, scaleup=scaleup, scaleFill=scaleFill, auto=auto)[
        0]  # padded resize
    # im = im.transpose((2, 0, 1))[::-1]  # HWC to CHW, BGR to RGB
    # im = np.flip(im, axis=2)  # 第三个维度翻转
    im = im[:, :, ::-1]  # 第三个维度翻转
    im = np.ascontiguousarray(im)  # contiguous
    plt.subplot(num, 2, point)
    point += 1
    plt.title(f"origin_{im0.shape[1]}_{im0.shape[0]}")  # 标题 宽_高
    plt.imshow(im_row)  # 显示图片
    plt.axis('off')  # 不显示坐标轴
    plt.subplot(num, 2, point)
    point += 1
    plt.title(f"process_{im.shape[1]}_{im.shape[0]}")  # 标题 宽_高
    plt.imshow(im)  # 显示图片
    plt.axis('off')  # 不显示坐标轴


get_img(scaleFill=False, scaleup=True, auto=True)
get_img(scaleFill=False, scaleup=True, auto=False)
get_img(scaleFill=False, scaleup=False, auto=True)
get_img(scaleFill=False, scaleup=False, auto=False)
get_img(scaleFill=True, scaleup=True, auto=True)
get_img(scaleFill=True, scaleup=True, auto=False)
get_img(scaleFill=True, scaleup=False, auto=True)
get_img(scaleFill=True, scaleup=False, auto=False)

plt.show()

在这里插入图片描述

YOLO的输入预处理中,使用letterbox方法可以将图像缩放到指定大小并保持原始比例,同时在边缘填充灰色像素以保持图像的纵横比。下面是基于YUV图像实现YOLO letterbox预处理的步骤: 1. 读取YUV图像数据并解码,得到YUV三个通道的图像数据。 2. 将YUV三个通道的图像数据缩放到指定大小,保持原始图像的纵横比不变。 3. 计算缩放后图像在Letterbox中的位置和大小,并在边缘填充灰色像素。 4. 将填充后的图像转换为RGB格式,为了符合YOLO的输入格式,还需要将像素值归一化到0-1之间。 5. 将处理后的图像送入YOLO模型进行检测。 下面是一个基于Python语言的示例代码,实现了基于YUV图像的YOLO letterbox预处理: ```python import cv2 import numpy as np def yuv2rgb(yuv): yuv = yuv.astype(np.float32) y = yuv[..., 0] u = yuv[..., 1] v = yuv[..., 2] r = y + 1.13983 * v g = y - 0.39465 * u - 0.58060 * v b = y + 2.03211 * u rgb = np.stack([r, g, b], axis=-1) rgb = np.clip(rgb, 0, 255).astype(np.uint8) return rgb def letterbox_resize_yuv(yuv, size): h, w, _ = yuv.shape ratio = min(size[0] / h, size[1] / w) new_h = int(h * ratio) new_w = int(w * ratio) resized_yuv = cv2.resize(yuv, (new_w, new_h)) pad_h = (size[0] - new_h) // 2 pad_w = (size[1] - new_w) // 2 padded_yuv = cv2.copyMakeBorder(resized_yuv, pad_h, size[0]-new_h-pad_h, pad_w, size[1]-new_w-pad_w, cv2.BORDER_CONSTANT, value=(128, 128, 128)) rgb = yuv2rgb(padded_yuv) rgb = rgb.astype(np.float32) / 255.0 return rgb # 读取YUV图像数据 yuv = cv2.imread("example.yuv", cv2.IMREAD_UNCHANGED) # 进行YOLO letterbox预处理 input_size = (416, 416) yolo_input = letterbox_resize_yuv(yuv, input_size) # 将处理后的图像送入YOLO模型进行检测 results = model.predict(np.expand_dims(yolo_input, axis=0)) ``` 在上面的代码中,`yuv2rgb`函数实现了将YUV图像转换为RGB图像的功能,`letterbox_resize_yuv`函数实现了YOLO letterbox预处理的功能。其中,`input_size`指定了模型输入的大小,`yuv`是原始YUV图像数据。经过预处理后,得到的`yolo_input`即为符合YOLO模型输入要求的图像数据,可以直接送入模型进行检测。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值