Skip to content

Commit

Permalink
PR: Add libpng and libjpeg-turbo requirement into conda recipe (pytor…
Browse files Browse the repository at this point in the history
…ch#2301)

* Add libpng requirement into conda recipe

* Try to install libjpeg-turbo

* Add PNG reading capabilities

* Remove newline

* Add image extension to compilation instructions

* Include png functions as part of the main library

* Update CMakeLists

* Detect if building on conda-build

* Debug

* More debug messages

* Print globbed libreries

* Print globbed libreries

* Point to correct PNG path

* Remove libJPEG preventively

* Debug extension loading

* Link libpng explicitly

* Link with PNG

* Add PNG reading capabilities

* Add libpng requirement into conda recipe

* Try to install libjpeg-turbo

* Remove newline

* Add image extension to compilation instructions

* Include png functions as part of the main library

* Update CMakeLists

* Detect if building on conda-build

* Debug

* More debug messages

* Print globbed libreries

* Print globbed libreries

* Point to correct PNG path

* Remove libJPEG preventively

* Debug extension loading

* Link libpng explicitly

* Link with PNG

* Install libpng on conda-based wheel distributions

* Add -y flag

* Add -y flag to yum

* Locate LibPNG on windows conda

* Remove empty else

* Copy libpng16.so

* Copy dylib on Mac

* Improve check on Windows

* Try to install ninja using conda on windows

* Use libpng on Windows

* Package lib on windows wheel

* Point library to the correct place

* Include binaries as part of wheel

* Copy libpng.so on linux

* Look for png.h on Windows when using conda-build

* Do not skip png tests on Mac/Win

* Restore libjpeg-turbo

* Install jpeg-turbo on wheel distributions

* Install libjpeg-turbo from conda-forge on wheel distributions

* Do not pull av on conda-build

* Add pillow disclaimer

* Vendors libjpeg-turbo 2.0.4

* Merge JPEG work

* Remove submodules

* Regenerate circle config

* Fix style issues

* Fix C++ style issues

* More style corrections

* Add JPEG-turbo to linking libraries

* More style corrections

* More style corrections

* More style corrections

* Install libjpeg-turbo-devel

* Install libturbo-jpeg on typing pipeline

* Update Circle template

* Windows and Unix turbojpeg have the same linking name

* Install turbojpeg-devel instead of libjpeg-turbo

* Copy TurboJPEG binaries to wheel

* Move test image

* Move back test image

* Update JPEG test path

* Remove dot from extension

* Move image functions to extension

* Use stdout arg in subprocess

* Disable image extension if libpng or turbojpeg are not found

* Append libpng stdout

* Prevent list appending on lists

* Minor path correction

* Minor error correction

* Add linking flags

* Style issues correction

* Address minor review corrections

* Refactor library search

* Restore access index

* Fix JPEG tests

* Update libpng version in Travis

* Add -y flag

* Remove dot

* Update libpng using apt

* Check libpng version

* Change libturbojpeg binary

* Update import

* Change call

* Restore av in conda recipe

* Minor error correction

* Remove unused comment in travis.yml

* Update README

* Fix missing links

* Remove fixes for 16.04

Co-authored-by: Ryad ZENINE <[email protected]>
  • Loading branch information
2 people authored and de-vri-es committed Aug 4, 2020
1 parent 0e54ccf commit 35ba835
Show file tree
Hide file tree
Showing 19 changed files with 563 additions and 11 deletions.
2 changes: 2 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ jobs:
- checkout
- run:
command: |
sudo apt-get update -y
sudo apt install -y libturbojpeg-dev
pip install --user --progress-bar off numpy mypy
pip install --user --progress-bar off --pre torch -f https://download.pytorch.org/whl/nightly/cpu/torch_nightly.html
pip install --user --progress-bar off --editable .
Expand Down
2 changes: 2 additions & 0 deletions .circleci/config.yml.in
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ jobs:
- checkout
- run:
command: |
sudo apt-get update -y
sudo apt install -y libturbojpeg-dev
pip install --user --progress-bar off numpy mypy
pip install --user --progress-bar off --pre torch -f https://download.pytorch.org/whl/nightly/cpu/torch_nightly.html
pip install --user --progress-bar off --editable .
Expand Down
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ jobs:

before_install:
- sudo apt-get update
- sudo apt-get install -y libpng-dev libturbojpeg-dev
- wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh;
- bash miniconda.sh -b -p $HOME/miniconda
- export PATH="$HOME/miniconda/bin:$PATH"
Expand Down
16 changes: 11 additions & 5 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,28 @@ if(WITH_CUDA)
endif()

find_package(Python3 COMPONENTS Development)

find_package(Torch REQUIRED)
find_package(PNG REQUIRED)


file(GLOB HEADERS torchvision/csrc/*.h)
file(GLOB OPERATOR_SOURCES torchvision/csrc/cpu/*.h torchvision/csrc/cpu/*.cpp torchvision/csrc/*.cpp)
file(GLOB IMAGE_HEADERS torchvision/csrc/cpu/image/*.h)
file(GLOB IMAGE_SOURCES torchvision/csrc/cpu/image/*.cpp)
file(GLOB OPERATOR_SOURCES torchvision/csrc/cpu/*.h torchvision/csrc/cpu/*.cpp ${IMAGE_HEADERS} ${IMAGE_SOURCES} ${HEADERS} torchvision/csrc/*.cpp)
if(WITH_CUDA)
file(GLOB OPERATOR_SOURCES ${OPERATOR_SOURCES} torchvision/csrc/cuda/*.h torchvision/csrc/cuda/*.cu)
endif()
file(GLOB MODELS_HEADERS torchvision/csrc/models/*.h)
file(GLOB MODELS_SOURCES torchvision/csrc/models/*.h torchvision/csrc/models/*.cpp)

add_library(${PROJECT_NAME} SHARED ${MODELS_SOURCES} ${OPERATOR_SOURCES})
target_link_libraries(${PROJECT_NAME} PRIVATE ${TORCH_LIBRARIES} Python3::Python)
add_library(${PROJECT_NAME} SHARED ${MODELS_SOURCES} ${OPERATOR_SOURCES} ${IMAGE_SOURCES})
target_link_libraries(${PROJECT_NAME} PRIVATE ${TORCH_LIBRARIES} ${PNG_LIBRARY} Python3::Python)
# target_link_libraries(${PROJECT_NAME} PRIVATE ${PNG_LIBRARY} Python3::Python)
set_target_properties(${PROJECT_NAME} PROPERTIES EXPORT_NAME TorchVision)

target_include_directories(${PROJECT_NAME} INTERFACE
$<BUILD_INTERFACE:${HEADERS}>
$<BUILD_INTERFACE:${HEADERS}:${PNG_INCLUDE_DIR}>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)

include(GNUInstallDirs)
Expand Down Expand Up @@ -61,7 +67,7 @@ install(FILES
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}/cpu)
if(WITH_CUDA)
install(FILES
torchvision/csrc/cuda/vision_cuda.h
torchvision/csrc/cuda/vision_cuda.h
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}/cuda)
endif()
install(FILES ${MODELS_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}/models)
13 changes: 11 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,22 @@ Torchvision currently supports the following image backends:

* `accimage`_ - if installed can be activated by calling :code:`torchvision.set_image_backend('accimage')`

* `libpng`_ - can be installed via conda :code:`conda install libpng` or any of the package managers for debian-based and RHEL-based Linux distributions.

* `libturbojpeg`_ - blazing speed, fast JPEG image loading. Can be installed from conda-forge :code:`conda install libjpeg-turbo -c conda-forge`.

**Notes:** ``libpng`` and ``libturbojpeg`` must be available at compilation time in order to be available. Also, most linux distributions distinguish between
``libturbojpeg`` and ``libjpeg-turbo``, where the former should be installed instead of the latter one.

.. _libpng : http://www.libpng.org/pub/png/libpng.html
.. _libturbojpeg: https://github.com/libjpeg-turbo/libjpeg-turbo
.. _Pillow : https://python-pillow.org/
.. _Pillow-SIMD : https://github.com/uploadcare/pillow-simd
.. _accimage: https://github.com/pytorch/accimage

C++ API
=======
TorchVision also offers a C++ API that contains C++ equivalent of python models.
TorchVision also offers a C++ API that contains C++ equivalent of python models.

Installation From source:

Expand All @@ -94,7 +103,7 @@ Installation From source:
cd build
# Add -DWITH_CUDA=on support for the CUDA if needed
cmake ..
make
make
make install
Once installed, the library can be accessed in cmake (after properly configuring ``CMAKE_PREFIX_PATH``) via the :code:`TorchVision::TorchVision` target:
Expand Down
24 changes: 24 additions & 0 deletions packaging/build_wheel.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,30 @@ setup_wheel_python
pip_install numpy pyyaml future ninja
setup_pip_pytorch_version
python setup.py clean

# Copy binaries to be included in the wheel distribution
if [[ "$(uname)" == Darwin || "$OSTYPE" == "msys" ]]; then
python_exec="$(which python)"
bin_path=$(dirname $python_exec)
env_path=$(dirname $bin_path)
if [[ "$(uname)" == Darwin ]]; then
# Include LibPNG
cp "$env_path/lib/libpng16.dylib" torchvision
# Include TurboJPEG
cp "$env_path/lib/libturbojpeg.dylib" torchvision
else
# Include libPNG
cp "$bin_path/Library/lib/libpng.lib" torchvision
# Include TurboJPEG
cp "$bin_path/Library/lib/turbojpeg.lib" torchvision
fi
else
# Include LibPNG
cp "/usr/lib64/libpng.so" torchvision
# Include TurboJPEG
cp "/usr/lib64/libturbojpeg.so" torchvision
fi

if [[ "$OSTYPE" == "msys" ]]; then
IS_WHEEL=1 "$script_dir/windows/internal/vc_env_helper.bat" python setup.py bdist_wheel
else
Expand Down
6 changes: 6 additions & 0 deletions packaging/pkg_helpers.bash
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,13 @@ setup_wheel_python() {
conda env remove -n "env$PYTHON_VERSION" || true
conda create -yn "env$PYTHON_VERSION" python="$PYTHON_VERSION"
conda activate "env$PYTHON_VERSION"
# Install libPNG from Anaconda (defaults)
conda install libpng -y
# Install libJPEG-turbo from conda-forge
conda install -y libjpeg-turbo -c conda-forge
else
# Install native CentOS libPNG, libJPEG-turbo
yum install -y libpng-devel turbojpeg-devel
case "$PYTHON_VERSION" in
2.7)
if [[ -n "$UNICODE_ABI" ]]; then
Expand Down
3 changes: 3 additions & 0 deletions packaging/torchvision/conda_build_config.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
channel_sources:
- defaults,conda-forge

blas_impl:
- mkl # [x86_64]
c_compiler:
Expand Down
6 changes: 6 additions & 0 deletions packaging/torchvision/meta.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ source:
requirements:
build:
- {{ compiler('c') }} # [win]
- libpng
- libjpeg-turbo

host:
- python
Expand All @@ -18,6 +20,10 @@ requirements:

run:
- python
- libpng
- libjpeg-turbo
# Pillow introduces unwanted conflicts with libjpeg-turbo, as it depends on jpeg
# The fix depends on https://github.com/conda-forge/conda-forge.github.io/issues/673
- pillow >=4.1.1
- numpy >=1.11
{{ environ.get('CONDA_PYTORCH_CONSTRAINT') }}
Expand Down
137 changes: 134 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import re
import sys
from setuptools import setup, find_packages
from pkg_resources import get_distribution, DistributionNotFound
from pkg_resources import parse_version, get_distribution, DistributionNotFound
import subprocess
import distutils.command.clean
import distutils.spawn
Expand Down Expand Up @@ -76,7 +76,76 @@ def write_version_file():
requirements.append(pillow_req + pillow_ver)


def find_library(name, vision_include):
this_dir = os.path.dirname(os.path.abspath(__file__))
build_prefix = os.environ.get('BUILD_PREFIX', None)
is_conda_build = build_prefix is not None

library_found = False
conda_installed = False
lib_folder = None
include_folder = None
library_header = '{0}.h'.format(name)

print('Running build on conda-build: {0}'.format(is_conda_build))
if is_conda_build:
# Add conda headers/libraries
if os.name == 'nt':
build_prefix = os.path.join(build_prefix, 'Library')
include_folder = os.path.join(build_prefix, 'include')
lib_folder = os.path.join(build_prefix, 'lib')
library_header_path = os.path.join(
include_folder, library_header)
library_found = os.path.isfile(library_header_path)
conda_installed = library_found
else:
# Check if using Anaconda to produce wheels
conda = distutils.spawn.find_executable('conda')
is_conda = conda is not None
print('Running build on conda: {0}'.format(is_conda))
if is_conda:
python_executable = sys.executable
py_folder = os.path.dirname(python_executable)
if os.name == 'nt':
env_path = os.path.join(py_folder, 'Library')
else:
env_path = os.path.dirname(py_folder)
lib_folder = os.path.join(env_path, 'lib')
include_folder = os.path.join(env_path, 'include')
library_header_path = os.path.join(
include_folder, library_header)
library_found = os.path.isfile(library_header_path)
conda_installed = library_found

# Try to locate turbojpeg in Linux standard paths
if not library_found:
if sys.platform == 'linux':
library_found = os.path.exists('/usr/include/{0}'.format(
library_header))
library_found = library_found or os.path.exists(
'/usr/local/include/{0}'.format(library_header))
else:
# Lookup in TORCHVISION_INCLUDE or in the package file
package_path = [os.path.join(this_dir, 'torchvision')]
for folder in vision_include + package_path:
candidate_path = os.path.join(folder, library_header)
library_found = os.path.exists(candidate_path)
if library_found:
break

return library_found, conda_installed, include_folder, lib_folder


def get_extensions():
vision_include = os.environ.get('TORCHVISION_INCLUDE', None)
vision_library = os.environ.get('TORCHVISION_LIBRARY', None)
vision_include = (vision_include.split(os.pathsep)
if vision_include is not None else [])
vision_library = (vision_library.split(os.pathsep)
if vision_library is not None else [])
include_dirs = vision_include
library_dirs = vision_library

this_dir = os.path.dirname(os.path.abspath(__file__))
extensions_dir = os.path.join(this_dir, 'torchvision', 'csrc')

Expand Down Expand Up @@ -149,13 +218,14 @@ def get_extensions():

sources = [os.path.join(extensions_dir, s) for s in sources]

include_dirs = [extensions_dir]
include_dirs += [extensions_dir]

ext_modules = [
extension(
'torchvision._C',
sources,
include_dirs=include_dirs,
library_dirs=library_dirs,
define_macros=define_macros,
extra_compile_args=extra_compile_args,
)
Expand All @@ -171,6 +241,65 @@ def get_extensions():
)
)

# Image reading extension
image_macros = []
image_include = [extensions_dir]
image_library = []
image_link_flags = []

# Locating libPNG
libpng = distutils.spawn.find_executable('libpng-config')
png_found = libpng is not None
image_macros += [('PNG_FOUND', str(int(png_found)))]
print('PNG found: {0}'.format(png_found))
if png_found:
png_version = subprocess.run([libpng, '--version'],
stdout=subprocess.PIPE)
png_version = png_version.stdout.strip().decode('utf-8')
print('libpng version: {0}'.format(png_version))
png_version = parse_version(png_version)
if png_version >= parse_version("1.6.0"):
print('Building torchvision with PNG image support')
png_lib = subprocess.run([libpng, '--libdir'],
stdout=subprocess.PIPE)
png_include = subprocess.run([libpng, '--I_opts'],
stdout=subprocess.PIPE)
image_library += [png_lib.stdout.strip().decode('utf-8')]
image_include += [png_include.stdout.strip().decode('utf-8')]
image_link_flags.append('png' if os.name != 'nt' else 'libpng')
else:
print('libpng installed version is less than 1.6.0, '
'disabling PNG support')
png_found = False

# Locating libjpegturbo
turbojpeg_info = find_library('turbojpeg', vision_include)
(turbojpeg_found, conda_installed,
turbo_include_folder, turbo_lib_folder) = turbojpeg_info

image_macros += [('JPEG_FOUND', str(int(turbojpeg_found)))]
print('turboJPEG found: {0}'.format(turbojpeg_found))
if turbojpeg_found:
print('Building torchvision with JPEG image support')
image_link_flags.append('turbojpeg')
if conda_installed:
image_library += [turbo_lib_folder]
image_include += [turbo_include_folder]

image_path = os.path.join(extensions_dir, 'cpu', 'image')
image_src = glob.glob(os.path.join(image_path, '*.cpp'))

if png_found or turbojpeg_found:
ext_modules.append(extension(
'torchvision.image',
image_src,
include_dirs=include_dirs + [image_path] + image_include,
library_dirs=library_dirs + image_library,
define_macros=image_macros,
libraries=image_link_flags,
extra_compile_args=extra_compile_args
))

ffmpeg_exe = distutils.spawn.find_executable('ffmpeg')
has_ffmpeg = ffmpeg_exe is not None

Expand Down Expand Up @@ -243,7 +372,9 @@ def run(self):

# Package info
packages=find_packages(exclude=('test',)),

package_data={
package_name: ['*.lib', '*.dylib', '*.so']
},
zip_safe=False,
install_requires=requirements,
extras_require={
Expand Down
Loading

0 comments on commit 35ba835

Please sign in to comment.