class TestResize: @cpu_only @pytest.mark.parametrize('size', [32, 34, 35, 36, 38]) def test_resize_int(self, size): # TODO: Minimal check for bug-fix, improve this later x = torch.rand(3, 32, 46) t = T.Resize(size=size) y = t(x) # If size is an int, smaller edge of the image will be matched to this number. # i.e, if height > width, then image will be rescaled to (size * height / width, size). assert isinstance(y, torch.Tensor) assert y.shape[1] == size assert y.shape[2] == int(size * 46 / 32) @pytest.mark.parametrize('device', cpu_and_gpu()) @pytest.mark.parametrize('dt', [None, torch.float32, torch.float64]) @pytest.mark.parametrize('size', [[32, ], [32, 32], (32, 32), [34, 35]]) @pytest.mark.parametrize('max_size', [None, 35, 1000]) @pytest.mark.parametrize('interpolation', [BILINEAR, BICUBIC, NEAREST]) def test_resize_scripted(self, dt, size, max_size, interpolation, device): tensor, _ = _create_data(height=34, width=36, device=device) batch_tensors = torch.randint(0, 256, size=(4, 3, 44, 56), dtype=torch.uint8, device=device) if dt is not None: # This is a trivial cast to float of uint8 data to test all cases tensor = tensor.to(dt) if max_size is not None and len(size) != 1: pytest.xfail("with max_size, size must be a sequence with 2 elements") transform = T.Resize(size=size, interpolation=interpolation, max_size=max_size) s_transform = torch.jit.script(transform) _test_transform_vs_scripted(transform, s_transform, tensor) _test_transform_vs_scripted_on_batch(transform, s_transform, batch_tensors) @cpu_only def test_resize_save(self): transform = T.Resize(size=[32, ]) s_transform = torch.jit.script(transform) with get_tmp_dir() as tmp_dir: s_transform.save(os.path.join(tmp_dir, "t_resize.pt")) @pytest.mark.parametrize('device', cpu_and_gpu()) @pytest.mark.parametrize('scale', [(0.7, 1.2), [0.7, 1.2]]) @pytest.mark.parametrize('ratio', [(0.75, 1.333), [0.75, 1.333]]) @pytest.mark.parametrize('size', [(32,), [44, ], [32, ], [32, 32], (32, 32), [44, 55]]) @pytest.mark.parametrize('interpolation', [NEAREST, BILINEAR, BICUBIC]) def test_resized_crop(self, scale, ratio, size, interpolation, device): tensor = torch.randint(0, 256, size=(3, 44, 56), dtype=torch.uint8, device=device) batch_tensors = torch.randint(0, 256, size=(4, 3, 44, 56), dtype=torch.uint8, device=device) transform = T.RandomResizedCrop(size=size, scale=scale, ratio=ratio, interpolation=interpolation) s_transform = torch.jit.script(transform) _test_transform_vs_scripted(transform, s_transform, tensor) _test_transform_vs_scripted_on_batch(transform, s_transform, batch_tensors) @cpu_only def test_resized_crop_save(self): transform = T.RandomResizedCrop(size=[32, ]) s_transform = torch.jit.script(transform) with get_tmp_dir() as tmp_dir: s_transform.save(os.path.join(tmp_dir, "t_resized_crop.pt"))
import pytest import test_models as TM import torch import torchvision.prototype.models.depth.stereo.raft_stereo as raft_stereo from common_utils import cpu_and_gpu, set_rng_seed @pytest.mark.parametrize("model_builder", (raft_stereo.raft_stereo_base, raft_stereo.raft_stereo_realtime)) @pytest.mark.parametrize("model_mode", ("standard", "scripted")) @pytest.mark.parametrize("dev", cpu_and_gpu()) def test_raft_stereo(model_builder, model_mode, dev): # A simple test to make sure the model can do forward pass and jit scriptable set_rng_seed(0) # Use corr_pyramid and corr_block with smaller num_levels and radius to prevent nan output # get the idea from test_models.test_raft corr_pyramid = raft_stereo.CorrPyramid1d(num_levels=2) corr_block = raft_stereo.CorrBlock1d(num_levels=2, radius=2) model = model_builder(corr_pyramid=corr_pyramid, corr_block=corr_block).eval().to(dev) if model_mode == "scripted": model = torch.jit.script(model) img1 = torch.rand(1, 3, 64, 64).to(dev) img2 = torch.rand(1, 3, 64, 64).to(dev) num_iters = 3 preds = model(img1, img2, num_iters=num_iters) depth_pred = preds[-1] assert len(preds) == num_iters, "Number of predictions should be the same as model.num_iters"
t = models.detection.transform.GeneralizedRCNNTransform(min_size=min_size, max_size=max_size, image_mean=image_mean, image_std=image_std) # Check integrity of object __repr__ attribute expected_string = 'GeneralizedRCNNTransform(' _indent = '\n ' expected_string += '{0}Normalize(mean={1}, std={2})'.format(_indent, image_mean, image_std) expected_string += '{0}Resize(min_size=({1},), max_size={2}, '.format(_indent, min_size, max_size) expected_string += "mode='bilinear')\n)" assert t.__repr__() == expected_string @pytest.mark.parametrize('model_name', get_available_classification_models()) @pytest.mark.parametrize('dev', cpu_and_gpu()) def test_classification_model(model_name, dev): set_rng_seed(0) defaults = { 'num_classes': 50, 'input_shape': (1, 3, 224, 224), } kwargs = {**defaults, **_model_params.get(model_name, {})} input_shape = kwargs.pop('input_shape') model = models.__dict__[model_name](**kwargs) model.eval().to(device=dev) # RNG always on CPU, to ensure x in cuda tests is bitwise identical to x in cpu tests x = torch.rand(input_shape).to(device=dev) out = model(x) _assert_expected(out.cpu(), model_name, prec=0.1)
class TestDeformConv: dtype = torch.float64 def expected_fn(self, x, weight, offset, mask, bias, stride=1, padding=0, dilation=1): stride_h, stride_w = _pair(stride) pad_h, pad_w = _pair(padding) dil_h, dil_w = _pair(dilation) weight_h, weight_w = weight.shape[-2:] n_batches, n_in_channels, in_h, in_w = x.shape n_out_channels = weight.shape[0] out_h = (in_h + 2 * pad_h - (dil_h * (weight_h - 1) + 1)) // stride_h + 1 out_w = (in_w + 2 * pad_w - (dil_w * (weight_w - 1) + 1)) // stride_w + 1 n_offset_grps = offset.shape[1] // (2 * weight_h * weight_w) in_c_per_offset_grp = n_in_channels // n_offset_grps n_weight_grps = n_in_channels // weight.shape[1] in_c_per_weight_grp = weight.shape[1] out_c_per_weight_grp = n_out_channels // n_weight_grps out = torch.zeros(n_batches, n_out_channels, out_h, out_w, device=x.device, dtype=x.dtype) for b in range(n_batches): for c_out in range(n_out_channels): for i in range(out_h): for j in range(out_w): for di in range(weight_h): for dj in range(weight_w): for c in range(in_c_per_weight_grp): weight_grp = c_out // out_c_per_weight_grp c_in = weight_grp * in_c_per_weight_grp + c offset_grp = c_in // in_c_per_offset_grp mask_idx = offset_grp * (weight_h * weight_w) + di * weight_w + dj offset_idx = 2 * mask_idx pi = stride_h * i - pad_h + dil_h * di + offset[b, offset_idx, i, j] pj = stride_w * j - pad_w + dil_w * dj + offset[b, offset_idx + 1, i, j] mask_value = 1.0 if mask is not None: mask_value = mask[b, mask_idx, i, j] out[b, c_out, i, j] += (mask_value * weight[c_out, c, di, dj] * bilinear_interpolate(x[b, c_in, :, :], pi, pj)) out += bias.view(1, n_out_channels, 1, 1) return out @lru_cache(maxsize=None) def get_fn_args(self, device, contiguous, batch_sz, dtype): n_in_channels = 6 n_out_channels = 2 n_weight_grps = 2 n_offset_grps = 3 stride = (2, 1) pad = (1, 0) dilation = (2, 1) stride_h, stride_w = stride pad_h, pad_w = pad dil_h, dil_w = dilation weight_h, weight_w = (3, 2) in_h, in_w = (5, 4) out_h = (in_h + 2 * pad_h - (dil_h * (weight_h - 1) + 1)) // stride_h + 1 out_w = (in_w + 2 * pad_w - (dil_w * (weight_w - 1) + 1)) // stride_w + 1 x = torch.rand(batch_sz, n_in_channels, in_h, in_w, device=device, dtype=dtype, requires_grad=True) offset = torch.randn(batch_sz, n_offset_grps * 2 * weight_h * weight_w, out_h, out_w, device=device, dtype=dtype, requires_grad=True) mask = torch.randn(batch_sz, n_offset_grps * weight_h * weight_w, out_h, out_w, device=device, dtype=dtype, requires_grad=True) weight = torch.randn(n_out_channels, n_in_channels // n_weight_grps, weight_h, weight_w, device=device, dtype=dtype, requires_grad=True) bias = torch.randn(n_out_channels, device=device, dtype=dtype, requires_grad=True) if not contiguous: x = x.permute(0, 1, 3, 2).contiguous().permute(0, 1, 3, 2) offset = offset.permute(1, 3, 0, 2).contiguous().permute(2, 0, 3, 1) mask = mask.permute(1, 3, 0, 2).contiguous().permute(2, 0, 3, 1) weight = weight.permute(3, 2, 0, 1).contiguous().permute(2, 3, 1, 0) return x, weight, offset, mask, bias, stride, pad, dilation @pytest.mark.parametrize('device', cpu_and_gpu()) @pytest.mark.parametrize('contiguous', (True, False)) @pytest.mark.parametrize('batch_sz', (0, 33)) def test_forward(self, device, contiguous, batch_sz, dtype=None): dtype = dtype or self.dtype x, _, offset, mask, _, stride, padding, dilation = self.get_fn_args(device, contiguous, batch_sz, dtype) in_channels = 6 out_channels = 2 kernel_size = (3, 2) groups = 2 tol = 2e-3 if dtype is torch.half else 1e-5 layer = ops.DeformConv2d(in_channels, out_channels, kernel_size, stride=stride, padding=padding, dilation=dilation, groups=groups).to(device=x.device, dtype=dtype) res = layer(x, offset, mask) weight = layer.weight.data bias = layer.bias.data expected = self.expected_fn(x, weight, offset, mask, bias, stride=stride, padding=padding, dilation=dilation) torch.testing.assert_close( res.to(expected), expected, rtol=tol, atol=tol, msg='\nres:\n{}\nexpected:\n{}'.format(res, expected) ) # no modulation test res = layer(x, offset) expected = self.expected_fn(x, weight, offset, None, bias, stride=stride, padding=padding, dilation=dilation) torch.testing.assert_close( res.to(expected), expected, rtol=tol, atol=tol, msg='\nres:\n{}\nexpected:\n{}'.format(res, expected) ) def test_wrong_sizes(self): in_channels = 6 out_channels = 2 kernel_size = (3, 2) groups = 2 x, _, offset, mask, _, stride, padding, dilation = self.get_fn_args('cpu', contiguous=True, batch_sz=10, dtype=self.dtype) layer = ops.DeformConv2d(in_channels, out_channels, kernel_size, stride=stride, padding=padding, dilation=dilation, groups=groups) with pytest.raises(RuntimeError, match="the shape of the offset"): wrong_offset = torch.rand_like(offset[:, :2]) layer(x, wrong_offset) with pytest.raises(RuntimeError, match=r'mask.shape\[1\] is not valid'): wrong_mask = torch.rand_like(mask[:, :2]) layer(x, offset, wrong_mask) @pytest.mark.parametrize('device', cpu_and_gpu()) @pytest.mark.parametrize('contiguous', (True, False)) @pytest.mark.parametrize('batch_sz', (0, 33)) def test_backward(self, device, contiguous, batch_sz): x, weight, offset, mask, bias, stride, padding, dilation = self.get_fn_args(device, contiguous, batch_sz, self.dtype) def func(x_, offset_, mask_, weight_, bias_): return ops.deform_conv2d(x_, offset_, weight_, bias_, stride=stride, padding=padding, dilation=dilation, mask=mask_) gradcheck(func, (x, offset, mask, weight, bias), nondet_tol=1e-5, fast_mode=True) def func_no_mask(x_, offset_, weight_, bias_): return ops.deform_conv2d(x_, offset_, weight_, bias_, stride=stride, padding=padding, dilation=dilation, mask=None) gradcheck(func_no_mask, (x, offset, weight, bias), nondet_tol=1e-5, fast_mode=True) @torch.jit.script def script_func(x_, offset_, mask_, weight_, bias_, stride_, pad_, dilation_): # type:(Tensor, Tensor, Tensor, Tensor, Tensor, Tuple[int, int], Tuple[int, int], Tuple[int, int])->Tensor return ops.deform_conv2d(x_, offset_, weight_, bias_, stride=stride_, padding=pad_, dilation=dilation_, mask=mask_) gradcheck(lambda z, off, msk, wei, bi: script_func(z, off, msk, wei, bi, stride, padding, dilation), (x, offset, mask, weight, bias), nondet_tol=1e-5, fast_mode=True) @torch.jit.script def script_func_no_mask(x_, offset_, weight_, bias_, stride_, pad_, dilation_): # type:(Tensor, Tensor, Tensor, Tensor, Tuple[int, int], Tuple[int, int], Tuple[int, int])->Tensor return ops.deform_conv2d(x_, offset_, weight_, bias_, stride=stride_, padding=pad_, dilation=dilation_, mask=None) gradcheck(lambda z, off, wei, bi: script_func_no_mask(z, off, wei, bi, stride, padding, dilation), (x, offset, weight, bias), nondet_tol=1e-5, fast_mode=True) @needs_cuda @pytest.mark.parametrize('contiguous', (True, False)) def test_compare_cpu_cuda_grads(self, contiguous): # Test from https://github.com/pytorch/vision/issues/2598 # Run on CUDA only # compare grads computed on CUDA with grads computed on CPU true_cpu_grads = None init_weight = torch.randn(9, 9, 3, 3, requires_grad=True) img = torch.randn(8, 9, 1000, 110) offset = torch.rand(8, 2 * 3 * 3, 1000, 110) mask = torch.rand(8, 3 * 3, 1000, 110) if not contiguous: img = img.permute(0, 1, 3, 2).contiguous().permute(0, 1, 3, 2) offset = offset.permute(1, 3, 0, 2).contiguous().permute(2, 0, 3, 1) mask = mask.permute(1, 3, 0, 2).contiguous().permute(2, 0, 3, 1) weight = init_weight.permute(3, 2, 0, 1).contiguous().permute(2, 3, 1, 0) else: weight = init_weight for d in ["cpu", "cuda"]: out = ops.deform_conv2d(img.to(d), offset.to(d), weight.to(d), padding=1, mask=mask.to(d)) out.mean().backward() if true_cpu_grads is None: true_cpu_grads = init_weight.grad assert true_cpu_grads is not None else: assert init_weight.grad is not None res_grads = init_weight.grad.to("cpu") torch.testing.assert_close(true_cpu_grads, res_grads) @needs_cuda @pytest.mark.parametrize('batch_sz', (0, 33)) @pytest.mark.parametrize('dtype', (torch.float, torch.half)) def test_autocast(self, batch_sz, dtype): with torch.cuda.amp.autocast(): self.test_forward(torch.device("cuda"), contiguous=False, batch_sz=batch_sz, dtype=dtype) def test_forward_scriptability(self): # Non-regression test for https://github.com/pytorch/vision/issues/4078 torch.jit.script(ops.DeformConv2d(in_channels=8, out_channels=8, kernel_size=3))
class TestRoIAlign(RoIOpTester): def fn(self, x, rois, pool_h, pool_w, spatial_scale=1, sampling_ratio=-1, aligned=False, **kwargs): return ops.RoIAlign((pool_h, pool_w), spatial_scale=spatial_scale, sampling_ratio=sampling_ratio, aligned=aligned)(x, rois) def get_script_fn(self, rois, pool_size): scriped = torch.jit.script(ops.roi_align) return lambda x: scriped(x, rois, pool_size) def expected_fn(self, in_data, rois, pool_h, pool_w, spatial_scale=1, sampling_ratio=-1, aligned=False, device=None, dtype=torch.float64): if device is None: device = torch.device("cpu") n_channels = in_data.size(1) out_data = torch.zeros(rois.size(0), n_channels, pool_h, pool_w, dtype=dtype, device=device) offset = 0.5 if aligned else 0. for r, roi in enumerate(rois): batch_idx = int(roi[0]) j_begin, i_begin, j_end, i_end = (x.item() * spatial_scale - offset for x in roi[1:]) roi_h = i_end - i_begin roi_w = j_end - j_begin bin_h = roi_h / pool_h bin_w = roi_w / pool_w for i in range(0, pool_h): start_h = i_begin + i * bin_h grid_h = sampling_ratio if sampling_ratio > 0 else int(np.ceil(bin_h)) for j in range(0, pool_w): start_w = j_begin + j * bin_w grid_w = sampling_ratio if sampling_ratio > 0 else int(np.ceil(bin_w)) for channel in range(0, n_channels): val = 0 for iy in range(0, grid_h): y = start_h + (iy + 0.5) * bin_h / grid_h for ix in range(0, grid_w): x = start_w + (ix + 0.5) * bin_w / grid_w val += bilinear_interpolate(in_data[batch_idx, channel, :, :], y, x, snap_border=True) val /= grid_h * grid_w out_data[r, channel, i, j] = val return out_data def test_boxes_shape(self): self._helper_boxes_shape(ops.roi_align) @pytest.mark.parametrize('aligned', (True, False)) @pytest.mark.parametrize('device', cpu_and_gpu()) @pytest.mark.parametrize('contiguous', (True, False)) def test_forward(self, device, contiguous, aligned, x_dtype=None, rois_dtype=None): super().test_forward(device=device, contiguous=contiguous, x_dtype=x_dtype, rois_dtype=rois_dtype, aligned=aligned) @needs_cuda @pytest.mark.parametrize('aligned', (True, False)) @pytest.mark.parametrize('x_dtype', (torch.float, torch.half)) @pytest.mark.parametrize('rois_dtype', (torch.float, torch.half)) def test_autocast(self, aligned, x_dtype, rois_dtype): with torch.cuda.amp.autocast(): self.test_forward(torch.device("cuda"), contiguous=False, aligned=aligned, x_dtype=x_dtype, rois_dtype=rois_dtype) def _make_rois(self, img_size, num_imgs, dtype, num_rois=1000): rois = torch.randint(0, img_size // 2, size=(num_rois, 5)).to(dtype) rois[:, 0] = torch.randint(0, num_imgs, size=(num_rois,)) # set batch index rois[:, 3:] += rois[:, 1:3] # make sure boxes aren't degenerate return rois @pytest.mark.parametrize('aligned', (True, False)) @pytest.mark.parametrize('scale, zero_point', ((1, 0), (2, 10), (0.1, 50))) @pytest.mark.parametrize('qdtype', (torch.qint8, torch.quint8, torch.qint32)) def test_qroialign(self, aligned, scale, zero_point, qdtype): """Make sure quantized version of RoIAlign is close to float version""" pool_size = 5 img_size = 10 n_channels = 2 num_imgs = 1 dtype = torch.float x = torch.randint(50, 100, size=(num_imgs, n_channels, img_size, img_size)).to(dtype) qx = torch.quantize_per_tensor(x, scale=scale, zero_point=zero_point, dtype=qdtype) rois = self._make_rois(img_size, num_imgs, dtype) qrois = torch.quantize_per_tensor(rois, scale=scale, zero_point=zero_point, dtype=qdtype) x, rois = qx.dequantize(), qrois.dequantize() # we want to pass the same inputs y = ops.roi_align( x, rois, output_size=pool_size, spatial_scale=1, sampling_ratio=-1, aligned=aligned, ) qy = ops.roi_align( qx, qrois, output_size=pool_size, spatial_scale=1, sampling_ratio=-1, aligned=aligned, ) # The output qy is itself a quantized tensor and there might have been a loss of info when it was # quantized. For a fair comparison we need to quantize y as well quantized_float_y = torch.quantize_per_tensor(y, scale=scale, zero_point=zero_point, dtype=qdtype) try: # Ideally, we would assert this, which passes with (scale, zero) == (1, 0) assert (qy == quantized_float_y).all() except AssertionError: # But because the computation aren't exactly the same between the 2 RoIAlign procedures, some # rounding error may lead to a difference of 2 in the output. # For example with (scale, zero) = (2, 10), 45.00000... will be quantized to 44 # but 45.00000001 will be rounded to 46. We make sure below that: # - such discrepancies between qy and quantized_float_y are very rare (less then 5%) # - any difference between qy and quantized_float_y is == scale diff_idx = torch.where(qy != quantized_float_y) num_diff = diff_idx[0].numel() assert num_diff / qy.numel() < .05 abs_diff = torch.abs(qy[diff_idx].dequantize() - quantized_float_y[diff_idx].dequantize()) t_scale = torch.full_like(abs_diff, fill_value=scale) torch.testing.assert_close(abs_diff, t_scale, rtol=1e-5, atol=1e-5) def test_qroi_align_multiple_images(self): dtype = torch.float x = torch.randint(50, 100, size=(2, 3, 10, 10)).to(dtype) qx = torch.quantize_per_tensor(x, scale=1, zero_point=0, dtype=torch.qint8) rois = self._make_rois(img_size=10, num_imgs=2, dtype=dtype, num_rois=10) qrois = torch.quantize_per_tensor(rois, scale=1, zero_point=0, dtype=torch.qint8) with pytest.raises(RuntimeError, match="Only one image per batch is allowed"): ops.roi_align(qx, qrois, output_size=5)
class RoIOpTester(ABC): dtype = torch.float64 @pytest.mark.parametrize('device', cpu_and_gpu()) @pytest.mark.parametrize('contiguous', (True, False)) def test_forward(self, device, contiguous, x_dtype=None, rois_dtype=None, **kwargs): x_dtype = self.dtype if x_dtype is None else x_dtype rois_dtype = self.dtype if rois_dtype is None else rois_dtype pool_size = 5 # n_channels % (pool_size ** 2) == 0 required for PS opeartions. n_channels = 2 * (pool_size ** 2) x = torch.rand(2, n_channels, 10, 10, dtype=x_dtype, device=device) if not contiguous: x = x.permute(0, 1, 3, 2) rois = torch.tensor([[0, 0, 0, 9, 9], # format is (xyxy) [0, 0, 5, 4, 9], [0, 5, 5, 9, 9], [1, 0, 0, 9, 9]], dtype=rois_dtype, device=device) pool_h, pool_w = pool_size, pool_size y = self.fn(x, rois, pool_h, pool_w, spatial_scale=1, sampling_ratio=-1, **kwargs) # the following should be true whether we're running an autocast test or not. assert y.dtype == x.dtype gt_y = self.expected_fn(x, rois, pool_h, pool_w, spatial_scale=1, sampling_ratio=-1, device=device, dtype=self.dtype, **kwargs) tol = 1e-3 if (x_dtype is torch.half or rois_dtype is torch.half) else 1e-5 torch.testing.assert_close(gt_y.to(y), y, rtol=tol, atol=tol) @pytest.mark.parametrize('device', cpu_and_gpu()) @pytest.mark.parametrize('contiguous', (True, False)) def test_backward(self, device, contiguous): pool_size = 2 x = torch.rand(1, 2 * (pool_size ** 2), 5, 5, dtype=self.dtype, device=device, requires_grad=True) if not contiguous: x = x.permute(0, 1, 3, 2) rois = torch.tensor([[0, 0, 0, 4, 4], # format is (xyxy) [0, 0, 2, 3, 4], [0, 2, 2, 4, 4]], dtype=self.dtype, device=device) def func(z): return self.fn(z, rois, pool_size, pool_size, spatial_scale=1, sampling_ratio=1) script_func = self.get_script_fn(rois, pool_size) gradcheck(func, (x,)) gradcheck(script_func, (x,)) @needs_cuda @pytest.mark.parametrize('x_dtype', (torch.float, torch.half)) @pytest.mark.parametrize('rois_dtype', (torch.float, torch.half)) def test_autocast(self, x_dtype, rois_dtype): with torch.cuda.amp.autocast(): self.test_forward(torch.device("cuda"), contiguous=False, x_dtype=x_dtype, rois_dtype=rois_dtype) def _helper_boxes_shape(self, func): # test boxes as Tensor[N, 5] with pytest.raises(AssertionError): a = torch.linspace(1, 8 * 8, 8 * 8).reshape(1, 1, 8, 8) boxes = torch.tensor([[0, 0, 3, 3]], dtype=a.dtype) func(a, boxes, output_size=(2, 2)) # test boxes as List[Tensor[N, 4]] with pytest.raises(AssertionError): a = torch.linspace(1, 8 * 8, 8 * 8).reshape(1, 1, 8, 8) boxes = torch.tensor([[0, 0, 3]], dtype=a.dtype) ops.roi_pool(a, [boxes], output_size=(2, 2)) @abstractmethod def fn(*args, **kwargs): pass @abstractmethod def get_script_fn(*args, **kwargs): pass @abstractmethod def expected_fn(*args, **kwargs): pass
class TestRotate: ALL_DTYPES = [None, torch.float32, torch.float64, torch.float16] scripted_rotate = torch.jit.script(F.rotate) IMG_W = 26 @pytest.mark.parametrize('device', cpu_and_gpu()) @pytest.mark.parametrize('height, width', [(26, IMG_W), (32, IMG_W)]) @pytest.mark.parametrize('center', [ None, (int(IMG_W * 0.3), int(IMG_W * 0.4)), [int(IMG_W * 0.5), int(IMG_W * 0.6)], ]) @pytest.mark.parametrize('dt', ALL_DTYPES) @pytest.mark.parametrize('angle', range(-180, 180, 17)) @pytest.mark.parametrize('expand', [True, False]) @pytest.mark.parametrize('fill', [None, [0, 0, 0], (1, 2, 3), [255, 255, 255], [1, ], (2.0, )]) @pytest.mark.parametrize('fn', [F.rotate, scripted_rotate]) def test_rotate(self, device, height, width, center, dt, angle, expand, fill, fn): tensor, pil_img = _create_data(height, width, device=device) if dt == torch.float16 and torch.device(device).type == "cpu": # skip float16 on CPU case return if dt is not None: tensor = tensor.to(dtype=dt) f_pil = int(fill[0]) if fill is not None and len(fill) == 1 else fill out_pil_img = F.rotate(pil_img, angle=angle, interpolation=NEAREST, expand=expand, center=center, fill=f_pil) out_pil_tensor = torch.from_numpy(np.array(out_pil_img).transpose((2, 0, 1))) out_tensor = fn(tensor, angle=angle, interpolation=NEAREST, expand=expand, center=center, fill=fill).cpu() if out_tensor.dtype != torch.uint8: out_tensor = out_tensor.to(torch.uint8) assert out_tensor.shape == out_pil_tensor.shape, ( f"{(height, width, NEAREST, dt, angle, expand, center)}: " f"{out_tensor.shape} vs {out_pil_tensor.shape}") num_diff_pixels = (out_tensor != out_pil_tensor).sum().item() / 3.0 ratio_diff_pixels = num_diff_pixels / out_tensor.shape[-1] / out_tensor.shape[-2] # Tolerance : less than 3% of different pixels assert ratio_diff_pixels < 0.03, ( f"{(height, width, NEAREST, dt, angle, expand, center, fill)}: " f"{ratio_diff_pixels}\n{out_tensor[0, :7, :7]} vs \n" f"{out_pil_tensor[0, :7, :7]}") @pytest.mark.parametrize('device', cpu_and_gpu()) @pytest.mark.parametrize('dt', ALL_DTYPES) def test_rotate_batch(self, device, dt): if dt == torch.float16 and device == "cpu": # skip float16 on CPU case return batch_tensors = _create_data_batch(26, 36, num_samples=4, device=device) if dt is not None: batch_tensors = batch_tensors.to(dtype=dt) center = (20, 22) _test_fn_on_batch( batch_tensors, F.rotate, angle=32, interpolation=NEAREST, expand=True, center=center ) def test_rotate_deprecation_resample(self): tensor, _ = _create_data(26, 26) # assert deprecation warning and non-BC with pytest.warns(UserWarning, match=r"Argument resample is deprecated and will be removed"): res1 = F.rotate(tensor, 45, resample=2) res2 = F.rotate(tensor, 45, interpolation=BILINEAR) assert_equal(res1, res2) def test_rotate_interpolation_type(self): tensor, _ = _create_data(26, 26) # assert changed type warning with pytest.warns(UserWarning, match=r"Argument interpolation should be of type InterpolationMode"): res1 = F.rotate(tensor, 45, interpolation=2) res2 = F.rotate(tensor, 45, interpolation=BILINEAR) assert_equal(res1, res2)
class TestAffine: ALL_DTYPES = [None, torch.float32, torch.float64, torch.float16] scripted_affine = torch.jit.script(F.affine) @pytest.mark.parametrize('device', cpu_and_gpu()) @pytest.mark.parametrize('height, width', [(26, 26), (32, 26)]) @pytest.mark.parametrize('dt', ALL_DTYPES) def test_identity_map(self, device, height, width, dt): # Tests on square and rectangular images tensor, pil_img = _create_data(height, width, device=device) if dt == torch.float16 and device == "cpu": # skip float16 on CPU case return if dt is not None: tensor = tensor.to(dtype=dt) # 1) identity map out_tensor = F.affine(tensor, angle=0, translate=[0, 0], scale=1.0, shear=[0.0, 0.0], interpolation=NEAREST) assert_equal(tensor, out_tensor, msg="{} vs {}".format(out_tensor[0, :5, :5], tensor[0, :5, :5])) out_tensor = self.scripted_affine( tensor, angle=0, translate=[0, 0], scale=1.0, shear=[0.0, 0.0], interpolation=NEAREST ) assert_equal(tensor, out_tensor, msg="{} vs {}".format(out_tensor[0, :5, :5], tensor[0, :5, :5])) @pytest.mark.parametrize('device', cpu_and_gpu()) @pytest.mark.parametrize('height, width', [(26, 26)]) @pytest.mark.parametrize('dt', ALL_DTYPES) @pytest.mark.parametrize('angle, config', [ (90, {'k': 1, 'dims': (-1, -2)}), (45, None), (30, None), (-30, None), (-45, None), (-90, {'k': -1, 'dims': (-1, -2)}), (180, {'k': 2, 'dims': (-1, -2)}), ]) @pytest.mark.parametrize('fn', [F.affine, scripted_affine]) def test_square_rotations(self, device, height, width, dt, angle, config, fn): # 2) Test rotation tensor, pil_img = _create_data(height, width, device=device) if dt == torch.float16 and device == "cpu": # skip float16 on CPU case return if dt is not None: tensor = tensor.to(dtype=dt) out_pil_img = F.affine( pil_img, angle=angle, translate=[0, 0], scale=1.0, shear=[0.0, 0.0], interpolation=NEAREST ) out_pil_tensor = torch.from_numpy(np.array(out_pil_img).transpose((2, 0, 1))).to(device) out_tensor = fn( tensor, angle=angle, translate=[0, 0], scale=1.0, shear=[0.0, 0.0], interpolation=NEAREST ) if config is not None: assert_equal(torch.rot90(tensor, **config), out_tensor) if out_tensor.dtype != torch.uint8: out_tensor = out_tensor.to(torch.uint8) num_diff_pixels = (out_tensor != out_pil_tensor).sum().item() / 3.0 ratio_diff_pixels = num_diff_pixels / out_tensor.shape[-1] / out_tensor.shape[-2] # Tolerance : less than 6% of different pixels assert ratio_diff_pixels < 0.06, "{}\n{} vs \n{}".format( ratio_diff_pixels, out_tensor[0, :7, :7], out_pil_tensor[0, :7, :7] ) @pytest.mark.parametrize('device', cpu_and_gpu()) @pytest.mark.parametrize('height, width', [(32, 26)]) @pytest.mark.parametrize('dt', ALL_DTYPES) @pytest.mark.parametrize('angle', [90, 45, 15, -30, -60, -120]) @pytest.mark.parametrize('fn', [F.affine, scripted_affine]) def test_rect_rotations(self, device, height, width, dt, angle, fn): # Tests on rectangular images tensor, pil_img = _create_data(height, width, device=device) if dt == torch.float16 and device == "cpu": # skip float16 on CPU case return if dt is not None: tensor = tensor.to(dtype=dt) out_pil_img = F.affine( pil_img, angle=angle, translate=[0, 0], scale=1.0, shear=[0.0, 0.0], interpolation=NEAREST ) out_pil_tensor = torch.from_numpy(np.array(out_pil_img).transpose((2, 0, 1))) out_tensor = fn( tensor, angle=angle, translate=[0, 0], scale=1.0, shear=[0.0, 0.0], interpolation=NEAREST ).cpu() if out_tensor.dtype != torch.uint8: out_tensor = out_tensor.to(torch.uint8) num_diff_pixels = (out_tensor != out_pil_tensor).sum().item() / 3.0 ratio_diff_pixels = num_diff_pixels / out_tensor.shape[-1] / out_tensor.shape[-2] # Tolerance : less than 3% of different pixels assert ratio_diff_pixels < 0.03, "{}: {}\n{} vs \n{}".format( angle, ratio_diff_pixels, out_tensor[0, :7, :7], out_pil_tensor[0, :7, :7] ) @pytest.mark.parametrize('device', cpu_and_gpu()) @pytest.mark.parametrize('height, width', [(26, 26), (32, 26)]) @pytest.mark.parametrize('dt', ALL_DTYPES) @pytest.mark.parametrize('t', [[10, 12], (-12, -13)]) @pytest.mark.parametrize('fn', [F.affine, scripted_affine]) def test_translations(self, device, height, width, dt, t, fn): # 3) Test translation tensor, pil_img = _create_data(height, width, device=device) if dt == torch.float16 and device == "cpu": # skip float16 on CPU case return if dt is not None: tensor = tensor.to(dtype=dt) out_pil_img = F.affine(pil_img, angle=0, translate=t, scale=1.0, shear=[0.0, 0.0], interpolation=NEAREST) out_tensor = fn(tensor, angle=0, translate=t, scale=1.0, shear=[0.0, 0.0], interpolation=NEAREST) if out_tensor.dtype != torch.uint8: out_tensor = out_tensor.to(torch.uint8) _assert_equal_tensor_to_pil(out_tensor, out_pil_img) @pytest.mark.parametrize('device', cpu_and_gpu()) @pytest.mark.parametrize('height, width', [(26, 26), (32, 26)]) @pytest.mark.parametrize('dt', ALL_DTYPES) @pytest.mark.parametrize('a, t, s, sh, f', [ (45.5, [5, 6], 1.0, [0.0, 0.0], None), (33, (5, -4), 1.0, [0.0, 0.0], [0, 0, 0]), (45, [-5, 4], 1.2, [0.0, 0.0], (1, 2, 3)), (33, (-4, -8), 2.0, [0.0, 0.0], [255, 255, 255]), (85, (10, -10), 0.7, [0.0, 0.0], [1, ]), (0, [0, 0], 1.0, [35.0, ], (2.0, )), (-25, [0, 0], 1.2, [0.0, 15.0], None), (-45, [-10, 0], 0.7, [2.0, 5.0], None), (-45, [-10, -10], 1.2, [4.0, 5.0], None), (-90, [0, 0], 1.0, [0.0, 0.0], None), ]) @pytest.mark.parametrize('fn', [F.affine, scripted_affine]) def test_all_ops(self, device, height, width, dt, a, t, s, sh, f, fn): # 4) Test rotation + translation + scale + shear tensor, pil_img = _create_data(height, width, device=device) if dt == torch.float16 and device == "cpu": # skip float16 on CPU case return if dt is not None: tensor = tensor.to(dtype=dt) f_pil = int(f[0]) if f is not None and len(f) == 1 else f out_pil_img = F.affine(pil_img, angle=a, translate=t, scale=s, shear=sh, interpolation=NEAREST, fill=f_pil) out_pil_tensor = torch.from_numpy(np.array(out_pil_img).transpose((2, 0, 1))) out_tensor = fn(tensor, angle=a, translate=t, scale=s, shear=sh, interpolation=NEAREST, fill=f).cpu() if out_tensor.dtype != torch.uint8: out_tensor = out_tensor.to(torch.uint8) num_diff_pixels = (out_tensor != out_pil_tensor).sum().item() / 3.0 ratio_diff_pixels = num_diff_pixels / out_tensor.shape[-1] / out_tensor.shape[-2] # Tolerance : less than 5% (cpu), 6% (cuda) of different pixels tol = 0.06 if device == "cuda" else 0.05 assert ratio_diff_pixels < tol, "{}: {}\n{} vs \n{}".format( (NEAREST, a, t, s, sh, f), ratio_diff_pixels, out_tensor[0, :7, :7], out_pil_tensor[0, :7, :7] ) @pytest.mark.parametrize('device', cpu_and_gpu()) @pytest.mark.parametrize('dt', ALL_DTYPES) def test_batches(self, device, dt): if dt == torch.float16 and device == "cpu": # skip float16 on CPU case return batch_tensors = _create_data_batch(26, 36, num_samples=4, device=device) if dt is not None: batch_tensors = batch_tensors.to(dtype=dt) _test_fn_on_batch( batch_tensors, F.affine, angle=-43, translate=[-3, 4], scale=1.2, shear=[4.0, 5.0] ) @pytest.mark.parametrize('device', cpu_and_gpu()) def test_warnings(self, device): tensor, pil_img = _create_data(26, 26, device=device) # assert deprecation warning and non-BC with pytest.warns(UserWarning, match=r"Argument resample is deprecated and will be removed"): res1 = F.affine(tensor, 45, translate=[0, 0], scale=1.0, shear=[0.0, 0.0], resample=2) res2 = F.affine(tensor, 45, translate=[0, 0], scale=1.0, shear=[0.0, 0.0], interpolation=BILINEAR) assert_equal(res1, res2) # assert changed type warning with pytest.warns(UserWarning, match=r"Argument interpolation should be of type InterpolationMode"): res1 = F.affine(tensor, 45, translate=[0, 0], scale=1.0, shear=[0.0, 0.0], interpolation=2) res2 = F.affine(tensor, 45, translate=[0, 0], scale=1.0, shear=[0.0, 0.0], interpolation=BILINEAR) assert_equal(res1, res2) with pytest.warns(UserWarning, match=r"Argument fillcolor is deprecated and will be removed"): res1 = F.affine(pil_img, 45, translate=[0, 0], scale=1.0, shear=[0.0, 0.0], fillcolor=10) res2 = F.affine(pil_img, 45, translate=[0, 0], scale=1.0, shear=[0.0, 0.0], fill=10) # we convert the PIL images to numpy as assert_equal doesn't work on PIL images. assert_equal(np.asarray(res1), np.asarray(res2))
class TestResize: @pytest.mark.parametrize("size", [32, 34, 35, 36, 38]) def test_resize_int(self, size): # TODO: Minimal check for bug-fix, improve this later x = torch.rand(3, 32, 46) t = T.Resize(size=size) y = t(x) # If size is an int, smaller edge of the image will be matched to this number. # i.e, if height > width, then image will be rescaled to (size * height / width, size). assert isinstance(y, torch.Tensor) assert y.shape[1] == size assert y.shape[2] == int(size * 46 / 32) @pytest.mark.parametrize("device", cpu_and_gpu()) @pytest.mark.parametrize("dt", [None, torch.float32, torch.float64]) @pytest.mark.parametrize( "size", [ [32], [32, 32], (32, 32), [34, 35], ], ) @pytest.mark.parametrize("max_size", [None, 35, 1000]) @pytest.mark.parametrize("interpolation", [BILINEAR, BICUBIC, NEAREST]) def test_resize_scripted(self, dt, size, max_size, interpolation, device): tensor, _ = _create_data(height=34, width=36, device=device) batch_tensors = torch.randint(0, 256, size=(4, 3, 44, 56), dtype=torch.uint8, device=device) if dt is not None: # This is a trivial cast to float of uint8 data to test all cases tensor = tensor.to(dt) if max_size is not None and len(size) != 1: pytest.skip("Size should be an int or a sequence of length 1 if max_size is specified") transform = T.Resize(size=size, interpolation=interpolation, max_size=max_size) s_transform = torch.jit.script(transform) _test_transform_vs_scripted(transform, s_transform, tensor) _test_transform_vs_scripted_on_batch(transform, s_transform, batch_tensors) def test_resize_save(self, tmpdir): transform = T.Resize(size=[32]) s_transform = torch.jit.script(transform) s_transform.save(os.path.join(tmpdir, "t_resize.pt")) @pytest.mark.parametrize("device", cpu_and_gpu()) @pytest.mark.parametrize("scale", [(0.7, 1.2), [0.7, 1.2]]) @pytest.mark.parametrize("ratio", [(0.75, 1.333), [0.75, 1.333]]) @pytest.mark.parametrize( "size", [ (32,), [44], [32], [32, 32], (32, 32), [44, 55], ], ) @pytest.mark.parametrize("interpolation", [NEAREST, BILINEAR, BICUBIC]) @pytest.mark.parametrize("antialias", [None, True, False]) def test_resized_crop(self, scale, ratio, size, interpolation, antialias, device): if antialias and interpolation == NEAREST: pytest.skip("Can not resize if interpolation mode is NEAREST and antialias=True") tensor = torch.randint(0, 256, size=(3, 44, 56), dtype=torch.uint8, device=device) batch_tensors = torch.randint(0, 256, size=(4, 3, 44, 56), dtype=torch.uint8, device=device) transform = T.RandomResizedCrop( size=size, scale=scale, ratio=ratio, interpolation=interpolation, antialias=antialias ) s_transform = torch.jit.script(transform) _test_transform_vs_scripted(transform, s_transform, tensor) _test_transform_vs_scripted_on_batch(transform, s_transform, batch_tensors) def test_resized_crop_save(self, tmpdir): transform = T.RandomResizedCrop( size=[ 32, ] ) s_transform = torch.jit.script(transform) s_transform.save(os.path.join(tmpdir, "t_resized_crop.pt"))