【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()

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值