//  [python]
def align(calib, scale, im_left, im_right, \
		  box_left, keypoints, poses):
	''' Dense alignment for multiple objects

	Inputs:
		scale: H_im_left/H_origin_img
		im_left, im_right: 1 x 3 x H x W
		box_left: rois x 4 in origin image
		keypoints: rois x 5 (kpt, kpt_type, prob, left_border, right_border in origin image
		status: rois x 1: truncation, w/o; 2: normal case
		poses: rois x 7 (x, y, z, w, h, l, theta)
	Return:
		solve_status: 1 denotes success, 0 denotes faild due to no valid pixel (rois x 1)
		best_dis: aligned disparity in origin image (rois x 1)

	'''
	scale = scale * 2
	im_left = F.interpolate(im_left, scale_factor=2, mode='bilinear')
	im_right = F.interpolate(im_right, scale_factor=2, mode='bilinear')

	f = calib.p2[0,0]*scale
	bl = (calib.p2[0,3] - calib.p3[0,3])*scale/f
	box_left = box_left*scale
	keypoints = keypoints*scale

	dis_init = f*bl/poses[:,2]
	dis_init = dis_init.cuda()

	all_uvz, all_weight = sample(calib, scale, im_left.size(2), im_left.size(3), \
								 box_left, poses, keypoints[:,3:5])

	solve_status = box_left.new(box_left.size(0)).zero_()
	if torch.sum(all_weight) == 0:
		return solve_status, dis_init

	solve_status = solve_status + 1.0
	solve_status[torch.sum(all_weight,1)==0] = 0
	
	f_h = float(im_left.size()[2])-1
	f_w = float(im_left.size()[3])-1

	grid_left = all_uvz.new(1, all_uvz.size(0), all_uvz.size(1), 2).zero_() # 1 x rois x pixels x 2
	grid_right = all_uvz.new(1, all_uvz.size(0), all_uvz.size(1), 2).zero_() # 1 x rois x pixels x 2
	
	grid_left[0,:,:,0] = (all_uvz[:,:,0] - f_w/2)/(f_w/2)
	grid_left[0,:,:,1] = (all_uvz[:,:,1] - f_h/2)/(f_h/2)
	grid_right[0,:,:,1] = (all_uvz[:,:,1] - f_h/2)/(f_h/2)
	
	all_weight = all_weight.unsqueeze(1).expand(-1, im_left.size(1), -1)   # (rois, 3L, num)

	iter_num = 50
	error_minimum = box_left.new(box_left.size(0)).zero_() +  100000000
	depth_interval = box_left.new(box_left.size(0)).zero_() + 0.5
	depth = dis_init.reciprocal()*f*bl - iter_num*depth_interval/2

	best_depth = depth
	
	for i in range(iter_num):
		dis = depth.reciprocal()*f*bl
		delta_d = (all_uvz[:,:,2]/(f*bl) + dis.reciprocal().unsqueeze(1).expand(-1,all_uvz.size(1))).reciprocal() # num calculate delta disparity for each pixel
		grid_right[0,:,:,0] = (all_uvz[:,:,0] - delta_d - f_w/2)/(f_w/2)
		error = F.grid_sample(im_left,grid_left, padding_mode='border')-\
				F.grid_sample(im_right,grid_right, padding_mode='border')  # (1L, 3L, rois, num)
		
		error = error.squeeze(0).data 					 		 # (3L, rois, num)
		error_pixel = error.permute(1,0,2).contiguous() 	 	 # (rois, 3L, num)
		
		error = error * all_weight
		error = error_pixel.view(error_pixel.size(0),-1) 		 # rois x (3 x num)
		
		error_sum = torch.sum(torch.abs(error),1) 				 # rois 
		update_inds = (error_sum < error_minimum) &\
						(dis - dis_init <= 50.0) & (dis - dis_init >= -50.0)
		
		best_depth[update_inds] = depth[update_inds]
		error_minimum[update_inds] = error_sum[update_inds]

		depth = depth+depth_interval 
	
	tune_num = 20
	tune_depth_interval = depth_interval*2/tune_num
	depth = best_depth - depth_interval
	for i in range(tune_num):

		dis = depth.reciprocal()*f*bl
		delta_d = (all_uvz[:,:,2]/(f*bl) + dis.reciprocal().unsqueeze(1).expand(-1,all_uvz.size(1))).reciprocal() # num calculate delta disparity for each pixel
		grid_right[0,:,:,0] = (all_uvz[:,:,0] - delta_d - f_w/2)/(f_w/2)
		error = F.grid_sample(im_left,grid_left, padding_mode='border')-\
				F.grid_sample(im_right,grid_right, padding_mode='border')  # (1L, 3L, rois, num)
		error = error.squeeze(0).data 					 		 # (3L, rois, num)
		error_pixel = error.permute(1,0,2).contiguous() 	 	 # (rois, 3L, num)
		error = error * all_weight
		error = error_pixel.view(error_pixel.size(0),-1) 		 # rois x (3 x num)
		
		error_sum = torch.sum(torch.abs(error),1)
		update_inds = error_sum < error_minimum
		best_depth[update_inds] = depth[update_inds]
		error_minimum[update_inds] = error_sum[update_inds]

		depth = depth+tune_depth_interval

	best_dis = f*bl/(best_depth*scale) + 0.5
	return solve_status, best_dis