【CV项目源码实现】darknet yolov3输入图片预处理及将预测框bbox还原至原图
前言
Yolov3网络的输入默认为416x416,然后待检测的图片不总是416x416,这就产生了如何将待检测图片,在不破坏特征的情况下缩放至416x416,并对应在网络产生预测框后,如何将416x416图中的预测框还原至原图的问题。
预处理过程letterbox_image
letterbox_image是按照原图横纵比不变进行缩放后的图像;
采用保持比例的图像缩放,代替传统的resize方式,对图像加以灰色背景,补全较目标比例相差的边缘部分;
step1. 求取缩放比例,将目标416x416,除以输入图像尺寸(width,height),取其中较小的一个比例,将长宽同时按此比例进行缩放。由于是长宽等比缩放,所以图片的特征并不失真。 step2. 制作rgb为(128,128,128),大小为416*416的底色图片。 step3. 将两张图片的中心对齐,将缩放后的图片粘贴到底色图上。
pytorch code
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import os.path from typing import Iterator import numpy as np import torch import cv2 import matplotlib.pyplot as plt from PIL import Image from torch.utils.data import Dataset, DataLoader, Subset, random_split import re from functools import reduce from torch.utils.tensorboard import SummaryWriter as Writer from torchvision import transforms, datasets import torchvision as tv from torch import nn import torch.nn.functional as F import time import math def letterbox_image(image, size): ''' 保持比例的图像缩放用:代替了传统的resize方式 对图片加以灰色背景,补全较目标比例相差的边缘部分。 该函数只能处理单张图片 :param image: 原始图片 :param size: 目标图像宽高的元组 :return: 缩放到size后的目标图像 ''' iw, ih = image.size w, h = size scale = min(w / iw, h / ih) nw = int(iw * scale) nh = int(ih * scale) #此时为按比例缩放,为torch提供的函数 image = image.resize((nw, nh), Image.BICUBIC) #构建新的RGB背景图片 new_image = Image.new('RGB', size, (128, 128, 128)) #缩放后的图片粘贴至背景图片上 '''参数可选4元组及2元组,如果选择2元组,则为新图片相当于背景图片的左上角坐标''' new_image.paste(image, ((w - nw) // 2, (h - nh) // 2)) return new_image
预测框还原到原图
step1. 输入图像在网络size的letterbox中的尺寸;
step2. 网络输出的bbox是相对于net size的比值,计算bbox相对于letterbox size中的offset;
step3. 计算letterbox中的相对坐标;
经坐标换算,得到的坐标还是在输入网络的图片net size坐标系下的绝对坐标,但是此时已经是相对于new size这个区域的坐标了,而不再相对于(0,0)原点。这样保证bbox不会扭曲;
所以,预测之后的bbox是高宽比不变的letterbox image的比值,对于输入图像,只需要scale计算即可;
darknet code(src/yolo_layer.c)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
void correct_yolo_boxes(detection *dets, int n, int w, int h, int netw, int neth, int relative) { int i; // 此处new_w表示输入图片经压缩后在网络输入大小的letter_box中的width,new_h表示在letter_box中的height, // 以1280*720的输入图片为例,在进行letter_box的过程中,原图经resize后的width为416, 那么resize后的对应height为720*416/1280, //所以height为234,而超过234的上下空余部分在作为网络输入之前填充了128,new_h=234 int new_w=0; int new_h=0; // 如果w>h说明resize的时候是以width/图像的width为resize比例的,先得到中间图的width,再根据比例得到height if (((float)netw/w) < ((float)neth/h)) { new_w = netw; new_h = (h * netw)/w; } else { new_h = neth; new_w = (w * neth)/h; } for (i = 0; i < n; ++i){ box b = dets[i].bbox; // 此处的公式很不好理解还是接着上面的例子,现有new_w=416,new_h=234,因为resize是以w为长边压缩的 // 所以x相对于width的比例不变,而b.y表示y相对于图像高度的比例,在进行这一步的转化之前,b.y表示 // 的是预测框的y坐标相对于网络height的比值,要转化到相对于letter_box中图像的height的比值时,需要先 // 计算出y在letter_box中的相对坐标,即(b.y - (neth - new_h)/2./neth),再除以比例 b.x = (b.x - (netw - new_w)/2./netw) / ((float)new_w/netw); b.y = (b.y - (neth - new_h)/2./neth) / ((float)new_h/neth); b.w *= (float)netw/new_w; b.h *= (float)neth/new_h; if(!relative){ b.x *= w; b.w *= w; b.y *= h; b.h *= h; } dets[i].bbox = b; } }
问题
如果我检测的图片长宽比不是1:1,或者我的图片大小并不是32的整数倍,如果 resize 会使目标失真,怎么办呢?
通过分析,实际上这些疑问都是没有必要的。yolo v5 设置的 img_size 并不会影响任意尺寸图像的检测,这个数值设置的目的是使输入图像先被 resize 成 640×640,满足检测网络结构,最后再 resize 成原始图像尺寸,进行显示。
参考
1. 【pytorch】自己实现精简版YOLOV3【三】,YOLOV3输入图片预处理:输入图片缩放及将生成预测框还原至原图;
2. YOLO V5 测试图像时 img_size 的设置问题;
心正意诚,做自己该做的事情,做自己喜欢做的事情,安静做一枚有思想的技术媛。
版权声明,转载请注明出处:https://www.cnblogs.com/happyamyhope/