Skip to content

Commit

Permalink
Merge branch 'master' into models/ssd
Browse files Browse the repository at this point in the history
  • Loading branch information
datumbox authored Apr 8, 2021
2 parents dcdd04d + ad9cc62 commit 5a2e22c
Show file tree
Hide file tree
Showing 31 changed files with 618 additions and 199 deletions.
3 changes: 2 additions & 1 deletion .circleci/regenerate.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"""

import jinja2
from jinja2 import select_autoescape
import yaml
import os.path

Expand Down Expand Up @@ -295,7 +296,7 @@ def ios_workflows(indentation=6, nightly=False):
env = jinja2.Environment(
loader=jinja2.FileSystemLoader(d),
lstrip_blocks=True,
autoescape=False,
autoescape=select_autoescape(enabled_extensions=('html', 'xml')),
keep_trailing_newline=True,
)

Expand Down
6 changes: 6 additions & 0 deletions .circleci/unittest/linux/scripts/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,11 @@ fi
printf "Installing PyTorch with %s\n" "${cudatoolkit}"
conda install -y -c "pytorch-${UPLOAD_CHANNEL}" -c conda-forge "pytorch-${UPLOAD_CHANNEL}"::pytorch "${cudatoolkit}"

if [ $PYTHON_VERSION == "3.6" ]; then
printf "Installing minimal PILLOW version\n"
# Install the minimal PILLOW version. Otherwise, let setup.py install the latest
pip install pillow==5.3.0
fi

printf "* Installing torchvision\n"
python setup.py develop
6 changes: 6 additions & 0 deletions .circleci/unittest/windows/scripts/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,11 @@ fi
printf "Installing PyTorch with %s\n" "${cudatoolkit}"
conda install -y -c "pytorch-${UPLOAD_CHANNEL}" -c conda-forge "pytorch-${UPLOAD_CHANNEL}"::pytorch "${cudatoolkit}"

if [ $PYTHON_VERSION == "3.6" ]; then
printf "Installing minimal PILLOW version\n"
# Install the minimal PILLOW version. Otherwise, let setup.py install the latest
pip install pillow==5.3.0
fi

printf "* Installing torchvision\n"
"$this_dir/vc_env_helper.bat" python setup.py develop
23 changes: 23 additions & 0 deletions .github/workflows/bandit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# GitHub Actions Bandit Workflow

name: Bandit

on:
pull_request:
branches: [ master ]

workflow_dispatch:

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2

# Task will fail if any high-severity issues are found
# Ignoring submodules
- name: Run Bandit Security Analysis
run: |
python -m pip install bandit
python -m bandit -r . -x ./third_party -lll
43 changes: 43 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# GitHub Actions CodeQL Workflow

name: CodeQL

on:
pull_request:
branches: [ master ]

workflow_dispatch:

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2

- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: python, cpp

- name: Install Ninja
run: |
sudo apt-get update -y
sudo apt-get install -y ninja-build
- name: Update submodules
run: git submodule update --init --recursive

- name: Install Torch
run: |
python -m pip install cmake
python -m pip install torch==1.8.1+cpu -f https://download.pytorch.org/whl/torch_stable.html
sudo ln -s /usr/bin/ninja /usr/bin/ninja-build
- name: Build TorchVision
run: python setup.py develop --user

# If any code scanning alerts are found, they will be under Security -> CodeQL
# Link: https://github.com/pytorch/vision/security/code-scanning
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
40 changes: 33 additions & 7 deletions docs/source/transforms.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,34 @@ torchvision.transforms
.. currentmodule:: torchvision.transforms

Transforms are common image transformations. They can be chained together using :class:`Compose`.
Additionally, there is the :mod:`torchvision.transforms.functional` module.
Functional transforms give fine-grained control over the transformations.
Most transform classes have a function equivalent: :ref:`functional
transforms <functional_transforms>` give fine-grained control over the
transformations.
This is useful if you have to build a more complex transformation pipeline
(e.g. in the case of segmentation tasks).

All transformations accept PIL Image, Tensor Image or batch of Tensor Images as input. Tensor Image is a tensor with
``(C, H, W)`` shape, where ``C`` is a number of channels, ``H`` and ``W`` are image height and width. Batch of
Tensor Images is a tensor of ``(B, C, H, W)`` shape, where ``B`` is a number of images in the batch. Deterministic or
random transformations applied on the batch of Tensor Images identically transform all the images of the batch.
Most transformations accept both `PIL <https://pillow.readthedocs.io>`_
images and tensor images, although some transformations are :ref:`PIL-only
<transforms_pil_only>` and some are :ref:`tensor-only
<transforms_tensor_only>`. The :ref:`conversion_transforms` may be used to
convert to and from PIL images.

The transformations that accept tensor images also accept batches of tensor
images. A Tensor Image is a tensor with ``(C, H, W)`` shape, where ``C`` is a
number of channels, ``H`` and ``W`` are image height and width. A batch of
Tensor Images is a tensor of ``(B, C, H, W)`` shape, where ``B`` is a number
of images in the batch.

The expected range of the values of a tensor image is implicitely defined by
the tensor dtype. Tensor images with a float dtype are expected to have
values in ``[0, 1)``. Tensor images with an integer dtype are expected to
have values in ``[0, MAX_DTYPE]`` where ``MAX_DTYPE`` is the largest value
that can be represented in that dtype.

Randomized transformations will apply the same transformation to all the
images of a given batch, but they will produce different transformations
across calls. For reproducible transformations across calls, you may use
:ref:`functional transforms <functional_transforms>`.

.. warning::

Expand Down Expand Up @@ -117,13 +136,16 @@ Transforms on PIL Image and torch.\*Tensor
.. autoclass:: GaussianBlur
:members:

.. _transforms_pil_only:

Transforms on PIL Image only
----------------------------

.. autoclass:: RandomChoice

.. autoclass:: RandomOrder

.. _transforms_tensor_only:

Transforms on torch.\*Tensor only
---------------------------------
Expand All @@ -139,6 +161,7 @@ Transforms on torch.\*Tensor only

.. autoclass:: ConvertImageDtype

.. _conversion_transforms:

Conversion Transforms
---------------------
Expand Down Expand Up @@ -173,13 +196,16 @@ The new transform can be used standalone or mixed-and-matched with existing tran
:members:


.. _functional_transforms:

Functional Transforms
---------------------

Functional transforms give you fine-grained control of the transformation pipeline.
As opposed to the transformations above, functional transforms don't contain a random number
generator for their parameters.
That means you have to specify/generate all parameters, but you can reuse the functional transform.
That means you have to specify/generate all parameters, but the functional transform will give you
reproducible results across calls.

Example:
you can apply a functional transform with the same parameters to multiple images like this:
Expand Down
4 changes: 4 additions & 0 deletions mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,7 @@ ignore_missing_imports = True
[mypy-av.*]

ignore_missing_imports = True

[mypy-defusedxml.*]

ignore_missing_imports = True
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def write_version_file():
pytorch_dep,
]

pillow_ver = ' >= 4.1.1'
pillow_ver = ' >= 5.3.0'
pillow_req = 'pillow-simd' if get_dist('pillow-simd') is not None else 'pillow'
requirements.append(pillow_req + pillow_ver)

Expand Down
Binary file modified test/assets/fakedata/draw_boxes_util.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
72 changes: 72 additions & 0 deletions test/test_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,78 @@ def _test_forward(self, device, contiguous, x_dtype=None, rois_dtype=None, **kwa
for aligned in (True, False):
super()._test_forward(device, contiguous, x_dtype, rois_dtype, aligned=aligned)

def test_qroialign(self):
"""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

def make_rois(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

for aligned in (True, False):
for scale, zero_point in ((1, 0), (2, 10), (0.1, 50)):
for qdtype in (torch.qint8, torch.quint8, torch.qint32):

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 = make_rois()
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)
self.assertTrue((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()
self.assertTrue(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)
self.assertTrue(torch.allclose(abs_diff, t_scale, atol=1e-5))

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 = make_rois(10)
qrois = torch.quantize_per_tensor(rois, scale=1, zero_point=0, dtype=torch.qint8)
with self.assertRaisesRegex(RuntimeError, "Only one image per batch is allowed"):
ops.roi_align(qx, qrois, output_size=pool_size)


class PSRoIAlignTester(RoIOpTester, unittest.TestCase):
def fn(self, x, rois, pool_h, pool_w, spatial_scale=1, sampling_ratio=-1, **kwargs):
Expand Down
12 changes: 9 additions & 3 deletions test/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
import unittest
from io import BytesIO
import torchvision.transforms.functional as F
from PIL import Image
from PIL import Image, __version__ as PILLOW_VERSION


PILLOW_VERSION = tuple(int(x) for x in PILLOW_VERSION.split('.'))

boxes = torch.tensor([[0, 0, 20, 20], [0, 0, 0, 0],
[10, 15, 30, 35], [23, 35, 93, 95]], dtype=torch.float)
Expand Down Expand Up @@ -120,8 +123,11 @@ def test_draw_boxes(self):
res = Image.fromarray(result.permute(1, 2, 0).contiguous().numpy())
res.save(path)

expected = torch.as_tensor(np.array(Image.open(path))).permute(2, 0, 1)
self.assertTrue(torch.equal(result, expected))
if PILLOW_VERSION >= (8, 2):
# The reference image is only valid for new PIL versions
expected = torch.as_tensor(np.array(Image.open(path))).permute(2, 0, 1)
self.assertTrue(torch.equal(result, expected))

# Check if modification is not in place
self.assertTrue(torch.all(torch.eq(boxes, boxes_cp)).item())
self.assertTrue(torch.all(torch.eq(img, img_cp)).item())
Expand Down
7 changes: 7 additions & 0 deletions torchvision/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,13 @@ def set_video_backend(backend):


def get_video_backend():
"""
Returns the currently active video backend used to decode videos.
Returns:
str: Name of the video backend. one of {'pyav', 'video_reader'}.
"""

return _video_backend


Expand Down
Loading

0 comments on commit 5a2e22c

Please sign in to comment.