极市导读
检测小目标和远程目标检测是监控应用中的一个主要挑战。这些物体由图像中少量的像素表示,缺乏足够的细节,使得传统的检测器难以检测。本文提出了一个名为切片辅助超推理(SAHI)的开源框架,该框架为小目标检测提供了一个通用的切片辅助推理和微调管道。所提出的技术可以应用在任何可用的目标检测器之上,而不需要进行任何微调。 >>加入极市CV技术交流群,走在计算机视觉的最前沿
论文链接:
https://paperswithcode.com/paper/slicing-aided-hyper-inference-and-fine-tuning
代码链接:https://github.com/obss/sahi
检测小目标和远程目标检测是监控应用中的一个主要挑战。这些物体由图像中少量的像素表示,缺乏足够的细节,使得传统的检测器难以检测。在这项工作中,提出了一个名为切片辅助超推理(SAHI)的开源框架,该框架为小目标检测提供了一个通用的切片辅助推理和微调管道。所提出的技术是通用的,因为它可以应用在任何可用的目标检测器之上,而不需要进行任何微调。
利用Visdrone和xView空中目标检测数据集上的目标检测Baseline的实验评估表明,该推理方法可将FCOS、VFNet和TOOD检测器的目标检测AP分别提高6.8%、5.1%和5.3%。此外,通过切片辅助微调可以进一步提高检测精度,从而按相同的顺序累计增加了12.7%、13.4%和14.5%的AP。
所提出的技术已与Detectron2、MMDetection和YOLOv5模型完成集成。
近年来,目标检测在人脸检测、视频目标检测、视频监控、自动驾驶汽车等不同应用领域得到了广泛的研究。在这一领域,深度学习架构的采用导致产生了高度精确的方法,如Faster R-CNN、RetinaNet,进一步发展为Cascade R-CNN、VarifocalNet和变体。
所有这些最近的检测器都是在著名的数据集上进行训练和评估的,如ImageNet、PascalVOC12、MSCOCO。这些数据集大多涉及低分辨率图像(640×480),包括相当大的像素覆盖的相当大的目标(平均覆盖图像高度的60%)。虽然训练后的模型对这些类型的输入数据具有成功的检测性能,但在高端无人机和监视摄像机生成的高分辨率图像中,它们对小目标检测任务的精度明显较低。
无人机、4K摄像机和深度学习研究的最新进展使远程目标检测成为可能,符合检测、观察、识别和DORI标准。DORI标准定义了不同任务对象的最小像素高度:10%的图像高度需要检测,20%的图像需要识别物体(全高清视频中的108像素)。相对较小的像素覆盖推动了基于CNN的目标检测方法的局限性,此外,高分辨率图像在计算和内存需求方面需要更大的需求。
在本文中提出了一种基于切片辅助推理和微调的通用解决方案,用于高分辨率图像上的小目标检测,同时保持较低的复杂度和内存需求。图1显示了Visdrone测试集样本图像上小目标检测的改进。
最近的基于学习的目标检测技术可分为两种主要类型。单级探测器,如SSD、YOLO、RetinaNet,直接预测物体的位置,而没有一个明确的建议阶段。两阶段区域建议的方法,如Fast R-CNN、Faster R-CNN、Cascade R-CNN,涉及区域建议阶段。然后对这些建议框进行细化,以定义目标的位置和大小。通常,单阶段方法比两阶段方法更快,而后者具有更高的精度。
最近,Anchor-Free检测器开始引起人们的注意。他们消除了Anchor box的使用,并将特征金字塔上的每个点分类为前景或背景,并直接预测从前景点到GT边界框的距离,从而产生检测。FCOS是第一个目标检测器,消除了预定义Anchor box集的需要,并需要计算需要。VarifocalNet(VFNet)学习预测IoU-aware classification score,它将目标存在的置信度和定位精度混合在一起,作为一个边界框的检测分数。学习是由基于一个新的星形边界框特征表示所提出的Varifocal Loss(VFL)监督的。TOOD以基于学习的方式将2个任务(目标分类和定位)明确对齐,通过设计的样本分配方案和任务对齐损失,在学习任务交互和任务特定特征和任务对齐学习之间提供了更好的平衡。
针对一般目标检测的算法在包含小而密集目标的高分辨率图像上表现不佳,导致了针对小目标检测的特定方法。有研究采用基于粒子群优化(PSO)和细菌觅食优化(BFO)的学习策略(PBLS)对分类器和损失函数进行优化。然而,这些对原始模型的重大修改阻止了从预训练的权重进行微调,需要从头开始训练。
此外,由于不寻常的优化步骤,它们很难适应目前的检测器。该方法对具有小目标的图像进行过采样,并通过复制多个小目标对其进行扩充。然而,这种增强需要分割注释,因此,它与目标检测数据集不兼容。有方法可以从原始图像中截取放大区域,从中学习到更丰富的小目标特征。额外的特征对检测性能有积极的贡献,但要扩大的区域的选择带来了计算负担。也有研究提出了一种用于小目标检测的全卷积网络,该网络包含了一种早期的视觉注意力机制,用来选择最有希望的包含小目标及其上下文的区域。还有研究提出了一种基于切片的技术,但其实现并不通用,仅适用于特定的目标检测器。也有研究提出了一种新型的小型行人检测网络(JCS-Net),该网络将分类任务和超分辨率任务整合在一个统一的框架中。
有学者提出了一种利用生成式对抗网络(GAN)从模糊的小人脸直接生成清晰的高分辨率人脸的算法。然而,由于这些技术提出了新的检测器体系结构,它们需要从头开始用大型数据集进行预训练,这是昂贵的。
为了解决小目标检测问题,作者提出了一个在微调和推理阶段基于切片的通用框架。将输入图像划分为重叠的切片,对于小目标相对于输入网络的图像产生相对较大的像素区域。
广泛使用的目标检测框架,如Detectron2、MMDetection和YOLOv5,在ImageNet和MSCOCO等数据集上提供预训练的权重。这允许使用更小的数据集和更短的训练跨度来对模型进行微调,而不是使用大数据集从头开始进行训练。
这些常见的数据集大多涉及低分辨率的图像(640×480),它们具有相当大的目标和较大的像素覆盖(平均覆盖图像高度的60%)。使用这些数据集进行预训练的模型为相似的输入提供了非常成功的检测性能。另一方面,在由高端无人机和监视摄像头生成的高分辨率图像中,它们对小目标检测任务的精度明显较低。
为了克服这个问题, 作者通过从图像微调数据集中提取patch来增强数据集, 如图2所示。每幅 图像 被分割成重叠的patch 在预定义范围 和 内选择N和N的 被视为超参数。然后在微调过程中, 通过保留高宽比来调整 patch的大小, 使图像宽度在800 1333像素之间, 以获得增强图像 , 因此相对的目标大小比原始图像更大。
这些图像 , 以及原始图像 (便于检测大物体) , 在微调过程中被利用。需要注意的是, 随着patch size的减小, 较大的物体可能不适合一个切片和交叉区域, 这可能导致对较大物体的检测性能较差。
在推理步骤中也使用了切片方法, 如图3所示。首先,将原始查询图像I分割成1个M\timesN个重叠的patch 。然后,在保持高宽比的同时, 调整每个patch的大小。然后,对每个重叠的patch都独立地应用目标检测正向传递。使用原始图像的一个可选的全推理(FI)可以用 于检测较大的目标。最后,重叠的预测结果, 如果使用,FI结果使用NMS合并回原始大小。在NMS过程中,具有比预定义匹配阈值Tm的Union(IoU)比值更高的box被匹配, 并且对于每个匹配, 具有检测概率低于 的检测被删除。
def slice_image(
image: Union[str, Image.Image],
coco_annotation_list: Optional[CocoAnnotation] = None,
output_file_name: Optional[str] = None,
output_dir: Optional[str] = None,
slice_height: int = 512,
slice_width: int = 512,
overlap_height_ratio: float = 0.2,
overlap_width_ratio: float = 0.2,
min_area_ratio: float = 0.1,
out_ext: Optional[str] = None,
verbose: bool = False,
) -> SliceImageResult:
# define verboseprint
verboselog = logger.info if verbose else lambda *a, **k: None
def _export_single_slice(image: np.ndarray, output_dir: str, slice_file_name: str):
image_pil = read_image_as_pil(image)
slice_file_path = str(Path(output_dir) / slice_file_name)
# export sliced image
image_pil.save(slice_file_path)
verboselog("sliced image path: " + slice_file_path)
# create outdir if not present
if output_dir is not None:
Path(output_dir).mkdir(parents=True, exist_ok=True)
# read image
image_pil = read_image_as_pil(image)
verboselog("image.shape: " + str(image_pil.size))
image_width, image_height = image_pil.size
if not (image_width != 0 and image_height != 0):
raise RuntimeError(f"invalid image size: {image_pil.size} for 'slice_image'.")
slice_bboxes = get_slice_bboxes(
image_height=image_height,
image_width=image_width,
slice_height=slice_height,
slice_width=slice_width,
overlap_height_ratio=overlap_height_ratio,
overlap_width_ratio=overlap_width_ratio,
)
t0 = time.time()
n_ims = 0
# init images and annotations lists
sliced_image_result = SliceImageResult(original_image_size=[image_height, image_width], image_dir=output_dir)
# iterate over slices
for slice_bbox in slice_bboxes:
n_ims += 1
# extract image
image_pil_slice = image_pil.crop(slice_bbox)
# process annotations if coco_annotations is given
if coco_annotation_list is not None:
sliced_coco_annotation_list = process_coco_annotations(coco_annotation_list, slice_bbox, min_area_ratio)
# set image file suffixes
slice_suffixes = "_".join(map(str, slice_bbox))
if out_ext:
suffix = out_ext
else:
try:
suffix = Path(image_pil.filename).suffix
except AttributeError:
suffix = ".jpg"
# set image file name and path
slice_file_name = f"{output_file_name}_{slice_suffixes}{suffix}"
# create coco image
slice_width = slice_bbox[2] - slice_bbox[0]
slice_height = slice_bbox[3] - slice_bbox[1]
coco_image = CocoImage(file_name=slice_file_name, height=slice_height, width=slice_width)
# append coco annotations (if present) to coco image
if coco_annotation_list:
for coco_annotation in sliced_coco_annotation_list:
coco_image.add_annotation(coco_annotation)
# create sliced image and append to sliced_image_result
sliced_image = SlicedImage(image=np.asarray(image_pil_slice), coco_image=coco_image, starting_pixel=[slice_bbox[0], slice_bbox[1]],)
sliced_image_result.add_sliced_image(sliced_image)
# export slices if output directory is provided
if output_file_name and output_dir:
conc_exec = concurrent.futures.ThreadPoolExecutor(max_workers=MAX_WORKERS)
conc_exec.map(_export_single_slice, sliced_image_result.images, [output_dir] * len(sliced_image_result), sliced_image_result.filenames,)
verboselog("Num slices: " + str(n_ims) + " slice_height: " + str(slice_height) + " slice_width: " + str(slice_width),)
return sliced_image_result
本文采用了MS COCO评估协议进行评估,包括总体和尺寸方面的AP50评分。具体来说,AP50在所有类别的单个IoU阈值0.5处计算,最大检测数设置为500。
在表1和表2对原始图像的常规推理中,以FI(Full inference)作为Baseline。SF(切片辅助微调)是在表1和表2中patch大小分别为480-640和300-500的增强数据集上进行微调的模型。
SAHI(切片辅助Hyper Inference)是指表1和表2中patch大小分别为640×640和400×400的推理。OP(Overlap Patch)表示切片推理过程中Patch之间有25%的重叠。
从表1可以看出,SAHI使目标检测AP分别提高了6.8%、5.1%和5.3%。SF可进一步提高检测精度,FCOS、VFNet和tod检测器的累计AP分别提高12.7%、13.4%和14.5%。在推理过程中,应用切片间25%的重叠,会增加小/中物体AP和整体AP,但略微降低大物体AP。增加是由切片预测的额外小目标真阳性引起,减少是由匹配大GT框的切片预测的假阳性引起。小目标检测AP以SF最佳,SI次之,大目标检测AP以SF最佳,FI次之,证实了FI对大目标检测的贡献。
xView数据集的结果如表2所示。由于xView目标非常小,经常使用原始图像进行训练,检测性能较差,SF大大提高了结果。FI的集成使大目标AP增加了3.0%,但导致小型/中型目标AP略有下降,这是预期的,因为一些较大的目标可能无法从较小的切片中检测到。切片间25%的重叠可以增加2.9%的检测AP。
xView包含高度不平衡的60个目标类别,尽管FCOS是一个较老的,据说较弱的检测器,但对于这个数据集,FCOS比VFNet有更好的性能。这一观察结果证实了FCOS中Focal Loss的有效性,该方法旨在处理类别失衡。TOOD在训练过程中也受益于Focal Loss,在3种检测器中检测效果最好。tod检测器在Visdrone和xView数据集上的误差分析结果分别如图3和图4所示。
公众号后台回复“数据集”获取30+深度学习数据集下载~
# CV技术社群邀请函 #
备注:姓名-学校/公司-研究方向-城市(如:小极-北大-目标检测-深圳)
即可申请加入极市目标检测/图像分割/工业检测/人脸/医学影像/3D/SLAM/自动驾驶/超分辨率/姿态估计/ReID/GAN/图像增强/OCR/视频理解等技术交流群
每月大咖直播分享、真实项目需求对接、求职内推、算法竞赛、干货资讯汇总、与 10000+来自港科大、北大、清华、中科院、CMU、腾讯、百度等名校名企视觉开发者互动交流~