Skip to content

[WIP] Package torchvision with libjpeg-turbo instead of libjpeg #5951

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 24 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
73cf2ba
Use libjpeg-turbo instead of libjpeg
NicolasHug May 4, 2022
8d83854
Unskip tests
NicolasHug May 4, 2022
cdfaeca
Adding conda-forge to the list of channels to look for
NicolasHug May 4, 2022
c48f861
Wrong package name...
NicolasHug May 4, 2022
bc55700
Also let Windows use libjpeg-turbo
NicolasHug May 4, 2022
b8c091f
Set a stricter tolerance and remove unnecessary legacy tests
NicolasHug May 4, 2022
a915354
Forgot this one
NicolasHug May 4, 2022
90e71e8
Merge branch 'main' of github.com:pytorch/vision into turbo
NicolasHug May 5, 2022
11e70ad
Let packaging scripts use libjpeg-turbo instead of libjpeg
NicolasHug May 5, 2022
6108940
libjpeg.dll isn't found?
NicolasHug May 5, 2022
f81da49
Add conda-forge channel to conda build call
NicolasHug May 6, 2022
e35bbb0
Merge branch 'main' into turbo_packaging
NicolasHug May 6, 2022
3d4c395
Merge branch 'main' of github.com:pytorch/vision into turbo_packaging
NicolasHug Sep 8, 2022
dd970fd
Merge branch 'turbo_packaging' of github.com:NicolasHug/vision into t…
NicolasHug Sep 8, 2022
dbda81e
M1 ???
NicolasHug Sep 8, 2022
a853917
try use libmamba as solver
pmeier Sep 9, 2022
0dc1d59
install solver first
pmeier Sep 9, 2022
e5fd362
try pin cudatoolkit <0 for CUDA >= 11.6
pmeier Sep 9, 2022
e2a013a
fix syntax
pmeier Sep 9, 2022
42e42ea
another try
pmeier Sep 9, 2022
ddfa319
and another
pmeier Sep 9, 2022
605024c
add debug echo
pmeier Sep 9, 2022
1e0b77f
reverse logic
pmeier Sep 9, 2022
2cccd77
Revert "add debug echo"
pmeier Sep 9, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .circleci/unittest/linux/scripts/environment.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
channels:
- pytorch
- defaults
- conda-forge
dependencies:
- pytest
- pytest-cov
- pytest-mock
- pip
- libpng
- jpeg
- libjpeg-turbo
- ca-certificates
- h5py
- pip:
Expand Down
3 changes: 2 additions & 1 deletion .circleci/unittest/windows/scripts/environment.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
channels:
- pytorch
- defaults
- conda-forge
dependencies:
- pytest
- pytest-cov
- pytest-mock
- pip
- libpng
- jpeg
- libjpeg-turbo
- ca-certificates
- hdf5
- setuptools
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/build-m1-binaries.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ jobs:
setup_build_version
fi

conda create -yp ${ENV_NAME} python=${PY_VERS} numpy libpng jpeg wheel pkg-config
conda create -yp ${ENV_NAME} python=${PY_VERS} numpy libpng wheel pkg-config libjpeg-turbo -c conda-forge
conda run -p ${ENV_NAME} python3 -mpip install torch --pre --extra-index-url=https://download.pytorch.org/whl/${CHANNEL}
conda run -p ${ENV_NAME} python3 -mpip install delocate
conda run -p ${ENV_NAME} python3 setup.py bdist_wheel
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test-m1.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
# Needed for JPEG library detection as setup.py detects conda presence by running `shutil.which('conda')`
export PATH=~/miniconda3/bin:$PATH
set -ex
conda create -yp ${ENV_NAME} python=${PY_VERS} numpy libpng jpeg scipy
conda create -yp ${ENV_NAME} python=${PY_VERS} numpy libpng scipy libjpeg-turbo -c conda-forge
conda run -p ${ENV_NAME} python3 -mpip install --pre torch --extra-index-url=https://download.pytorch.org/whl/${CHANNEL}
conda run -p ${ENV_NAME} python3 setup.py develop
conda run -p ${ENV_NAME} python3 -mpip install pytest pytest-mock av
Expand Down
3 changes: 2 additions & 1 deletion packaging/build_cmake.sh
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ conda install -yq \pytorch=$PYTORCH_VERSION $CONDA_CUDATOOLKIT_CONSTRAINT $PYTOR
TORCH_PATH=$(dirname $(python -c "import torch; print(torch.__file__)"))

if [[ "$(uname)" == Darwin || "$OSTYPE" == "msys" ]]; then
conda install -yq libpng jpeg
conda install -yq libpng
conda install -yq libjpeg-turbo -c conda-forge
else
yum install -y libpng-devel libjpeg-turbo-devel
fi
Expand Down
5 changes: 4 additions & 1 deletion packaging/build_conda.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,7 @@ setup_visual_studio_constraint
setup_junit_results_folder
export CUDATOOLKIT_CHANNEL="nvidia"

conda build -c $CUDATOOLKIT_CHANNEL $CONDA_CHANNEL_FLAGS --no-anaconda-upload --python "$PYTHON_VERSION" packaging/torchvision
conda install conda-libmamba-solver
export CONDA_EXPERIMENTAL_SOLVER="libmamba"

conda build -c $CUDATOOLKIT_CHANNEL $CONDA_CHANNEL_FLAGS -c conda-forge --no-anaconda-upload --python "$PYTHON_VERSION" packaging/torchvision
2 changes: 1 addition & 1 deletion packaging/build_wheel.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ if [[ "$(uname)" == Darwin || "$OSTYPE" == "msys" ]]; then
pip_install "delocate>=0.9"
else
cp "$bin_path/Library/bin/libpng16.dll" torchvision
cp "$bin_path/Library/bin/libjpeg.dll" torchvision
# cp "$bin_path/Library/bin/libjpeg.dll" torchvision
fi
else
# Install auditwheel to get some inspection utilities
Expand Down
4 changes: 3 additions & 1 deletion packaging/pkg_helpers.bash
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,9 @@ setup_wheel_python() {
conda create ${CONDA_CHANNEL_FLAGS} -yn "env$PYTHON_VERSION" python="$PYTHON_VERSION"
conda activate "env$PYTHON_VERSION"
# Install libpng from Anaconda (defaults)
conda install ${CONDA_CHANNEL_FLAGS} libpng "jpeg<=9b" -y
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this script used? The jpeg<=9b is definitely stale as we no longer pin the library.

conda install ${CONDA_CHANNEL_FLAGS} libpng -y
# Install libjpeg-turbo from conda-forge
conda install -c conda-forge libjpeg-turbo -y
else
# Install native CentOS libJPEG, freetype and GnuTLS
yum install -y libjpeg-turbo-devel freetype gnutls
Expand Down
13 changes: 9 additions & 4 deletions packaging/torchvision/meta.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{% set build_variant = environ.get('CONDA_BUILD_VARIANT', 'cpu') %}
{% set cuda_version = environ.get('CU_VERSION', 'cpu').replace('cpu', 'cu-1')[2:] | int %}

package:
name: torchvision
version: "{{ environ.get('BUILD_VERSION') }}"
Expand All @@ -10,7 +12,7 @@ requirements:
build:
- {{ compiler('c') }} # [win]
- libpng
- jpeg
- libjpeg-turbo
# NOTE: The only ffmpeg version that we build is actually 4.2
- ffmpeg >=4.2 # [not win]

Expand All @@ -27,7 +29,7 @@ requirements:
- requests
- libpng
- ffmpeg >=4.2 # [not win]
- jpeg
- libjpeg-turbo
- pillow >=5.3.0, !=8.3.*
- pytorch-mutex 1.0 {{ build_variant }} # [not osx ]
{{ environ.get('CONDA_PYTORCH_CONSTRAINT') }}
Expand All @@ -38,7 +40,10 @@ requirements:
- cpuonly
{% elif not osx %}
run_constrained:
- cpuonly <0
- cpuonly <0
{% if cuda_version < 116 %}
- cudatoolkit <0
{% endif %}
{% endif %}

build:
Expand All @@ -61,7 +66,7 @@ test:
requires:
- pytest
- scipy
- jpeg
- libjpeg-turbo
- ca-certificates


Expand Down
79 changes: 3 additions & 76 deletions test/test_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,10 @@ def test_decode_jpeg(img_path, pil_mode, mode):
data = read_file(img_path)
img_ljpeg = decode_image(data, mode=mode)

# Permit a small variation on pixel values to account for implementation
# differences between Pillow and LibJPEG.
# Permit a small variation on pixel values to account for differences
# between Pillow's libjpeg and ours.
abs_mean_diff = (img_ljpeg.type(torch.float32) - img_pil).abs().mean().item()
assert abs_mean_diff < 2
assert abs_mean_diff < 1


def test_decode_jpeg_errors():
Expand Down Expand Up @@ -412,77 +412,6 @@ def test_encode_jpeg_errors():
encode_jpeg(torch.empty((100, 100), dtype=torch.uint8))


def _collect_if(cond):
# TODO: remove this once test_encode_jpeg_reference and test_write_jpeg_reference
# are removed
def _inner(test_func):
if cond:
return test_func
else:
return pytest.mark.dont_collect(test_func)

return _inner


@_collect_if(cond=False)
@pytest.mark.parametrize(
"img_path",
[pytest.param(jpeg_path, id=_get_safe_image_name(jpeg_path)) for jpeg_path in get_images(ENCODE_JPEG, ".jpg")],
)
def test_encode_jpeg_reference(img_path):
# This test is *wrong*.
# It compares a torchvision-encoded jpeg with a PIL-encoded jpeg (the reference), but it
# starts encoding the torchvision version from an image that comes from
# decode_jpeg, which can yield different results from pil.decode (see
# test_decode... which uses a high tolerance).
# Instead, we should start encoding from the exact same decoded image, for a
# valid comparison. This is done in test_encode_jpeg, but unfortunately
# these more correct tests fail on windows (probably because of a difference
# in libjpeg) between torchvision and PIL.
# FIXME: make the correct tests pass on windows and remove this.
dirname = os.path.dirname(img_path)
filename, _ = os.path.splitext(os.path.basename(img_path))
write_folder = os.path.join(dirname, "jpeg_write")
expected_file = os.path.join(write_folder, f"{filename}_pil.jpg")
img = decode_jpeg(read_file(img_path))

with open(expected_file, "rb") as f:
pil_bytes = f.read()
pil_bytes = torch.as_tensor(list(pil_bytes), dtype=torch.uint8)
for src_img in [img, img.contiguous()]:
# PIL sets jpeg quality to 75 by default
jpeg_bytes = encode_jpeg(src_img, quality=75)
assert_equal(jpeg_bytes, pil_bytes)


@_collect_if(cond=False)
@pytest.mark.parametrize(
"img_path",
[pytest.param(jpeg_path, id=_get_safe_image_name(jpeg_path)) for jpeg_path in get_images(ENCODE_JPEG, ".jpg")],
)
def test_write_jpeg_reference(img_path, tmpdir):
# FIXME: Remove this eventually, see test_encode_jpeg_reference
data = read_file(img_path)
img = decode_jpeg(data)

basedir = os.path.dirname(img_path)
filename, _ = os.path.splitext(os.path.basename(img_path))
torch_jpeg = os.path.join(tmpdir, f"{filename}_torch.jpg")
pil_jpeg = os.path.join(basedir, "jpeg_write", f"{filename}_pil.jpg")

write_jpeg(img, torch_jpeg, quality=75)

with open(torch_jpeg, "rb") as f:
torch_bytes = f.read()

with open(pil_jpeg, "rb") as f:
pil_bytes = f.read()

assert_equal(torch_bytes, pil_bytes)


# TODO: Remove the skip. See https://github.com/pytorch/vision/issues/5162.
@pytest.mark.skip("this test fails because PIL uses libjpeg-turbo")
@pytest.mark.parametrize(
"img_path",
[pytest.param(jpeg_path, id=_get_safe_image_name(jpeg_path)) for jpeg_path in get_images(ENCODE_JPEG, ".jpg")],
Expand All @@ -501,8 +430,6 @@ def test_encode_jpeg(img_path):
assert_equal(encoded_jpeg_torch, encoded_jpeg_pil)


# TODO: Remove the skip. See https://github.com/pytorch/vision/issues/5162.
@pytest.mark.skip("this test fails because PIL uses libjpeg-turbo")
@pytest.mark.parametrize(
"img_path",
[pytest.param(jpeg_path, id=_get_safe_image_name(jpeg_path)) for jpeg_path in get_images(ENCODE_JPEG, ".jpg")],
Expand Down