目录
前言:
1. 写入json文件时的 ground truth bbox
2. 导入图片 和 获取对应的 ground truth bbox
3. 进行数据增强后, 决定了 最终的 ground truth bbox
数据集的ground truth bbox 是一个重要的部分,在训练过程的开始输入数据会经过一系列的变化,然后才被送入到model中,在这个过程中,ground truth bbox 是怎样对应着 数据的变化而相应的改变,从而达到标注的信息是正确的呢?o(* ̄▽ ̄*)o
for trackid, ann in enumerate(anns): # ann 相当于拿出每张图片的每个目标的bbox
rect = ann['bbox'] # bbox
c = ann['category_id'] # 类别 id
bbox = [rect[0], rect[1], rect[0] + rect[2], rect[1] + rect[3]] # xywh to xyxy
if rect[2] <= 0 or rect[3] <= 0: # lead nan error in cls.
count += 1
print(count, rect)
continue
dataset[video_crop_base_path]['{:02d}'.format(trackid)] = {'000000': bbox} # 举例 {'val2017/000000397133':{'00':{'000000':[217.62,240.54,256.61,298.289999]}}}
可以看出,虽然数据集中的每张图片被裁剪成以目标为中心并resize成511(详见另一篇用于目标跟踪的COCO数据集的预处理过程的API,以及对训练数据的数据增强操作),但是写入json文件的ground truth bbox 还是原来最初标注的bbox值,没做任何处理,只不过从xywh转成xyxy型写入的,所以经过裁剪处理后的图片与json文件中的ground truth bbox 并不是 对应的,但是依然可以获取一些原始的信息,比如bbox的 长和宽,以及会根据这个原始的bbox 来计算填充裁剪bbox的尺寸。
利用创建的json文件中的bbox可视化效果如下,可见其包含的信息还是原来未处理中的目标
裁剪后图片画json文件中的bbox
裁剪前的原始图片画json文件中的bbox
由于裁剪的图片都是以目标为中心的,所以根据裁剪图片的形状可以确定目标的中心坐标。然后虽然裁剪的时候尺寸不一定是设定的255和127,不过最终确定的模板和搜索区域的输入是确定的,即127和255。所以根据这个,可以获得目标的 ground truth bbox,目标的中心坐标就是图片的中心坐标,目标的 w 和 h 可以根据裁剪 bbox(填充后的尺寸和resize后的输入尺寸的比例)和json文件中的ground truth bbox 得到的w和h 得到,如下代码中所示
def _get_bbox(self, image, shape): # shape 就是 bbox
imh, imw = image.shape[:2] # 511,511
if len(shape) == 4: # True
w, h = shape[2]-shape[0], shape[3]-shape[1] # 举例 h:42.920000000000016 w:38.360000000000014
else:
w, h = shape
context_amount = 0.5
exemplar_size = cfg.TRAIN.EXEMPLAR_SIZE # 127
wc_z = w + context_amount * (w+h) # 举例 79.0000000000000
hc_z = h + context_amount * (w+h) # 举例 83.5600000000000
s_z = np.sqrt(wc_z * hc_z) # 举例 81.248015367668 裁剪填充的尺寸
scale_z = exemplar_size / s_z # 举例 1.5631115104248 ratio
w = w*scale_z # 举例 59.96150953989797 裁剪的尺寸 w 以及 h
h = h*scale_z # 举例 67.08890002743356
cx, cy = imw//2, imh//2 # 举例 cx:255 cy : 255 以目标为中心
bbox = center2corner(Center(cx, cy, w, h)) # {corner:4} 原矩形框没有用上,只用了w和w计算一下比例,然后目标中心就是511图片的中心
return bbox
但这个bbox 是对应 裁剪后的满足输入到model 尺寸的image的标注,到现在为止只是导入了原来裁剪后的图片,还没进行 填充 再 resize成 127和255 这步操作,所以现在 image 和 ground truth bbox 也不是 对应的。
在 iamge 上画一下 现在的 ground truth bbox,如下所示,虽然框住的目标是正确的,但实际上现在还不算是紧密的矩形框,简单来说中心位置是正确的,但是size是不对应的,该图片还没进行填充裁剪和resize操作(现在的尺寸依然是511),这个图中的目标太小体现不出来差别。
到现在 ,image的尺寸依然是(511,511),进入数据增强,首先会设定一个新的crop_bbox(后面会以这个为基础构建变换矩阵, 对 image 进行 放射变换),以目标为中心,template_size 或者 search_size为边长,
shape = image.shape # (511,511,3)
crop_bbox = center2corner(Center(shape[0]//2, shape[1]//2,
size-1, size-1))
将其 (蓝色)与第2部分得到的ground truth bbox (黄色) 一起画出来如下所示
数据增强过程中,只有 平移和尺度变换会改变bbox的位置和尺寸,所以这步过后最终的ground turt bbox才确定下来。
进行平移和尺度变换后的矩形框如下图红色所示。它是根据上图蓝色部分基础上进行的平移加变换。画出来后如下图所示。,其中红色就是平移加尺度变换后的裁剪bbox
那么裁剪bbox进行平移和尺度变换后,裁剪的图片也会相比于原本打算的会发生变化,接下来就是怎样把目标的 ground turth bbox 也进行相应的变换,是目标与 ground turth bbox 对应,进行如下操作:
x1, y1 = crop_bbox.x1, crop_bbox.y1
bbox = Corner(bbox.x1 - x1, bbox.y1 - y1,
bbox.x2 - x1, bbox.y2 - y1)
if self.scale:
bbox = Corner(bbox.x1 / scale_x, bbox.y1 / scale_y,
bbox.x2 / scale_x, bbox.y2 / scale_y)
在未裁剪的image 画出 bbox如下所示
如图中左上角矩形框所示,这是为裁剪图片,但是如果把红色矩形框的左上角顶点移到图片image的左上角顶点上,是不是可以想象出它已经包围了目标。其实源代码中也是这样做的,从上面的代码中就可以看出,就是相当于把 进行尺度变换后的用于裁剪的bbox(红色框)左上角顶点(x1, y1)作为参照对应到图片的左上角顶点(0,0),用ground turth bbox(黄色框所示)的左上角顶点和右下角顶点减去(x1, y1)。
接下来把image 进行裁剪,裁剪成127或者255,
def _crop_roi(self, image, bbox, out_sz, padding=(0, 0, 0)):
bbox = [float(x) for x in bbox] # list:4 [, , ,]
a = (out_sz-1) / (bbox[2]-bbox[0])
b = (out_sz-1) / (bbox[3]-bbox[1])
c = -a * bbox[0]
d = -b * bbox[1]
mapping = np.array([[a, 0, c],
[0, b, d]]).astype(np.float)
crop = cv2.warpAffine(image, mapping, (out_sz, out_sz),
borderMode=cv2.BORDER_CONSTANT,
borderValue=padding)
以红色矩形框为基础,设置变换矩阵,进行仿射变换,得到相应的输出尺寸,裁剪后的图片上画出ground turth bbox 如下图所示,可以以看到下面就是最终的 search图片和 ground turth bbox 的由来。
这里会有一个疑问,不仅search会进行平移加尺度变换 ,而且模板template也会进行平移加尺度变换,那模板的目标会相对于整个区域发生偏移吗?的确模板很可能不在以目标为中心,不过这无关紧要,因为模板送入model的作用就是整个作为卷积核去对大的搜索区域进行相关运算,所以目标在不在中心不影响它应尽的作用,而且这样也增加了模板的多样性以及model的鲁棒性。
template, _ = self.template_aug(template_image,
template_box,
cfg.TRAIN.EXEMPLAR_SIZE,
gray=gray) # ndarry:(127,127,3)
search, bbox = self.search_aug(search_image,
search_box,
cfg.TRAIN.SEARCH_SIZE,
gray=gray)