Skip to content

Commit 588f7ae

Browse files
jamt9000vfdev-5
andauthored
Negative padding for functional_pil #2381 (#2744)
* Negative padding for functional_pil #2381 * Tests for PIL negative padding #2381 * Move PIL vs tensor test inside test_pad * Adapt test_pad from test_transforms_tensor.py Co-authored-by: vfdev <[email protected]>
1 parent a9a8220 commit 588f7ae

File tree

3 files changed

+43
-20
lines changed

3 files changed

+43
-20
lines changed

test/test_transforms.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,16 @@ def test_pad_with_non_constant_padding_modes(self):
380380
self.assertTrue(np.all(symmetric_middle_slice == np.asarray([0, 1, 200, 200, 1, 0])))
381381
self.assertEqual(transforms.ToTensor()(symmetric_padded_img).size(), (3, 32, 34))
382382

383+
# Check negative padding explicitly for symmetric case, since it is not
384+
# implemented for tensor case to compare to
385+
# Crop 1 to left, pad 2 to top, pad 3 to right, crop 3 to bottom
386+
symmetric_padded_img_neg = F.pad(img, (-1, 2, 3, -3), padding_mode='symmetric')
387+
symmetric_neg_middle_left = np.asarray(symmetric_padded_img_neg).transpose(2, 0, 1)[0][17][:3]
388+
symmetric_neg_middle_right = np.asarray(symmetric_padded_img_neg).transpose(2, 0, 1)[0][17][-4:]
389+
self.assertTrue(np.all(symmetric_neg_middle_left == np.asarray([1, 0, 0])))
390+
self.assertTrue(np.all(symmetric_neg_middle_right == np.asarray([200, 200, 0, 0])))
391+
self.assertEqual(transforms.ToTensor()(symmetric_padded_img_neg).size(), (3, 28, 31))
392+
383393
def test_pad_raises_with_invalid_pad_sequence_len(self):
384394
with self.assertRaises(ValueError):
385395
transforms.Pad(())

test/test_transforms_tensor.py

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -121,26 +121,30 @@ def test_color_jitter(self):
121121
)
122122

123123
def test_pad(self):
124-
125-
# Test functional.pad (PIL and Tensor) with padding as single int
126-
self._test_functional_op(
127-
"pad", fn_kwargs={"padding": 2, "fill": 0, "padding_mode": "constant"}
128-
)
129-
# Test functional.pad and transforms.Pad with padding as [int, ]
130-
fn_kwargs = meth_kwargs = {"padding": [2, ], "fill": 0, "padding_mode": "constant"}
131-
self._test_op(
132-
"pad", "Pad", fn_kwargs=fn_kwargs, meth_kwargs=meth_kwargs
133-
)
134-
# Test functional.pad and transforms.Pad with padding as list
135-
fn_kwargs = meth_kwargs = {"padding": [4, 4], "fill": 0, "padding_mode": "constant"}
136-
self._test_op(
137-
"pad", "Pad", fn_kwargs=fn_kwargs, meth_kwargs=meth_kwargs
138-
)
139-
# Test functional.pad and transforms.Pad with padding as tuple
140-
fn_kwargs = meth_kwargs = {"padding": (2, 2, 2, 2), "fill": 127, "padding_mode": "constant"}
141-
self._test_op(
142-
"pad", "Pad", fn_kwargs=fn_kwargs, meth_kwargs=meth_kwargs
143-
)
124+
for m in ["constant", "edge", "reflect", "symmetric"]:
125+
fill = 127 if m == "constant" else 0
126+
# Negative pad currently unsupported for Tensor and symmetric
127+
multipliers = [1] if m == "symmetric" else [1, -1]
128+
for mul in multipliers:
129+
# Test functional.pad (PIL and Tensor) with padding as single int
130+
self._test_functional_op(
131+
"pad", fn_kwargs={"padding": mul * 2, "fill": fill, "padding_mode": m}
132+
)
133+
# Test functional.pad and transforms.Pad with padding as [int, ]
134+
fn_kwargs = meth_kwargs = {"padding": [mul * 2, ], "fill": fill, "padding_mode": m}
135+
self._test_op(
136+
"pad", "Pad", fn_kwargs=fn_kwargs, meth_kwargs=meth_kwargs
137+
)
138+
# Test functional.pad and transforms.Pad with padding as list
139+
fn_kwargs = meth_kwargs = {"padding": [mul * 4, 4], "fill": fill, "padding_mode": m}
140+
self._test_op(
141+
"pad", "Pad", fn_kwargs=fn_kwargs, meth_kwargs=meth_kwargs
142+
)
143+
# Test functional.pad and transforms.Pad with padding as tuple
144+
fn_kwargs = meth_kwargs = {"padding": (mul * 2, 2, 2, mul * 2), "fill": fill, "padding_mode": m}
145+
self._test_op(
146+
"pad", "Pad", fn_kwargs=fn_kwargs, meth_kwargs=meth_kwargs
147+
)
144148

145149
def test_crop(self):
146150
fn_kwargs = {"top": 2, "left": 3, "height": 4, "width": 5}

torchvision/transforms/functional_pil.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,15 @@ def pad(img, padding, fill=0, padding_mode="constant"):
330330
pad_right = padding[2]
331331
pad_bottom = padding[3]
332332

333+
p = [pad_left, pad_top, pad_right, pad_bottom]
334+
cropping = -np.minimum(p, 0)
335+
336+
if cropping.any():
337+
crop_left, crop_top, crop_right, crop_bottom = cropping
338+
img = img.crop((crop_left, crop_top, img.width - crop_right, img.height - crop_bottom))
339+
340+
pad_left, pad_top, pad_right, pad_bottom = np.maximum(p, 0)
341+
333342
if img.mode == 'P':
334343
palette = img.getpalette()
335344
img = np.asarray(img)

0 commit comments

Comments
 (0)