孪生网络算法对数据需求量较大,训练一般使用多个数据集。检测数据集普遍图大目标多,pysot 第一步准备数据,屏蔽了不同数据集的差异,也提升了数据加载效率。网络设定的模板大小为 127 × 127 127\times 127 127×127,搜索区域大约为 255 × 255 255\times 255 255×255。为给数据增强留有余地,数据切图大小为 511 × 511 511\times 511 511×511。下面以 COCO 数据集为例进行介绍。
par_crop.py
since = time.time()
main(int(sys.argv[1]), int(sys.argv[2]))
time_elapsed = time.time() - since
print('Total complete in {:.0f}m {:.0f}s'.format(
time_elapsed // 60, time_elapsed % 60))
main
创建切图路径。
dataDir = '.'
crop_path = './crop{:d}'.format(instanc_size)
if not isdir(crop_path): mkdir(crop_path)
对于训练集和验证集,根据标注文件实例化 pycocotools.coco.COCO。
concurrent.futures.Executor 是一个抽象类,提供异步执行调用的方法。要通过它的子类调用,而不是直接调用。
concurrent.futures.Executor.submit 调度可调用对象fn
,以fn(*args **kwargs)
方式执行并返回代表可调用对象的执行的 Future 对像。
concurrent.futures.ProcessPoolExecutor 是 Executor 的子类,使用最多max_workers
个进程的进程池异步执行调用。如果 max_workers
为None
或未给出,则默认为计算机上的处理器数。 如果max_workers
低于或等于0,则会引发 ValueError。在 Windows 上,max_workers
必须小于等于61.如果不是,则会引发 ValueError。如果max_workers
为None
,那么即使有更多的处理器可用,默认值也最多为61。mp_context
可以是一个多进程上下文或是None
。 用它来启动工作者。如果mp_context
为None
或未给出,将使用默认的多进程上下文。
initializer
是一个可选的 callable,在每个 worker 进程的开头调用; initargs
是传递给初始化器的参数元组。如果初始化程序引发异常,则所有当前挂起的作业以及向池中提交更多作业的任何尝试都将引发 BrokenProcessPool。
crop_img 从图上根据目标框
loadImgs 加载指定 ID 的图片。
loadAnns 加载指定 ID 的注释。
getAnnIds 获取满足给定过滤条件的标注 ID。默认跳过该过滤器。
printProgress 打印进程状态。
for dataType in ['val2017', 'train2017']:
set_crop_base_path = join(crop_path, dataType)
set_img_base_path = join(dataDir, dataType)
annFile = '{}/annotations/instances_{}.json'.format(dataDir,dataType)
coco = COCO(annFile)
n_imgs = len(coco.imgs)
with futures.ProcessPoolExecutor(max_workers=num_threads) as executor:
fs = [executor.submit(crop_img, coco.loadImgs(id)[0],
coco.loadAnns(coco.getAnnIds(imgIds=id, iscrowd=None)),
set_crop_base_path, set_img_base_path, instanc_size) for id in coco.imgs]
for i, f in enumerate(futures.as_completed(fs)):
# Write progress to error so that it can be seen
printProgress(i, n_imgs, prefix=dataType, suffix='Done ', barLength=40)
print('done')
crop_img
从img['file_name']
读取文件名。以该名称创建文件夹。
读取图像,根据每个目标框截取后保存。
crop_like_SiamFC 像 SiamFC 那样截取,即外扩截取后缩放到指定大小。
frame_crop_base_path = join(set_crop_base_path, img['file_name'].split('/')[-1].split('.')[0])
if not isdir(frame_crop_base_path): makedirs(frame_crop_base_path)
im = cv2.imread('{}/{}'.format(set_img_base_path, img['file_name']))
avg_chans = np.mean(im, axis=(0, 1))
for trackid, ann in enumerate(anns):
rect = ann['bbox']
bbox = [rect[0], rect[1], rect[0] + rect[2], rect[1] + rect[3]]
if rect[2] <= 0 or rect[3] <=0:
continue
z, x = crop_like_SiamFC(im, bbox, instanc_size=instanc_size, padding=avg_chans)
cv2.imwrite(join(frame_crop_base_path, '{:06d}.{:02d}.z.jpg'.format(0, trackid)), z)
cv2.imwrite(join(frame_crop_base_path, '{:06d}.{:02d}.x.jpg'.format(0, trackid)), x)
crop_like_SiamFC
由[x1, y1, x2, y2]
的框得到中心和大小。
pos_s_2_bbox 根据中心位置和大小解码出框。
crop_hwc 截取图像块并缩放为指定大小。
target_pos = [(bbox[2]+bbox[0])/2., (bbox[3]+bbox[1])/2.]
target_size = [bbox[2]-bbox[0], bbox[3]-bbox[1]]
wc_z = target_size[1] + context_amount * sum(target_size)
hc_z = target_size[0] + context_amount * sum(target_size)
s_z = np.sqrt(wc_z * hc_z)
scale_z = exemplar_size / s_z
d_search = (instanc_size - exemplar_size) / 2
pad = d_search / scale_z
s_x = s_z + 2 * pad
z = crop_hwc(image, pos_s_2_bbox(target_pos, s_z), exemplar_size, padding)
x = crop_hwc(image, pos_s_2_bbox(target_pos, s_x), instanc_size, padding)
return z, x
crop_hwc
a
和b
分别为宽高的比例系数。
c
和d
分别为宽高方向的平移量。
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)
return crop
printProgress
"""
Call in a loop to create terminal progress bar
@params:
iteration - Required : current iteration (Int)
total - Required : total iterations (Int)
prefix - Optional : prefix string (Str)
suffix - Optional : suffix string (Str)
decimals - Optional : positive number of decimals in percent complete (Int)
barLength - Optional : character length of bar (Int)
"""
formatStr = "{0:." + str(decimals) + "f}"
percents = formatStr.format(100 * (iteration / float(total)))
filledLength = int(round(barLength * iteration / float(total)))
bar = '' * filledLength + '-' * (barLength - filledLength)
sys.stdout.write('\r%s |%s| %s%s %s' % (prefix, bar, percents, '%', suffix)),
if iteration == total:
sys.stdout.write('\x1b[2K\r')
sys.stdout.flush()