From 2b7894365aabf5d9fddc11910df950e4a4898a38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 8 Jun 2020 15:31:40 -0500 Subject: [PATCH 001/129] Add libpng requirement into conda recipe --- packaging/torchvision/meta.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packaging/torchvision/meta.yaml b/packaging/torchvision/meta.yaml index 8be8eabdd09..639b0a254b8 100644 --- a/packaging/torchvision/meta.yaml +++ b/packaging/torchvision/meta.yaml @@ -8,6 +8,7 @@ source: requirements: build: - {{ compiler('c') }} # [win] + - libpng host: - python @@ -18,6 +19,7 @@ requirements: run: - python + - libpng - pillow >=4.1.1 - numpy >=1.11 {{ environ.get('CONDA_PYTORCH_CONSTRAINT') }} From 23255aac79e17cb880148bd22307b2922236e1aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 8 Jun 2020 18:23:15 -0500 Subject: [PATCH 002/129] Try to install libjpeg-turbo --- packaging/torchvision/conda_build_config.yaml | 3 +++ packaging/torchvision/meta.yaml | 2 ++ 2 files changed, 5 insertions(+) diff --git a/packaging/torchvision/conda_build_config.yaml b/packaging/torchvision/conda_build_config.yaml index 5188bb0ebec..adbbc732a0c 100644 --- a/packaging/torchvision/conda_build_config.yaml +++ b/packaging/torchvision/conda_build_config.yaml @@ -1,3 +1,6 @@ +channel_sources: + - defaults,conda-forge + blas_impl: - mkl # [x86_64] c_compiler: diff --git a/packaging/torchvision/meta.yaml b/packaging/torchvision/meta.yaml index 639b0a254b8..b87b63a1bc4 100644 --- a/packaging/torchvision/meta.yaml +++ b/packaging/torchvision/meta.yaml @@ -9,6 +9,7 @@ requirements: build: - {{ compiler('c') }} # [win] - libpng + - libjpeg-turbo host: - python @@ -20,6 +21,7 @@ requirements: run: - python - libpng + - libjpeg-turbo - pillow >=4.1.1 - numpy >=1.11 {{ environ.get('CONDA_PYTORCH_CONSTRAINT') }} From 006ab0ca5ae2cdf45704a7284bc138716a58b735 Mon Sep 17 00:00:00 2001 From: Ryad ZENINE Date: Thu, 13 Feb 2020 14:40:17 +0100 Subject: [PATCH 003/129] Add PNG reading capabilities --- .circleci/config.yml | 2 +- CMakeLists.txt | 17 ++++- test/test_image.py | 44 +++++++++++++ torchvision/csrc/cpu/image/readpng_cpu.cpp | 75 ++++++++++++++++++++++ torchvision/csrc/cpu/image/readpng_cpu.h | 6 ++ torchvision/csrc/image.h | 3 + torchvision/csrc/vision.cpp | 6 ++ torchvision/io/image.py | 48 ++++++++++++++ 8 files changed, 198 insertions(+), 3 deletions(-) create mode 100644 test/test_image.py create mode 100644 torchvision/csrc/cpu/image/readpng_cpu.cpp create mode 100644 torchvision/csrc/cpu/image/readpng_cpu.h create mode 100644 torchvision/csrc/image.h create mode 100644 torchvision/io/image.py diff --git a/.circleci/config.yml b/.circleci/config.yml index 081337bdcf9..fac5bb866d5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1692,4 +1692,4 @@ workflows: only: /v[0-9]+(\.[0-9]+)*-rc[0-9]+/ name: nightly_binary_win_conda_py3.8_cu102_upload requires: - - nightly_binary_win_conda_py3.8_cu102 \ No newline at end of file + - nightly_binary_win_conda_py3.8_cu102 diff --git a/CMakeLists.txt b/CMakeLists.txt index fa50f155ce4..f1b33f1805d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,10 @@ if(WITH_CUDA) add_definitions(-D__CUDA_NO_HALF_OPERATORS__) endif() +if(Unix) + add_subdirectory("third_party/libpng") +endif() + find_package(Python3 COMPONENTS Development) find_package(Torch REQUIRED) @@ -21,8 +25,17 @@ 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) +file(GLOB IMAGE_HEADERS torchvision/csrc/image.h) +file(GLOB IMAGE_SOURCES torchvision/csrc/cpu/image/*.h torchvision/csrc/cpu/image/*.cpp) + +if(Unix) + add_library(${PROJECT_NAME} SHARED ${MODELS_SOURCES} ${OPERATOR_SOURCES} {IMAGE_SOURCES}) + target_link_libraries(${PROJECT_NAME} PRIVATE ${TORCH_LIBRARIES} Python3::Python "${PNG_LIBRARIES}") +else() + add_library(${PROJECT_NAME} SHARED ${MODELS_SOURCES} ${OPERATOR_SOURCES}) + target_link_libraries(${PROJECT_NAME} PRIVATE ${TORCH_LIBRARIES} Python3::Python) +endif() + set_target_properties(${PROJECT_NAME} PROPERTIES EXPORT_NAME TorchVision) target_include_directories(${PROJECT_NAME} INTERFACE diff --git a/test/test_image.py b/test/test_image.py new file mode 100644 index 00000000000..2dee7912aa7 --- /dev/null +++ b/test/test_image.py @@ -0,0 +1,44 @@ +import os +import unittest +import sys + +import torch +from PIL import Image +if sys.platform.startswith('linux'): + from torchvision.io.image import read_png, decode_png +import numpy as np + +IMAGE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "assets", "fakedata", "imagefolder") + + +def get_images(directory, img_ext): + assert os.path.isdir(directory) + for root, dir, files in os.walk(directory): + for fl in files: + _, ext = os.path.splitext(fl) + if ext == img_ext: + yield os.path.join(root, fl) + + +class ImageTester(unittest.TestCase): + @unittest.skipUnless(sys.platform.startswith("linux"), "Support only available on linux for now.") + def test_read_png(self): + for img_path in get_images(IMAGE_DIR, "png"): + img_pil = torch.from_numpy(np.array(Image.open(img_path))) + img_lpng = read_png(img_path) + self.assertEqual(img_lpng, img_pil) + + @unittest.skipUnless(sys.platform.startswith("linux"), "Support only available on linux for now.") + def test_decode_png(self): + for img_path in get_images(IMAGE_DIR, "png"): + img_pil = torch.from_numpy(np.array(Image.open(img_path))) + size = os.path.getsize(img_path) + img_lpng = decode_png(torch.from_file(img_path, dtype=torch.uint8, size=size)) + self.assertEqual(img_lpng, img_pil) + + self.assertEqual(decode_png(torch.empty()), torch.empty()) + self.assertEqual(decode_png(torch.randint(3, 5, (300,))), torch.empty()) + + +if __name__ == '__main__': + unittest.main() diff --git a/torchvision/csrc/cpu/image/readpng_cpu.cpp b/torchvision/csrc/cpu/image/readpng_cpu.cpp new file mode 100644 index 00000000000..c6581f168b1 --- /dev/null +++ b/torchvision/csrc/cpu/image/readpng_cpu.cpp @@ -0,0 +1,75 @@ +#include "readpng_cpu.h" + +#include +#include +#include + +torch::Tensor decodePNG(const torch::Tensor& data) { + auto png_ptr = + png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); + TORCH_CHECK(png_ptr, "libpng read structure allocation failed!") + auto info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_read_struct(&png_ptr, nullptr, nullptr); + // Seems redundant with the if statement. done here to avoid leaking memory. + TORCH_CHECK(info_ptr, "libpng info structure allocation failed!") + } + + auto datap = data.accessor().data(); + + if (setjmp(png_jmpbuf(png_ptr)) != 0) { + png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); + TORCH_CHECK(false, "Internal error."); + } + auto is_png = !png_sig_cmp(datap, 0, 8); + TORCH_CHECK(is_png, "Content is not png!") + + struct Reader { + png_const_bytep ptr; + } reader; + reader.ptr = png_const_bytep(datap) + 8; + + auto read_callback = + [](png_structp png_ptr, png_bytep output, png_size_t bytes) { + auto reader = static_cast(png_get_io_ptr(png_ptr)); + std::copy(reader->ptr, reader->ptr + bytes, output); + reader->ptr += bytes; + }; + png_set_sig_bytes(png_ptr, 8); + png_set_read_fn(png_ptr, &reader, read_callback); + png_read_info(png_ptr, info_ptr); + + png_uint_32 width, height; + int bit_depth, color_type; + auto retval = png_get_IHDR( + png_ptr, + info_ptr, + &width, + &height, + &bit_depth, + &color_type, + nullptr, + nullptr, + nullptr); + + if (retval != 1) { + png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); + TORCH_CHECK(retval == 1, "Could read image metadata from content.") + } + if (color_type != PNG_COLOR_TYPE_RGB) { + png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); + TORCH_CHECK( + color_type == PNG_COLOR_TYPE_RGB, "Non RGB images are not supported.") + } + + auto tensor = + torch::empty({int64_t(height), int64_t(width), int64_t(3)}, torch::kU8); + auto ptr = tensor.accessor().data(); + auto bytes = png_get_rowbytes(png_ptr, info_ptr); + for (decltype(height) i = 0; i < height; ++i) { + png_read_row(png_ptr, ptr, nullptr); + ptr += bytes; + } + png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); + return tensor; +} diff --git a/torchvision/csrc/cpu/image/readpng_cpu.h b/torchvision/csrc/cpu/image/readpng_cpu.h new file mode 100644 index 00000000000..d2151a43aa9 --- /dev/null +++ b/torchvision/csrc/cpu/image/readpng_cpu.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#include + +torch::Tensor decodePNG(const torch::Tensor& data); diff --git a/torchvision/csrc/image.h b/torchvision/csrc/image.h new file mode 100644 index 00000000000..01d2063564d --- /dev/null +++ b/torchvision/csrc/image.h @@ -0,0 +1,3 @@ +#pragma once + +#include "cpu/image/readpng_cpu.h" diff --git a/torchvision/csrc/vision.cpp b/torchvision/csrc/vision.cpp index 9debc3da9b6..90a3f83fb2b 100644 --- a/torchvision/csrc/vision.cpp +++ b/torchvision/csrc/vision.cpp @@ -14,6 +14,9 @@ #include "ROIAlign.h" #include "ROIPool.h" #include "empty_tensor_op.h" +#ifdef __linux__ +#include "image.h" +#endif #include "nms.h" // If we are in a Windows environment, we need to define @@ -52,4 +55,7 @@ static auto registry = .op("torchvision::ps_roi_align", &ps_roi_align) .op("torchvision::ps_roi_pool", &ps_roi_pool) .op("torchvision::deform_conv2d", &deform_conv2d) +#ifdef __linux__ + .op("torchvision::decode_png", &decodePNG) +#endif .op("torchvision::_cuda_version", &_cuda_version); diff --git a/torchvision/io/image.py b/torchvision/io/image.py new file mode 100644 index 00000000000..bbf7470b097 --- /dev/null +++ b/torchvision/io/image.py @@ -0,0 +1,48 @@ +import torch +from torch import nn, Tensor +import os + + +def decode_png(input): + # type: (Tensor) -> Tensor + """ + Decodes a PNG image into a 3 dimensional RGB Tensor. + The values of the output tensor are uint8 between 0 and 255. + + Arguments: + input (Tensor[1]): a one dimensional int8 tensor containing + the raw bytes of the PNG image. + + Returns: + output (Tensor[image_width, image_height, 3]) + """ + if not isinstance(input, torch.Tensor) or len(input) == 0: + raise ValueError("Expected a non empty 1-dimensional tensor.") + + if not input.dtype == torch.uint8: + raise ValueError("Expected a torch.uint8 tensor.") + output = torch.ops.torchvision.decode_png(input) + return output + + +def read_png(path): + # type: (str) -> Tensor + """ + Reads a PNG image into a 3 dimensional RGB Tensor. + The values of the output tensor are uint8 between 0 and 255. + + Arguments: + path (str): path of the PNG image. + + Returns: + output (Tensor[image_width, image_height, 3]) + """ + if not os.path.isfile(path): + raise ValueError("Expected a valid file path.") + + size = os.path.getsize(path) + if size == 0: + raise ValueError("Expected a non empty file.") + data = torch.from_file(path, dtype=torch.uint8, size=size) + return decode_png(data) + From 7b9ec24747f91c6afefc2abdcc22e9ba84e43c29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 11 Jun 2020 12:01:37 -0500 Subject: [PATCH 004/129] Remove newline --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index fac5bb866d5..081337bdcf9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1692,4 +1692,4 @@ workflows: only: /v[0-9]+(\.[0-9]+)*-rc[0-9]+/ name: nightly_binary_win_conda_py3.8_cu102_upload requires: - - nightly_binary_win_conda_py3.8_cu102 + - nightly_binary_win_conda_py3.8_cu102 \ No newline at end of file From f97a9f09ccda4cc20b501f2a4b724b26eaa7eb20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 11 Jun 2020 14:24:25 -0500 Subject: [PATCH 005/129] Add image extension to compilation instructions --- setup.py | 22 ++++++++++++++++++++++ test/test_image.py | 4 ++-- torchvision/io/image.py | 1 - 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 85a692120b3..05acf292742 100644 --- a/setup.py +++ b/setup.py @@ -171,6 +171,28 @@ def get_extensions(): ) ) + try: + include_dirs += [os.environ['LIBRARY_INC']] + except KeyError: + pass + + try: + library_dirs = [os.environ['LIBRARY_LIB']] + except KeyError: + library_dirs = [] + + # Image reading extension + image_src_dir = os.path.join(this_dir, 'torchvision', 'csrc', 'cpu', 'image') + image_src = glob.glob(os.path.join(image_src_dir, "*.cpp")) + ext_modules.append(extension( + 'torchvision.image', + image_src, + include_dirs=include_dirs + image_src_dir, + library_dirs=library_dirs, + define_macros=define_macros, + extra_compile_args=extra_compile_args + )) + ffmpeg_exe = distutils.spawn.find_executable('ffmpeg') has_ffmpeg = ffmpeg_exe is not None diff --git a/test/test_image.py b/test/test_image.py index 2dee7912aa7..001a3ab76d3 100644 --- a/test/test_image.py +++ b/test/test_image.py @@ -35,10 +35,10 @@ def test_decode_png(self): size = os.path.getsize(img_path) img_lpng = decode_png(torch.from_file(img_path, dtype=torch.uint8, size=size)) self.assertEqual(img_lpng, img_pil) - + self.assertEqual(decode_png(torch.empty()), torch.empty()) self.assertEqual(decode_png(torch.randint(3, 5, (300,))), torch.empty()) - + if __name__ == '__main__': unittest.main() diff --git a/torchvision/io/image.py b/torchvision/io/image.py index bbf7470b097..74dcad09dbe 100644 --- a/torchvision/io/image.py +++ b/torchvision/io/image.py @@ -45,4 +45,3 @@ def read_png(path): raise ValueError("Expected a non empty file.") data = torch.from_file(path, dtype=torch.uint8, size=size) return decode_png(data) - From ac6d26e2180b15a1cb90ccd093cf5fc7b342e145 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 11 Jun 2020 14:52:04 -0500 Subject: [PATCH 006/129] Include png functions as part of the main library --- setup.py | 46 ++++++++++++++++++++------------------ torchvision/io/__init__.py | 3 +++ 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/setup.py b/setup.py index 05acf292742..ea80d6ebbac 100644 --- a/setup.py +++ b/setup.py @@ -77,11 +77,22 @@ def write_version_file(): def get_extensions(): + try: + include_dirs = [os.environ['LIBRARY_INC']] + except KeyError: + include_dirs = [] + + try: + library_dirs = [os.environ['LIBRARY_LIB']] + except KeyError: + library_dirs = [] + this_dir = os.path.dirname(os.path.abspath(__file__)) extensions_dir = os.path.join(this_dir, 'torchvision', 'csrc') main_file = glob.glob(os.path.join(extensions_dir, '*.cpp')) source_cpu = glob.glob(os.path.join(extensions_dir, 'cpu', '*.cpp')) + image_src = glob.glob(os.path.join(extensions_dir, 'cpu', 'image', '*.cpp')) is_rocm_pytorch = False if torch.__version__ >= '1.5': @@ -104,7 +115,7 @@ def get_extensions(): else: source_cuda = glob.glob(os.path.join(extensions_dir, 'cuda', '*.cu')) - sources = main_file + source_cpu + sources = main_file + source_cpu + image_src extension = CppExtension compile_cpp_tests = os.getenv('WITH_CPP_MODELS_TEST', '0') == '1' @@ -149,13 +160,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, ) @@ -171,27 +183,17 @@ def get_extensions(): ) ) - try: - include_dirs += [os.environ['LIBRARY_INC']] - except KeyError: - pass - - try: - library_dirs = [os.environ['LIBRARY_LIB']] - except KeyError: - library_dirs = [] - # Image reading extension - image_src_dir = os.path.join(this_dir, 'torchvision', 'csrc', 'cpu', 'image') - image_src = glob.glob(os.path.join(image_src_dir, "*.cpp")) - ext_modules.append(extension( - 'torchvision.image', - image_src, - include_dirs=include_dirs + image_src_dir, - library_dirs=library_dirs, - define_macros=define_macros, - extra_compile_args=extra_compile_args - )) + # image_src_dir = os.path.join(this_dir, 'torchvision', 'csrc', 'cpu', 'image') + # image_src = glob.glob(os.path.join(image_src_dir, "*.cpp")) + # ext_modules.append(extension( + # 'torchvision.image', + # image_src, + # include_dirs=include_dirs + [image_src_dir], + # library_dirs=library_dirs, + # define_macros=define_macros, + # extra_compile_args=extra_compile_args + # )) ffmpeg_exe = distutils.spawn.find_executable('ffmpeg') has_ffmpeg = ffmpeg_exe is not None diff --git a/torchvision/io/__init__.py b/torchvision/io/__init__.py index cbbf560412e..7bf9db55557 100644 --- a/torchvision/io/__init__.py +++ b/torchvision/io/__init__.py @@ -15,6 +15,8 @@ write_video, ) +from .image import (read_png) + __all__ = [ "write_video", @@ -31,4 +33,5 @@ "_read_video_meta_data", "VideoMetaData", "Timebase", + "read_png" ] From b14912e32bf29885b0b4c2448c2d8bc4a1ca2178 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 11 Jun 2020 17:28:16 -0500 Subject: [PATCH 007/129] Update CMakeLists --- CMakeLists.txt | 27 +++++++++------------------ torchvision/csrc/vision.cpp | 4 ---- 2 files changed, 9 insertions(+), 22 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f1b33f1805d..753c9f87218 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,36 +10,27 @@ if(WITH_CUDA) add_definitions(-D__CUDA_NO_HALF_OPERATORS__) endif() -if(Unix) - add_subdirectory("third_party/libpng") -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) -file(GLOB IMAGE_HEADERS torchvision/csrc/image.h) -file(GLOB IMAGE_SOURCES torchvision/csrc/cpu/image/*.h torchvision/csrc/cpu/image/*.cpp) - -if(Unix) - add_library(${PROJECT_NAME} SHARED ${MODELS_SOURCES} ${OPERATOR_SOURCES} {IMAGE_SOURCES}) - target_link_libraries(${PROJECT_NAME} PRIVATE ${TORCH_LIBRARIES} Python3::Python "${PNG_LIBRARIES}") -else() - add_library(${PROJECT_NAME} SHARED ${MODELS_SOURCES} ${OPERATOR_SOURCES}) - target_link_libraries(${PROJECT_NAME} PRIVATE ${TORCH_LIBRARIES} Python3::Python) -endif() - +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 - $ + $ $) include(GNUInstallDirs) @@ -74,7 +65,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) diff --git a/torchvision/csrc/vision.cpp b/torchvision/csrc/vision.cpp index 90a3f83fb2b..b0d453c4f33 100644 --- a/torchvision/csrc/vision.cpp +++ b/torchvision/csrc/vision.cpp @@ -14,9 +14,7 @@ #include "ROIAlign.h" #include "ROIPool.h" #include "empty_tensor_op.h" -#ifdef __linux__ #include "image.h" -#endif #include "nms.h" // If we are in a Windows environment, we need to define @@ -55,7 +53,5 @@ static auto registry = .op("torchvision::ps_roi_align", &ps_roi_align) .op("torchvision::ps_roi_pool", &ps_roi_pool) .op("torchvision::deform_conv2d", &deform_conv2d) -#ifdef __linux__ .op("torchvision::decode_png", &decodePNG) -#endif .op("torchvision::_cuda_version", &_cuda_version); From 770cea5ef32e46b1735a696063709bdba5a2204f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 11 Jun 2020 18:32:37 -0500 Subject: [PATCH 008/129] Detect if building on conda-build --- setup.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/setup.py b/setup.py index ea80d6ebbac..c1c37d4d52a 100644 --- a/setup.py +++ b/setup.py @@ -87,6 +87,16 @@ def get_extensions(): except KeyError: library_dirs = [] + # Check if building on Conda + build_prefix = os.environ.get('BUILD_PREFIX', None) + is_conda_build = build_prefix is not None + if is_conda_build: + # Add LibPNG headers/libraries + png_include = os.path.join(build_prefix, 'include/libpng16/png') + conda_library = os.path.join(build_prefix, 'lib') + include_dirs.append(png_include) + library_dirs.append(conda_library) + this_dir = os.path.dirname(os.path.abspath(__file__)) extensions_dir = os.path.join(this_dir, 'torchvision', 'csrc') From 0861b8077c5da5ea3ed54c61c817d7780e6cab81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 11 Jun 2020 18:45:47 -0500 Subject: [PATCH 009/129] Debug --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index c1c37d4d52a..c36f247cb75 100644 --- a/setup.py +++ b/setup.py @@ -90,6 +90,7 @@ def get_extensions(): # Check if building on Conda build_prefix = os.environ.get('BUILD_PREFIX', None) is_conda_build = build_prefix is not None + print('Running build on conda-build: {0}'.format(is_conda_build)) if is_conda_build: # Add LibPNG headers/libraries png_include = os.path.join(build_prefix, 'include/libpng16/png') From a42a029933bf972e8c788469138e973da21103ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 11 Jun 2020 18:58:48 -0500 Subject: [PATCH 010/129] More debug messages --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index c36f247cb75..b639f82dc32 100644 --- a/setup.py +++ b/setup.py @@ -94,6 +94,7 @@ def get_extensions(): if is_conda_build: # Add LibPNG headers/libraries png_include = os.path.join(build_prefix, 'include/libpng16/png') + print('PNG found? {0}'.format(os.path.isdir(png_include))) conda_library = os.path.join(build_prefix, 'lib') include_dirs.append(png_include) library_dirs.append(conda_library) From b7a19ea56106249619cbb2dfef4a1ac8423a9cb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 11 Jun 2020 19:16:17 -0500 Subject: [PATCH 011/129] Print globbed libreries --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index b639f82dc32..d0b5b956959 100644 --- a/setup.py +++ b/setup.py @@ -95,6 +95,7 @@ def get_extensions(): # Add LibPNG headers/libraries png_include = os.path.join(build_prefix, 'include/libpng16/png') print('PNG found? {0}'.format(os.path.isdir(png_include))) + print('Library path: {0}'.format(glob.glob(os.path.join(build_prefix, 'include/lib*')))) conda_library = os.path.join(build_prefix, 'lib') include_dirs.append(png_include) library_dirs.append(conda_library) From 1afde4df388849a2f1b341e8ac93679f67fb9dd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 11 Jun 2020 19:34:37 -0500 Subject: [PATCH 012/129] Print globbed libreries --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index d0b5b956959..5bbebdedd3a 100644 --- a/setup.py +++ b/setup.py @@ -95,7 +95,8 @@ def get_extensions(): # Add LibPNG headers/libraries png_include = os.path.join(build_prefix, 'include/libpng16/png') print('PNG found? {0}'.format(os.path.isdir(png_include))) - print('Library path: {0}'.format(glob.glob(os.path.join(build_prefix, 'include/lib*')))) + print('Library path: {0}'.format( + glob.glob(os.path.join(build_prefix, 'include/libpng16/*')))) conda_library = os.path.join(build_prefix, 'lib') include_dirs.append(png_include) library_dirs.append(conda_library) From 386fd5b34fd3df4734549b1dc0bd6163c005439a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 11 Jun 2020 19:40:56 -0500 Subject: [PATCH 013/129] Point to correct PNG path --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 5bbebdedd3a..a3a75aa40d8 100644 --- a/setup.py +++ b/setup.py @@ -93,7 +93,7 @@ def get_extensions(): print('Running build on conda-build: {0}'.format(is_conda_build)) if is_conda_build: # Add LibPNG headers/libraries - png_include = os.path.join(build_prefix, 'include/libpng16/png') + png_include = os.path.join(build_prefix, 'include/libpng16') print('PNG found? {0}'.format(os.path.isdir(png_include))) print('Library path: {0}'.format( glob.glob(os.path.join(build_prefix, 'include/libpng16/*')))) From 2b5c4693804639584fa2d2ba796b22f58bd76e18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Fri, 12 Jun 2020 13:35:09 -0500 Subject: [PATCH 014/129] Remove libJPEG preventively --- packaging/torchvision/meta.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/torchvision/meta.yaml b/packaging/torchvision/meta.yaml index b87b63a1bc4..773307737b1 100644 --- a/packaging/torchvision/meta.yaml +++ b/packaging/torchvision/meta.yaml @@ -21,7 +21,7 @@ requirements: run: - python - libpng - - libjpeg-turbo + # - libjpeg-turbo - pillow >=4.1.1 - numpy >=1.11 {{ environ.get('CONDA_PYTORCH_CONSTRAINT') }} From 0341aa504abd7b040ddd7294e5daaaa16ee87656 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Fri, 12 Jun 2020 14:39:45 -0500 Subject: [PATCH 015/129] Debug extension loading --- torchvision/extension.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/torchvision/extension.py b/torchvision/extension.py index db3356aa67a..17b606ba9a8 100644 --- a/torchvision/extension.py +++ b/torchvision/extension.py @@ -24,6 +24,8 @@ def _register_extensions(): _register_extensions() _HAS_OPS = True except (ImportError, OSError): + import traceback + traceback.print_exc() pass From 721e5e3d806c986d5dff33c3ded73bfac852ba25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Fri, 12 Jun 2020 15:01:50 -0500 Subject: [PATCH 016/129] Link libpng explicitly --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index a3a75aa40d8..dcfbf64f5d1 100644 --- a/setup.py +++ b/setup.py @@ -182,6 +182,7 @@ def get_extensions(): sources, include_dirs=include_dirs, library_dirs=library_dirs, + libraries=['libpng'], define_macros=define_macros, extra_compile_args=extra_compile_args, ) From 2186d688410fa881a2462a92fbb3b6e4726754af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Fri, 12 Jun 2020 15:07:06 -0500 Subject: [PATCH 017/129] Link with PNG --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index dcfbf64f5d1..fcd711e8bf6 100644 --- a/setup.py +++ b/setup.py @@ -182,7 +182,7 @@ def get_extensions(): sources, include_dirs=include_dirs, library_dirs=library_dirs, - libraries=['libpng'], + libraries=['png'], define_macros=define_macros, extra_compile_args=extra_compile_args, ) From b80fb08363eade6f2f533ee7b6dcfabf7a75a815 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 8 Jun 2020 15:31:40 -0500 Subject: [PATCH 018/129] Add libpng requirement into conda recipe --- packaging/torchvision/meta.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packaging/torchvision/meta.yaml b/packaging/torchvision/meta.yaml index 8be8eabdd09..639b0a254b8 100644 --- a/packaging/torchvision/meta.yaml +++ b/packaging/torchvision/meta.yaml @@ -8,6 +8,7 @@ source: requirements: build: - {{ compiler('c') }} # [win] + - libpng host: - python @@ -18,6 +19,7 @@ requirements: run: - python + - libpng - pillow >=4.1.1 - numpy >=1.11 {{ environ.get('CONDA_PYTORCH_CONSTRAINT') }} From 3d153f082b469536209a6c8f1d17abb38d85a238 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 8 Jun 2020 18:23:15 -0500 Subject: [PATCH 019/129] Try to install libjpeg-turbo --- packaging/torchvision/conda_build_config.yaml | 3 +++ packaging/torchvision/meta.yaml | 2 ++ 2 files changed, 5 insertions(+) diff --git a/packaging/torchvision/conda_build_config.yaml b/packaging/torchvision/conda_build_config.yaml index 5188bb0ebec..adbbc732a0c 100644 --- a/packaging/torchvision/conda_build_config.yaml +++ b/packaging/torchvision/conda_build_config.yaml @@ -1,3 +1,6 @@ +channel_sources: + - defaults,conda-forge + blas_impl: - mkl # [x86_64] c_compiler: diff --git a/packaging/torchvision/meta.yaml b/packaging/torchvision/meta.yaml index 639b0a254b8..b87b63a1bc4 100644 --- a/packaging/torchvision/meta.yaml +++ b/packaging/torchvision/meta.yaml @@ -9,6 +9,7 @@ requirements: build: - {{ compiler('c') }} # [win] - libpng + - libjpeg-turbo host: - python @@ -20,6 +21,7 @@ requirements: run: - python - libpng + - libjpeg-turbo - pillow >=4.1.1 - numpy >=1.11 {{ environ.get('CONDA_PYTORCH_CONSTRAINT') }} From 36b0a8fb050c817dcecc4ac426e40d79914c6c6b Mon Sep 17 00:00:00 2001 From: Ryad ZENINE Date: Thu, 13 Feb 2020 14:40:17 +0100 Subject: [PATCH 020/129] Add PNG reading capabilities --- .circleci/config.yml | 2 +- CMakeLists.txt | 17 ++++- test/test_image.py | 44 +++++++++++++ torchvision/csrc/cpu/image/readpng_cpu.cpp | 75 ++++++++++++++++++++++ torchvision/csrc/cpu/image/readpng_cpu.h | 6 ++ torchvision/csrc/image.h | 3 + torchvision/csrc/vision.cpp | 6 ++ torchvision/io/image.py | 48 ++++++++++++++ 8 files changed, 198 insertions(+), 3 deletions(-) create mode 100644 test/test_image.py create mode 100644 torchvision/csrc/cpu/image/readpng_cpu.cpp create mode 100644 torchvision/csrc/cpu/image/readpng_cpu.h create mode 100644 torchvision/csrc/image.h create mode 100644 torchvision/io/image.py diff --git a/.circleci/config.yml b/.circleci/config.yml index 081337bdcf9..fac5bb866d5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1692,4 +1692,4 @@ workflows: only: /v[0-9]+(\.[0-9]+)*-rc[0-9]+/ name: nightly_binary_win_conda_py3.8_cu102_upload requires: - - nightly_binary_win_conda_py3.8_cu102 \ No newline at end of file + - nightly_binary_win_conda_py3.8_cu102 diff --git a/CMakeLists.txt b/CMakeLists.txt index fa50f155ce4..f1b33f1805d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,10 @@ if(WITH_CUDA) add_definitions(-D__CUDA_NO_HALF_OPERATORS__) endif() +if(Unix) + add_subdirectory("third_party/libpng") +endif() + find_package(Python3 COMPONENTS Development) find_package(Torch REQUIRED) @@ -21,8 +25,17 @@ 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) +file(GLOB IMAGE_HEADERS torchvision/csrc/image.h) +file(GLOB IMAGE_SOURCES torchvision/csrc/cpu/image/*.h torchvision/csrc/cpu/image/*.cpp) + +if(Unix) + add_library(${PROJECT_NAME} SHARED ${MODELS_SOURCES} ${OPERATOR_SOURCES} {IMAGE_SOURCES}) + target_link_libraries(${PROJECT_NAME} PRIVATE ${TORCH_LIBRARIES} Python3::Python "${PNG_LIBRARIES}") +else() + add_library(${PROJECT_NAME} SHARED ${MODELS_SOURCES} ${OPERATOR_SOURCES}) + target_link_libraries(${PROJECT_NAME} PRIVATE ${TORCH_LIBRARIES} Python3::Python) +endif() + set_target_properties(${PROJECT_NAME} PROPERTIES EXPORT_NAME TorchVision) target_include_directories(${PROJECT_NAME} INTERFACE diff --git a/test/test_image.py b/test/test_image.py new file mode 100644 index 00000000000..2dee7912aa7 --- /dev/null +++ b/test/test_image.py @@ -0,0 +1,44 @@ +import os +import unittest +import sys + +import torch +from PIL import Image +if sys.platform.startswith('linux'): + from torchvision.io.image import read_png, decode_png +import numpy as np + +IMAGE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "assets", "fakedata", "imagefolder") + + +def get_images(directory, img_ext): + assert os.path.isdir(directory) + for root, dir, files in os.walk(directory): + for fl in files: + _, ext = os.path.splitext(fl) + if ext == img_ext: + yield os.path.join(root, fl) + + +class ImageTester(unittest.TestCase): + @unittest.skipUnless(sys.platform.startswith("linux"), "Support only available on linux for now.") + def test_read_png(self): + for img_path in get_images(IMAGE_DIR, "png"): + img_pil = torch.from_numpy(np.array(Image.open(img_path))) + img_lpng = read_png(img_path) + self.assertEqual(img_lpng, img_pil) + + @unittest.skipUnless(sys.platform.startswith("linux"), "Support only available on linux for now.") + def test_decode_png(self): + for img_path in get_images(IMAGE_DIR, "png"): + img_pil = torch.from_numpy(np.array(Image.open(img_path))) + size = os.path.getsize(img_path) + img_lpng = decode_png(torch.from_file(img_path, dtype=torch.uint8, size=size)) + self.assertEqual(img_lpng, img_pil) + + self.assertEqual(decode_png(torch.empty()), torch.empty()) + self.assertEqual(decode_png(torch.randint(3, 5, (300,))), torch.empty()) + + +if __name__ == '__main__': + unittest.main() diff --git a/torchvision/csrc/cpu/image/readpng_cpu.cpp b/torchvision/csrc/cpu/image/readpng_cpu.cpp new file mode 100644 index 00000000000..c6581f168b1 --- /dev/null +++ b/torchvision/csrc/cpu/image/readpng_cpu.cpp @@ -0,0 +1,75 @@ +#include "readpng_cpu.h" + +#include +#include +#include + +torch::Tensor decodePNG(const torch::Tensor& data) { + auto png_ptr = + png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); + TORCH_CHECK(png_ptr, "libpng read structure allocation failed!") + auto info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_read_struct(&png_ptr, nullptr, nullptr); + // Seems redundant with the if statement. done here to avoid leaking memory. + TORCH_CHECK(info_ptr, "libpng info structure allocation failed!") + } + + auto datap = data.accessor().data(); + + if (setjmp(png_jmpbuf(png_ptr)) != 0) { + png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); + TORCH_CHECK(false, "Internal error."); + } + auto is_png = !png_sig_cmp(datap, 0, 8); + TORCH_CHECK(is_png, "Content is not png!") + + struct Reader { + png_const_bytep ptr; + } reader; + reader.ptr = png_const_bytep(datap) + 8; + + auto read_callback = + [](png_structp png_ptr, png_bytep output, png_size_t bytes) { + auto reader = static_cast(png_get_io_ptr(png_ptr)); + std::copy(reader->ptr, reader->ptr + bytes, output); + reader->ptr += bytes; + }; + png_set_sig_bytes(png_ptr, 8); + png_set_read_fn(png_ptr, &reader, read_callback); + png_read_info(png_ptr, info_ptr); + + png_uint_32 width, height; + int bit_depth, color_type; + auto retval = png_get_IHDR( + png_ptr, + info_ptr, + &width, + &height, + &bit_depth, + &color_type, + nullptr, + nullptr, + nullptr); + + if (retval != 1) { + png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); + TORCH_CHECK(retval == 1, "Could read image metadata from content.") + } + if (color_type != PNG_COLOR_TYPE_RGB) { + png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); + TORCH_CHECK( + color_type == PNG_COLOR_TYPE_RGB, "Non RGB images are not supported.") + } + + auto tensor = + torch::empty({int64_t(height), int64_t(width), int64_t(3)}, torch::kU8); + auto ptr = tensor.accessor().data(); + auto bytes = png_get_rowbytes(png_ptr, info_ptr); + for (decltype(height) i = 0; i < height; ++i) { + png_read_row(png_ptr, ptr, nullptr); + ptr += bytes; + } + png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); + return tensor; +} diff --git a/torchvision/csrc/cpu/image/readpng_cpu.h b/torchvision/csrc/cpu/image/readpng_cpu.h new file mode 100644 index 00000000000..d2151a43aa9 --- /dev/null +++ b/torchvision/csrc/cpu/image/readpng_cpu.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#include + +torch::Tensor decodePNG(const torch::Tensor& data); diff --git a/torchvision/csrc/image.h b/torchvision/csrc/image.h new file mode 100644 index 00000000000..01d2063564d --- /dev/null +++ b/torchvision/csrc/image.h @@ -0,0 +1,3 @@ +#pragma once + +#include "cpu/image/readpng_cpu.h" diff --git a/torchvision/csrc/vision.cpp b/torchvision/csrc/vision.cpp index 9debc3da9b6..90a3f83fb2b 100644 --- a/torchvision/csrc/vision.cpp +++ b/torchvision/csrc/vision.cpp @@ -14,6 +14,9 @@ #include "ROIAlign.h" #include "ROIPool.h" #include "empty_tensor_op.h" +#ifdef __linux__ +#include "image.h" +#endif #include "nms.h" // If we are in a Windows environment, we need to define @@ -52,4 +55,7 @@ static auto registry = .op("torchvision::ps_roi_align", &ps_roi_align) .op("torchvision::ps_roi_pool", &ps_roi_pool) .op("torchvision::deform_conv2d", &deform_conv2d) +#ifdef __linux__ + .op("torchvision::decode_png", &decodePNG) +#endif .op("torchvision::_cuda_version", &_cuda_version); diff --git a/torchvision/io/image.py b/torchvision/io/image.py new file mode 100644 index 00000000000..bbf7470b097 --- /dev/null +++ b/torchvision/io/image.py @@ -0,0 +1,48 @@ +import torch +from torch import nn, Tensor +import os + + +def decode_png(input): + # type: (Tensor) -> Tensor + """ + Decodes a PNG image into a 3 dimensional RGB Tensor. + The values of the output tensor are uint8 between 0 and 255. + + Arguments: + input (Tensor[1]): a one dimensional int8 tensor containing + the raw bytes of the PNG image. + + Returns: + output (Tensor[image_width, image_height, 3]) + """ + if not isinstance(input, torch.Tensor) or len(input) == 0: + raise ValueError("Expected a non empty 1-dimensional tensor.") + + if not input.dtype == torch.uint8: + raise ValueError("Expected a torch.uint8 tensor.") + output = torch.ops.torchvision.decode_png(input) + return output + + +def read_png(path): + # type: (str) -> Tensor + """ + Reads a PNG image into a 3 dimensional RGB Tensor. + The values of the output tensor are uint8 between 0 and 255. + + Arguments: + path (str): path of the PNG image. + + Returns: + output (Tensor[image_width, image_height, 3]) + """ + if not os.path.isfile(path): + raise ValueError("Expected a valid file path.") + + size = os.path.getsize(path) + if size == 0: + raise ValueError("Expected a non empty file.") + data = torch.from_file(path, dtype=torch.uint8, size=size) + return decode_png(data) + From 9d14d9e5fc0a2a8b2bc70ad4cb1aa50d2d39110a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 11 Jun 2020 12:01:37 -0500 Subject: [PATCH 021/129] Remove newline --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index fac5bb866d5..081337bdcf9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1692,4 +1692,4 @@ workflows: only: /v[0-9]+(\.[0-9]+)*-rc[0-9]+/ name: nightly_binary_win_conda_py3.8_cu102_upload requires: - - nightly_binary_win_conda_py3.8_cu102 + - nightly_binary_win_conda_py3.8_cu102 \ No newline at end of file From 852a2899332079577b5c09c0fe826fd3da914542 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 11 Jun 2020 14:24:25 -0500 Subject: [PATCH 022/129] Add image extension to compilation instructions --- setup.py | 22 ++++++++++++++++++++++ test/test_image.py | 4 ++-- torchvision/io/image.py | 1 - 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 85a692120b3..05acf292742 100644 --- a/setup.py +++ b/setup.py @@ -171,6 +171,28 @@ def get_extensions(): ) ) + try: + include_dirs += [os.environ['LIBRARY_INC']] + except KeyError: + pass + + try: + library_dirs = [os.environ['LIBRARY_LIB']] + except KeyError: + library_dirs = [] + + # Image reading extension + image_src_dir = os.path.join(this_dir, 'torchvision', 'csrc', 'cpu', 'image') + image_src = glob.glob(os.path.join(image_src_dir, "*.cpp")) + ext_modules.append(extension( + 'torchvision.image', + image_src, + include_dirs=include_dirs + image_src_dir, + library_dirs=library_dirs, + define_macros=define_macros, + extra_compile_args=extra_compile_args + )) + ffmpeg_exe = distutils.spawn.find_executable('ffmpeg') has_ffmpeg = ffmpeg_exe is not None diff --git a/test/test_image.py b/test/test_image.py index 2dee7912aa7..001a3ab76d3 100644 --- a/test/test_image.py +++ b/test/test_image.py @@ -35,10 +35,10 @@ def test_decode_png(self): size = os.path.getsize(img_path) img_lpng = decode_png(torch.from_file(img_path, dtype=torch.uint8, size=size)) self.assertEqual(img_lpng, img_pil) - + self.assertEqual(decode_png(torch.empty()), torch.empty()) self.assertEqual(decode_png(torch.randint(3, 5, (300,))), torch.empty()) - + if __name__ == '__main__': unittest.main() diff --git a/torchvision/io/image.py b/torchvision/io/image.py index bbf7470b097..74dcad09dbe 100644 --- a/torchvision/io/image.py +++ b/torchvision/io/image.py @@ -45,4 +45,3 @@ def read_png(path): raise ValueError("Expected a non empty file.") data = torch.from_file(path, dtype=torch.uint8, size=size) return decode_png(data) - From 3e86f49c5a5f78acc97bef73ac6fad1a897d46c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 11 Jun 2020 14:52:04 -0500 Subject: [PATCH 023/129] Include png functions as part of the main library --- setup.py | 46 ++++++++++++++++++++------------------ torchvision/io/__init__.py | 3 +++ 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/setup.py b/setup.py index 05acf292742..ea80d6ebbac 100644 --- a/setup.py +++ b/setup.py @@ -77,11 +77,22 @@ def write_version_file(): def get_extensions(): + try: + include_dirs = [os.environ['LIBRARY_INC']] + except KeyError: + include_dirs = [] + + try: + library_dirs = [os.environ['LIBRARY_LIB']] + except KeyError: + library_dirs = [] + this_dir = os.path.dirname(os.path.abspath(__file__)) extensions_dir = os.path.join(this_dir, 'torchvision', 'csrc') main_file = glob.glob(os.path.join(extensions_dir, '*.cpp')) source_cpu = glob.glob(os.path.join(extensions_dir, 'cpu', '*.cpp')) + image_src = glob.glob(os.path.join(extensions_dir, 'cpu', 'image', '*.cpp')) is_rocm_pytorch = False if torch.__version__ >= '1.5': @@ -104,7 +115,7 @@ def get_extensions(): else: source_cuda = glob.glob(os.path.join(extensions_dir, 'cuda', '*.cu')) - sources = main_file + source_cpu + sources = main_file + source_cpu + image_src extension = CppExtension compile_cpp_tests = os.getenv('WITH_CPP_MODELS_TEST', '0') == '1' @@ -149,13 +160,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, ) @@ -171,27 +183,17 @@ def get_extensions(): ) ) - try: - include_dirs += [os.environ['LIBRARY_INC']] - except KeyError: - pass - - try: - library_dirs = [os.environ['LIBRARY_LIB']] - except KeyError: - library_dirs = [] - # Image reading extension - image_src_dir = os.path.join(this_dir, 'torchvision', 'csrc', 'cpu', 'image') - image_src = glob.glob(os.path.join(image_src_dir, "*.cpp")) - ext_modules.append(extension( - 'torchvision.image', - image_src, - include_dirs=include_dirs + image_src_dir, - library_dirs=library_dirs, - define_macros=define_macros, - extra_compile_args=extra_compile_args - )) + # image_src_dir = os.path.join(this_dir, 'torchvision', 'csrc', 'cpu', 'image') + # image_src = glob.glob(os.path.join(image_src_dir, "*.cpp")) + # ext_modules.append(extension( + # 'torchvision.image', + # image_src, + # include_dirs=include_dirs + [image_src_dir], + # library_dirs=library_dirs, + # define_macros=define_macros, + # extra_compile_args=extra_compile_args + # )) ffmpeg_exe = distutils.spawn.find_executable('ffmpeg') has_ffmpeg = ffmpeg_exe is not None diff --git a/torchvision/io/__init__.py b/torchvision/io/__init__.py index cbbf560412e..7bf9db55557 100644 --- a/torchvision/io/__init__.py +++ b/torchvision/io/__init__.py @@ -15,6 +15,8 @@ write_video, ) +from .image import (read_png) + __all__ = [ "write_video", @@ -31,4 +33,5 @@ "_read_video_meta_data", "VideoMetaData", "Timebase", + "read_png" ] From 021e767591b993750e74febe9f84f09cf471c543 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 11 Jun 2020 17:28:16 -0500 Subject: [PATCH 024/129] Update CMakeLists --- CMakeLists.txt | 27 +++++++++------------------ torchvision/csrc/vision.cpp | 4 ---- 2 files changed, 9 insertions(+), 22 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f1b33f1805d..753c9f87218 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,36 +10,27 @@ if(WITH_CUDA) add_definitions(-D__CUDA_NO_HALF_OPERATORS__) endif() -if(Unix) - add_subdirectory("third_party/libpng") -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) -file(GLOB IMAGE_HEADERS torchvision/csrc/image.h) -file(GLOB IMAGE_SOURCES torchvision/csrc/cpu/image/*.h torchvision/csrc/cpu/image/*.cpp) - -if(Unix) - add_library(${PROJECT_NAME} SHARED ${MODELS_SOURCES} ${OPERATOR_SOURCES} {IMAGE_SOURCES}) - target_link_libraries(${PROJECT_NAME} PRIVATE ${TORCH_LIBRARIES} Python3::Python "${PNG_LIBRARIES}") -else() - add_library(${PROJECT_NAME} SHARED ${MODELS_SOURCES} ${OPERATOR_SOURCES}) - target_link_libraries(${PROJECT_NAME} PRIVATE ${TORCH_LIBRARIES} Python3::Python) -endif() - +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 - $ + $ $) include(GNUInstallDirs) @@ -74,7 +65,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) diff --git a/torchvision/csrc/vision.cpp b/torchvision/csrc/vision.cpp index 90a3f83fb2b..b0d453c4f33 100644 --- a/torchvision/csrc/vision.cpp +++ b/torchvision/csrc/vision.cpp @@ -14,9 +14,7 @@ #include "ROIAlign.h" #include "ROIPool.h" #include "empty_tensor_op.h" -#ifdef __linux__ #include "image.h" -#endif #include "nms.h" // If we are in a Windows environment, we need to define @@ -55,7 +53,5 @@ static auto registry = .op("torchvision::ps_roi_align", &ps_roi_align) .op("torchvision::ps_roi_pool", &ps_roi_pool) .op("torchvision::deform_conv2d", &deform_conv2d) -#ifdef __linux__ .op("torchvision::decode_png", &decodePNG) -#endif .op("torchvision::_cuda_version", &_cuda_version); From e73417501f8f6a7dddd3e6f6f152fa0e574ff700 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 11 Jun 2020 18:32:37 -0500 Subject: [PATCH 025/129] Detect if building on conda-build --- setup.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/setup.py b/setup.py index ea80d6ebbac..c1c37d4d52a 100644 --- a/setup.py +++ b/setup.py @@ -87,6 +87,16 @@ def get_extensions(): except KeyError: library_dirs = [] + # Check if building on Conda + build_prefix = os.environ.get('BUILD_PREFIX', None) + is_conda_build = build_prefix is not None + if is_conda_build: + # Add LibPNG headers/libraries + png_include = os.path.join(build_prefix, 'include/libpng16/png') + conda_library = os.path.join(build_prefix, 'lib') + include_dirs.append(png_include) + library_dirs.append(conda_library) + this_dir = os.path.dirname(os.path.abspath(__file__)) extensions_dir = os.path.join(this_dir, 'torchvision', 'csrc') From 58c652473657e5f13e123684c0f3725f7edafcba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 11 Jun 2020 18:45:47 -0500 Subject: [PATCH 026/129] Debug --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index c1c37d4d52a..c36f247cb75 100644 --- a/setup.py +++ b/setup.py @@ -90,6 +90,7 @@ def get_extensions(): # Check if building on Conda build_prefix = os.environ.get('BUILD_PREFIX', None) is_conda_build = build_prefix is not None + print('Running build on conda-build: {0}'.format(is_conda_build)) if is_conda_build: # Add LibPNG headers/libraries png_include = os.path.join(build_prefix, 'include/libpng16/png') From b9295c1efc15499d227ce3830ef9a664752a0d59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 11 Jun 2020 18:58:48 -0500 Subject: [PATCH 027/129] More debug messages --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index c36f247cb75..b639f82dc32 100644 --- a/setup.py +++ b/setup.py @@ -94,6 +94,7 @@ def get_extensions(): if is_conda_build: # Add LibPNG headers/libraries png_include = os.path.join(build_prefix, 'include/libpng16/png') + print('PNG found? {0}'.format(os.path.isdir(png_include))) conda_library = os.path.join(build_prefix, 'lib') include_dirs.append(png_include) library_dirs.append(conda_library) From 02fa9d92dbb80e38bda99ef1f49f5ff52e7ad0a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 11 Jun 2020 19:16:17 -0500 Subject: [PATCH 028/129] Print globbed libreries --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index b639f82dc32..d0b5b956959 100644 --- a/setup.py +++ b/setup.py @@ -95,6 +95,7 @@ def get_extensions(): # Add LibPNG headers/libraries png_include = os.path.join(build_prefix, 'include/libpng16/png') print('PNG found? {0}'.format(os.path.isdir(png_include))) + print('Library path: {0}'.format(glob.glob(os.path.join(build_prefix, 'include/lib*')))) conda_library = os.path.join(build_prefix, 'lib') include_dirs.append(png_include) library_dirs.append(conda_library) From 6c757d4f61216829032bb8b959e356c97191b883 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 11 Jun 2020 19:34:37 -0500 Subject: [PATCH 029/129] Print globbed libreries --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index d0b5b956959..5bbebdedd3a 100644 --- a/setup.py +++ b/setup.py @@ -95,7 +95,8 @@ def get_extensions(): # Add LibPNG headers/libraries png_include = os.path.join(build_prefix, 'include/libpng16/png') print('PNG found? {0}'.format(os.path.isdir(png_include))) - print('Library path: {0}'.format(glob.glob(os.path.join(build_prefix, 'include/lib*')))) + print('Library path: {0}'.format( + glob.glob(os.path.join(build_prefix, 'include/libpng16/*')))) conda_library = os.path.join(build_prefix, 'lib') include_dirs.append(png_include) library_dirs.append(conda_library) From c207eab9078aa11e295d63cbe7d38d68ae4848de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 11 Jun 2020 19:40:56 -0500 Subject: [PATCH 030/129] Point to correct PNG path --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 5bbebdedd3a..a3a75aa40d8 100644 --- a/setup.py +++ b/setup.py @@ -93,7 +93,7 @@ def get_extensions(): print('Running build on conda-build: {0}'.format(is_conda_build)) if is_conda_build: # Add LibPNG headers/libraries - png_include = os.path.join(build_prefix, 'include/libpng16/png') + png_include = os.path.join(build_prefix, 'include/libpng16') print('PNG found? {0}'.format(os.path.isdir(png_include))) print('Library path: {0}'.format( glob.glob(os.path.join(build_prefix, 'include/libpng16/*')))) From a1aa2e644c99646685bdb740bc20928f25b8cd5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Fri, 12 Jun 2020 13:35:09 -0500 Subject: [PATCH 031/129] Remove libJPEG preventively --- packaging/torchvision/meta.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/torchvision/meta.yaml b/packaging/torchvision/meta.yaml index b87b63a1bc4..773307737b1 100644 --- a/packaging/torchvision/meta.yaml +++ b/packaging/torchvision/meta.yaml @@ -21,7 +21,7 @@ requirements: run: - python - libpng - - libjpeg-turbo + # - libjpeg-turbo - pillow >=4.1.1 - numpy >=1.11 {{ environ.get('CONDA_PYTORCH_CONSTRAINT') }} From 34fc7d641b5d29d04f21c11feadb91d81a24513f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Fri, 12 Jun 2020 14:39:45 -0500 Subject: [PATCH 032/129] Debug extension loading --- torchvision/extension.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/torchvision/extension.py b/torchvision/extension.py index db3356aa67a..17b606ba9a8 100644 --- a/torchvision/extension.py +++ b/torchvision/extension.py @@ -24,6 +24,8 @@ def _register_extensions(): _register_extensions() _HAS_OPS = True except (ImportError, OSError): + import traceback + traceback.print_exc() pass From 3ed20445a361e1e2c6afcd6d59c208dbecb85c39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Fri, 12 Jun 2020 15:01:50 -0500 Subject: [PATCH 033/129] Link libpng explicitly --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index a3a75aa40d8..dcfbf64f5d1 100644 --- a/setup.py +++ b/setup.py @@ -182,6 +182,7 @@ def get_extensions(): sources, include_dirs=include_dirs, library_dirs=library_dirs, + libraries=['libpng'], define_macros=define_macros, extra_compile_args=extra_compile_args, ) From eaaf6580520693ad30a8f9acb09292f7d81335f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Fri, 12 Jun 2020 15:07:06 -0500 Subject: [PATCH 034/129] Link with PNG --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index dcfbf64f5d1..fcd711e8bf6 100644 --- a/setup.py +++ b/setup.py @@ -182,7 +182,7 @@ def get_extensions(): sources, include_dirs=include_dirs, library_dirs=library_dirs, - libraries=['libpng'], + libraries=['png'], define_macros=define_macros, extra_compile_args=extra_compile_args, ) From c17202e58baa28b42092ee540026c62f7ce08c50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Fri, 12 Jun 2020 18:01:13 -0500 Subject: [PATCH 035/129] Install libpng on conda-based wheel distributions --- packaging/pkg_helpers.bash | 4 ++++ setup.py | 18 +++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/packaging/pkg_helpers.bash b/packaging/pkg_helpers.bash index d8bdfc61e5d..45309e4dd66 100644 --- a/packaging/pkg_helpers.bash +++ b/packaging/pkg_helpers.bash @@ -170,7 +170,11 @@ 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 conda + conda install libpng else + # Install native CentOS libPNG + yum install libpng-devel case "$PYTHON_VERSION" in 2.7) if [[ -n "$UNICODE_ABI" ]]; then diff --git a/setup.py b/setup.py index fcd711e8bf6..3130c83019d 100644 --- a/setup.py +++ b/setup.py @@ -93,13 +93,25 @@ def get_extensions(): print('Running build on conda-build: {0}'.format(is_conda_build)) if is_conda_build: # Add LibPNG headers/libraries - png_include = os.path.join(build_prefix, 'include/libpng16') + png_include = os.path.join(build_prefix, 'include', 'libpng16') print('PNG found? {0}'.format(os.path.isdir(png_include))) - print('Library path: {0}'.format( - glob.glob(os.path.join(build_prefix, 'include/libpng16/*')))) conda_library = os.path.join(build_prefix, 'lib') include_dirs.append(png_include) library_dirs.append(conda_library) + 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 + env_bin_folder = os.path.dirname(python_executable) + env_folder = os.path.dirname(env_bin_folder) + env_lib_folder = os.path.join(env_folder, 'lib') + env_png_folder = os.path.join(env_folder, 'include', 'libpng16') + print('PNG found? {0}'.format(os.path.isdir(env_png_folder))) + include_dirs.append(env_png_folder) + library_dirs.append(env_lib_folder) this_dir = os.path.dirname(os.path.abspath(__file__)) extensions_dir = os.path.join(this_dir, 'torchvision', 'csrc') From 741f85516a153ede1ba5fbf16939f3a308a775f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Fri, 12 Jun 2020 18:03:51 -0500 Subject: [PATCH 036/129] Add -y flag --- packaging/pkg_helpers.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/pkg_helpers.bash b/packaging/pkg_helpers.bash index 45309e4dd66..92fdec2c235 100644 --- a/packaging/pkg_helpers.bash +++ b/packaging/pkg_helpers.bash @@ -171,7 +171,7 @@ setup_wheel_python() { conda create -yn "env$PYTHON_VERSION" python="$PYTHON_VERSION" conda activate "env$PYTHON_VERSION" # Install libPNG from conda - conda install libpng + conda install libpng -y else # Install native CentOS libPNG yum install libpng-devel From a46f503782623fa8e04560a22eca3e5e5279a33c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Fri, 12 Jun 2020 18:08:34 -0500 Subject: [PATCH 037/129] Add -y flag to yum --- packaging/pkg_helpers.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/pkg_helpers.bash b/packaging/pkg_helpers.bash index 92fdec2c235..85ad576dc18 100644 --- a/packaging/pkg_helpers.bash +++ b/packaging/pkg_helpers.bash @@ -174,7 +174,7 @@ setup_wheel_python() { conda install libpng -y else # Install native CentOS libPNG - yum install libpng-devel + yum install -y libpng-devel case "$PYTHON_VERSION" in 2.7) if [[ -n "$UNICODE_ABI" ]]; then From 4bad03358891c8f85d997d91a06964cf7ce4b260 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Fri, 12 Jun 2020 18:37:56 -0500 Subject: [PATCH 038/129] Locate LibPNG on windows conda --- packaging/build_wheel.sh | 15 +++++++++++++++ setup.py | 18 +++++++++++++----- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/packaging/build_wheel.sh b/packaging/build_wheel.sh index d94c0f4c7c8..695255a4c80 100755 --- a/packaging/build_wheel.sh +++ b/packaging/build_wheel.sh @@ -10,6 +10,21 @@ 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/libpng.so" torchvision + else + fi +else + +fi + if [[ "$OSTYPE" == "msys" ]]; then IS_WHEEL=1 "$script_dir/windows/internal/vc_env_helper.bat" python setup.py bdist_wheel else diff --git a/setup.py b/setup.py index 3130c83019d..87f56e9cd17 100644 --- a/setup.py +++ b/setup.py @@ -105,11 +105,19 @@ def get_extensions(): print('Running build on conda: {0}'.format(is_conda)) if is_conda: python_executable = sys.executable - env_bin_folder = os.path.dirname(python_executable) - env_folder = os.path.dirname(env_bin_folder) - env_lib_folder = os.path.join(env_folder, 'lib') - env_png_folder = os.path.join(env_folder, 'include', 'libpng16') - print('PNG found? {0}'.format(os.path.isdir(env_png_folder))) + if os.name == 'nt': + env_folder = os.path.dirname(python_executable) + env_library_path = os.path.join(env_folder, 'Library') + env_include_folder = os.path.join(env_library_path, 'include') + env_png_folder = os.path.join(env_include_folder, 'libpng16') + env_lib_folder = os.path.join(env_library_path, 'lib') + print('PNG found? {0}'.format(os.path.isdir(env_png_folder))) + else: + env_bin_folder = os.path.dirname(python_executable) + env_folder = os.path.dirname(env_bin_folder) + env_lib_folder = os.path.join(env_folder, 'lib') + env_png_folder = os.path.join(env_folder, 'include', 'libpng16') + print('PNG found? {0}'.format(os.path.isdir(env_png_folder))) include_dirs.append(env_png_folder) library_dirs.append(env_lib_folder) From b419fc13f13dd4e18136ef876100dfad81f8bc85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Fri, 12 Jun 2020 18:40:59 -0500 Subject: [PATCH 039/129] Remove empty else --- packaging/build_wheel.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/packaging/build_wheel.sh b/packaging/build_wheel.sh index 695255a4c80..d4ad4eca0a4 100755 --- a/packaging/build_wheel.sh +++ b/packaging/build_wheel.sh @@ -19,10 +19,7 @@ if [[ "$(uname)" == Darwin || "$OSTYPE" == "msys" ]]; then if [[ "$(uname)" == Darwin ]]; then # Include LibPNG cp "$env_path/lib/libpng.so" torchvision - else fi -else - fi if [[ "$OSTYPE" == "msys" ]]; then From 83eff790e6e5c0f54bc01446122d25dd052f75d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Fri, 12 Jun 2020 18:47:37 -0500 Subject: [PATCH 040/129] Copy libpng16.so --- packaging/build_wheel.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/build_wheel.sh b/packaging/build_wheel.sh index d4ad4eca0a4..771074724f2 100755 --- a/packaging/build_wheel.sh +++ b/packaging/build_wheel.sh @@ -18,7 +18,7 @@ if [[ "$(uname)" == Darwin || "$OSTYPE" == "msys" ]]; then env_path=$(dirname $bin_path) if [[ "$(uname)" == Darwin ]]; then # Include LibPNG - cp "$env_path/lib/libpng.so" torchvision + cp "$env_path/lib/libpng16.so" torchvision fi fi From eb2846f0903e11fdd522295ae48e250cb165ce05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Fri, 12 Jun 2020 18:54:36 -0500 Subject: [PATCH 041/129] Copy dylib on Mac --- packaging/build_wheel.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/build_wheel.sh b/packaging/build_wheel.sh index 771074724f2..1d8f9e5b11a 100755 --- a/packaging/build_wheel.sh +++ b/packaging/build_wheel.sh @@ -18,7 +18,7 @@ if [[ "$(uname)" == Darwin || "$OSTYPE" == "msys" ]]; then env_path=$(dirname $bin_path) if [[ "$(uname)" == Darwin ]]; then # Include LibPNG - cp "$env_path/lib/libpng16.so" torchvision + cp "$env_path/lib/libpng16.dylib" torchvision fi fi From 3563ef3c3890122731312b2cbd0f8ad4328065db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Fri, 12 Jun 2020 22:13:05 -0500 Subject: [PATCH 042/129] Improve check on Windows --- packaging/torchvision/meta.yaml | 2 +- setup.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packaging/torchvision/meta.yaml b/packaging/torchvision/meta.yaml index 773307737b1..c7891131da0 100644 --- a/packaging/torchvision/meta.yaml +++ b/packaging/torchvision/meta.yaml @@ -9,7 +9,7 @@ requirements: build: - {{ compiler('c') }} # [win] - libpng - - libjpeg-turbo + # - libjpeg-turbo host: - python diff --git a/setup.py b/setup.py index 87f56e9cd17..66d9f7bb4a2 100644 --- a/setup.py +++ b/setup.py @@ -109,9 +109,10 @@ def get_extensions(): env_folder = os.path.dirname(python_executable) env_library_path = os.path.join(env_folder, 'Library') env_include_folder = os.path.join(env_library_path, 'include') - env_png_folder = os.path.join(env_include_folder, 'libpng16') + env_png_folder = env_include_folder + png_header = os.path.join(env_include_folder, 'png.h') env_lib_folder = os.path.join(env_library_path, 'lib') - print('PNG found? {0}'.format(os.path.isdir(env_png_folder))) + print('PNG found? {0}'.format(os.path.isfile(png_header))) else: env_bin_folder = os.path.dirname(python_executable) env_folder = os.path.dirname(env_bin_folder) From 347383f63b116efea66e485e3f94726b731d05e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 15 Jun 2020 11:00:36 -0500 Subject: [PATCH 043/129] Try to install ninja using conda on windows --- packaging/build_wheel.sh | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packaging/build_wheel.sh b/packaging/build_wheel.sh index 1d8f9e5b11a..b3186fbd63c 100755 --- a/packaging/build_wheel.sh +++ b/packaging/build_wheel.sh @@ -7,7 +7,17 @@ script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" export BUILD_TYPE=wheel setup_env 0.7.0 setup_wheel_python -pip_install numpy pyyaml future ninja + +pip_install numpy pyyaml future + +if [[ "$OSTYPE" == "msys" ]]; then + # Apparently, there are some errors trying to compile ninja locally on + # Windows, better to use conda on this case + conda install ninja +else + pip install ninja +fi + setup_pip_pytorch_version python setup.py clean From 51f6b48d411c24eed8627ff9fb20124d51d2164e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 15 Jun 2020 11:10:40 -0500 Subject: [PATCH 044/129] Use libpng on Windows --- packaging/build_wheel.sh | 4 ++-- setup.py | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packaging/build_wheel.sh b/packaging/build_wheel.sh index b3186fbd63c..1bcd1d802e0 100755 --- a/packaging/build_wheel.sh +++ b/packaging/build_wheel.sh @@ -13,9 +13,9 @@ pip_install numpy pyyaml future if [[ "$OSTYPE" == "msys" ]]; then # Apparently, there are some errors trying to compile ninja locally on # Windows, better to use conda on this case - conda install ninja + conda install ninja -y else - pip install ninja + pip_install ninja fi setup_pip_pytorch_version diff --git a/setup.py b/setup.py index 66d9f7bb4a2..f74ba454aa2 100644 --- a/setup.py +++ b/setup.py @@ -196,6 +196,9 @@ def get_extensions(): sources = [os.path.join(extensions_dir, s) for s in sources] include_dirs += [extensions_dir] + libraries = [] + # Add libPNG + libraries.append('png' if os.name != 'nt' else 'libpng') ext_modules = [ extension( @@ -203,7 +206,7 @@ def get_extensions(): sources, include_dirs=include_dirs, library_dirs=library_dirs, - libraries=['png'], + libraries=libraries, define_macros=define_macros, extra_compile_args=extra_compile_args, ) From ad0044268c7f4e857b4fee04602f7913632a7826 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 15 Jun 2020 11:16:56 -0500 Subject: [PATCH 045/129] Package lib on windows wheel --- packaging/build_wheel.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packaging/build_wheel.sh b/packaging/build_wheel.sh index 1bcd1d802e0..4c320370fe0 100755 --- a/packaging/build_wheel.sh +++ b/packaging/build_wheel.sh @@ -29,6 +29,9 @@ if [[ "$(uname)" == Darwin || "$OSTYPE" == "msys" ]]; then if [[ "$(uname)" == Darwin ]]; then # Include LibPNG cp "$env_path/lib/libpng16.dylib" torchvision + else + # Include libPNG + cp "$env_path/Library/lib/libpng.lib" torchvision fi fi From 4d82283bf96e7b8e90ab1392f87dfd0fccdb86f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 15 Jun 2020 11:25:59 -0500 Subject: [PATCH 046/129] Point library to the correct place --- packaging/build_wheel.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/build_wheel.sh b/packaging/build_wheel.sh index 4c320370fe0..1d0c9f0d595 100755 --- a/packaging/build_wheel.sh +++ b/packaging/build_wheel.sh @@ -31,7 +31,7 @@ if [[ "$(uname)" == Darwin || "$OSTYPE" == "msys" ]]; then cp "$env_path/lib/libpng16.dylib" torchvision else # Include libPNG - cp "$env_path/Library/lib/libpng.lib" torchvision + cp "$bin_path/Library/lib/libpng.lib" torchvision fi fi From 32b2207285f7b05d4262e1c3eeba523b2197b0eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 15 Jun 2020 11:35:18 -0500 Subject: [PATCH 047/129] Include binaries as part of wheel --- setup.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index f74ba454aa2..d871b644699 100644 --- a/setup.py +++ b/setup.py @@ -306,7 +306,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={ From 9a5aefed80c3ad8a73fc0736c279867be1aad9ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 15 Jun 2020 11:46:05 -0500 Subject: [PATCH 048/129] Copy libpng.so on linux --- packaging/build_wheel.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packaging/build_wheel.sh b/packaging/build_wheel.sh index 1d0c9f0d595..12ac781da09 100755 --- a/packaging/build_wheel.sh +++ b/packaging/build_wheel.sh @@ -33,6 +33,8 @@ if [[ "$(uname)" == Darwin || "$OSTYPE" == "msys" ]]; then # Include libPNG cp "$bin_path/Library/lib/libpng.lib" torchvision fi +else + cp "/usr/lib64/libpng.so" torchvision fi if [[ "$OSTYPE" == "msys" ]]; then From a44c3b546162a524e1b36e35e618f7c4b6ff6462 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 15 Jun 2020 12:01:11 -0500 Subject: [PATCH 049/129] Look for png.h on Windows when using conda-build --- setup.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/setup.py b/setup.py index d871b644699..ec54615008a 100644 --- a/setup.py +++ b/setup.py @@ -92,12 +92,21 @@ def get_extensions(): is_conda_build = build_prefix is not None print('Running build on conda-build: {0}'.format(is_conda_build)) if is_conda_build: - # Add LibPNG headers/libraries - png_include = os.path.join(build_prefix, 'include', 'libpng16') - print('PNG found? {0}'.format(os.path.isdir(png_include))) - conda_library = os.path.join(build_prefix, 'lib') - include_dirs.append(png_include) - library_dirs.append(conda_library) + if os.name == 'nt': + lib_path = os.path.join(build_prefix, 'Library') + include_folder = os.path.join(lib_path, 'include') + png_header = os.path.join(include_folder, 'png.h') + lib_folder = os.path.join(lib_path, 'lib') + print('PNG found? {0}'.format(os.path.isfile(png_header))) + include_dirs.append(include_folder) + library_dirs.append(lib_folder) + else: + # Add LibPNG headers/libraries + png_include = os.path.join(build_prefix, 'include', 'libpng16') + print('PNG found? {0}'.format(os.path.isdir(png_include))) + conda_library = os.path.join(build_prefix, 'lib') + include_dirs.append(png_include) + library_dirs.append(conda_library) else: # Check if using Anaconda to produce wheels conda = distutils.spawn.find_executable('conda') From dfcde68c2e7b927d8a30eadf5dfb121bb1c59e24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 15 Jun 2020 12:15:43 -0500 Subject: [PATCH 050/129] Do not skip png tests on Mac/Win --- test/test_image.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/test_image.py b/test/test_image.py index 001a3ab76d3..57fcb5ad803 100644 --- a/test/test_image.py +++ b/test/test_image.py @@ -21,14 +21,12 @@ def get_images(directory, img_ext): class ImageTester(unittest.TestCase): - @unittest.skipUnless(sys.platform.startswith("linux"), "Support only available on linux for now.") def test_read_png(self): for img_path in get_images(IMAGE_DIR, "png"): img_pil = torch.from_numpy(np.array(Image.open(img_path))) img_lpng = read_png(img_path) self.assertEqual(img_lpng, img_pil) - @unittest.skipUnless(sys.platform.startswith("linux"), "Support only available on linux for now.") def test_decode_png(self): for img_path in get_images(IMAGE_DIR, "png"): img_pil = torch.from_numpy(np.array(Image.open(img_path))) From d8d46d69b899bfb506e8cc089cf73ff4418777cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 15 Jun 2020 14:51:19 -0500 Subject: [PATCH 051/129] Restore libjpeg-turbo --- packaging/torchvision/meta.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packaging/torchvision/meta.yaml b/packaging/torchvision/meta.yaml index c7891131da0..b87b63a1bc4 100644 --- a/packaging/torchvision/meta.yaml +++ b/packaging/torchvision/meta.yaml @@ -9,7 +9,7 @@ requirements: build: - {{ compiler('c') }} # [win] - libpng - # - libjpeg-turbo + - libjpeg-turbo host: - python @@ -21,7 +21,7 @@ requirements: run: - python - libpng - # - libjpeg-turbo + - libjpeg-turbo - pillow >=4.1.1 - numpy >=1.11 {{ environ.get('CONDA_PYTORCH_CONSTRAINT') }} From eea8552258b63b8722384024e578c366227ae86c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 15 Jun 2020 15:03:23 -0500 Subject: [PATCH 052/129] Install jpeg-turbo on wheel distributions --- packaging/pkg_helpers.bash | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packaging/pkg_helpers.bash b/packaging/pkg_helpers.bash index 85ad576dc18..18345698f5e 100644 --- a/packaging/pkg_helpers.bash +++ b/packaging/pkg_helpers.bash @@ -170,11 +170,11 @@ 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 conda - conda install libpng -y + # Install libPNG, libJPEG-turbo from conda + conda install libpng libjpeg-turbo -y else - # Install native CentOS libPNG - yum install -y libpng-devel + # Install native CentOS libPNG, libJPEG-turbo + yum install -y libpng-devel libjpeg-turbo case "$PYTHON_VERSION" in 2.7) if [[ -n "$UNICODE_ABI" ]]; then From 8c7dc31a766e717302370ad7afdf2d6ec01c7412 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 15 Jun 2020 15:07:28 -0500 Subject: [PATCH 053/129] Install libjpeg-turbo from conda-forge on wheel distributions --- packaging/pkg_helpers.bash | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packaging/pkg_helpers.bash b/packaging/pkg_helpers.bash index 18345698f5e..1c39af299bc 100644 --- a/packaging/pkg_helpers.bash +++ b/packaging/pkg_helpers.bash @@ -170,8 +170,10 @@ 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, libJPEG-turbo from conda - conda install libpng libjpeg-turbo -y + # 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 libjpeg-turbo From ee8148a2f892a85135fd0cc1453b528722ece87f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 15 Jun 2020 16:42:16 -0500 Subject: [PATCH 054/129] Do not pull av on conda-build --- packaging/torchvision/meta.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packaging/torchvision/meta.yaml b/packaging/torchvision/meta.yaml index b87b63a1bc4..ce56fcef3c5 100644 --- a/packaging/torchvision/meta.yaml +++ b/packaging/torchvision/meta.yaml @@ -48,7 +48,9 @@ test: requires: - pytest - scipy - - av + # AV is disabled temporarily until the jpeg/jpeg-turbo conflicts are solved + # in conda-forge + # - av - ca-certificates {{ environ.get('CONDA_TYPING_CONSTRAINT') }} commands: From 059fa422052f196b6a04e23fdc42038bac65a7b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 15 Jun 2020 16:59:01 -0500 Subject: [PATCH 055/129] Add pillow disclaimer --- packaging/torchvision/meta.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packaging/torchvision/meta.yaml b/packaging/torchvision/meta.yaml index ce56fcef3c5..70fd5aa5a29 100644 --- a/packaging/torchvision/meta.yaml +++ b/packaging/torchvision/meta.yaml @@ -22,6 +22,8 @@ requirements: - 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') }} From 0ed1af68e7f96fc448e4fff3c35209a3e1931261 Mon Sep 17 00:00:00 2001 From: Ryad ZENINE Date: Fri, 14 Feb 2020 07:21:09 +0100 Subject: [PATCH 056/129] Vendors libjpeg-turbo 2.0.4 --- .gitmodules | 3 +++ third_party/libjpeg-turbo | 1 + 2 files changed, 4 insertions(+) create mode 100644 .gitmodules create mode 160000 third_party/libjpeg-turbo diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000000..dac99d261c2 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "third_party/libjpeg-turbo"] + path = third_party/libjpeg-turbo + url = https://github.com/libjpeg-turbo/libjpeg-turbo diff --git a/third_party/libjpeg-turbo b/third_party/libjpeg-turbo new file mode 160000 index 00000000000..166e34213e4 --- /dev/null +++ b/third_party/libjpeg-turbo @@ -0,0 +1 @@ +Subproject commit 166e34213e4f4e2363ce058a7bcc69fd03e38b76 From 11d1a7a181ce31a18226608f9bc4a469fe496af0 Mon Sep 17 00:00:00 2001 From: Ryad ZENINE Date: Sat, 15 Feb 2020 15:43:57 +0100 Subject: [PATCH 057/129] Merge JPEG work --- .circleci/config.yml | 2 +- CMakeLists.txt | 2 + test/test_image.py | 34 ++++++++++++++-- torchvision/csrc/cpu/image/readjpeg_cpu.cpp | 37 +++++++++++++++++ torchvision/csrc/cpu/image/readjpeg_cpu.h | 5 +++ torchvision/csrc/image.h | 1 + torchvision/csrc/vision.cpp | 1 + torchvision/io/image.py | 44 +++++++++++++++++++++ 8 files changed, 122 insertions(+), 4 deletions(-) create mode 100644 torchvision/csrc/cpu/image/readjpeg_cpu.cpp create mode 100644 torchvision/csrc/cpu/image/readjpeg_cpu.h diff --git a/.circleci/config.yml b/.circleci/config.yml index 081337bdcf9..fac5bb866d5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1692,4 +1692,4 @@ workflows: only: /v[0-9]+(\.[0-9]+)*-rc[0-9]+/ name: nightly_binary_win_conda_py3.8_cu102_upload requires: - - nightly_binary_win_conda_py3.8_cu102 \ No newline at end of file + - nightly_binary_win_conda_py3.8_cu102 diff --git a/CMakeLists.txt b/CMakeLists.txt index 753c9f87218..0d7e7aaa9d8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,9 +11,11 @@ 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 IMAGE_HEADERS torchvision/csrc/cpu/image/*.h) file(GLOB IMAGE_SOURCES torchvision/csrc/cpu/image/*.cpp) diff --git a/test/test_image.py b/test/test_image.py index 57fcb5ad803..f2164a7072a 100644 --- a/test/test_image.py +++ b/test/test_image.py @@ -3,9 +3,9 @@ import sys import torch +import torchvision from PIL import Image -if sys.platform.startswith('linux'): - from torchvision.io.image import read_png, decode_png +from torchvision.io.image import read_png, decode_png, read_jpeg, decode_jpeg import numpy as np IMAGE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "assets", "fakedata", "imagefolder") @@ -13,7 +13,7 @@ def get_images(directory, img_ext): assert os.path.isdir(directory) - for root, dir, files in os.walk(directory): + for root, _, files in os.walk(directory): for fl in files: _, ext = os.path.splitext(fl) if ext == img_ext: @@ -21,6 +21,33 @@ def get_images(directory, img_ext): class ImageTester(unittest.TestCase): + def test_read_jpeg(self): + for img_path in get_images(IMAGE_DIR, ".jpg"): + img_pil = torch.from_numpy(np.array(Image.open(img_path))) + img_ljpeg = read_jpeg(img_path) + + err = torch.abs(img_ljpeg.flatten().float() - img_pil.flatten().float()).sum().float() / (img_ljpeg.shape[0] * img_ljpeg.shape[1] * img_ljpeg.shape[2] * 255) + self.assertLessEqual(err, 1e-2) + + def test_decode_jpeg(self): + for img_path in get_images(IMAGE_DIR, ".jpg"): + img_pil = torch.from_numpy(np.array(Image.open(img_path))) + size = os.path.getsize(img_path) + img_ljpeg = decode_jpeg(torch.from_file(img_path, dtype=torch.uint8, size=size)) + + err = torch.abs(img_ljpeg.flatten().float() - img_pil.flatten().float()).sum().float() / (img_ljpeg.shape[0] * img_ljpeg.shape[1] * img_ljpeg.shape[2] * 255) + + self.assertLessEqual(err, 1e-2) + + with self.assertRaisesRegex(ValueError, "Expected a non empty 1-dimensional tensor."): + decode_jpeg(torch.empty((100, 1), dtype=torch.uint8)) + + with self.assertRaisesRegex(ValueError, "Expected a torch.uint8 tensor."): + decode_jpeg(torch.empty((100, ), dtype=torch.float16)) + + with self.assertRaisesRegex(ValueError, "Invalid jpeg input."): + decode_jpeg(torch.empty((100), dtype=torch.uint8)) + def test_read_png(self): for img_path in get_images(IMAGE_DIR, "png"): img_pil = torch.from_numpy(np.array(Image.open(img_path))) @@ -40,3 +67,4 @@ def test_decode_png(self): if __name__ == '__main__': unittest.main() + diff --git a/torchvision/csrc/cpu/image/readjpeg_cpu.cpp b/torchvision/csrc/cpu/image/readjpeg_cpu.cpp new file mode 100644 index 00000000000..ec2a14ee8e6 --- /dev/null +++ b/torchvision/csrc/cpu/image/readjpeg_cpu.cpp @@ -0,0 +1,37 @@ +#include "readjpeg_cpu.h" + +#include +#include +#include + +torch::Tensor decodeJPEG(const torch::Tensor& data) { + + tjhandle tjInstance = tjInitDecompress(); + if (tjInstance == NULL) { + TORCH_CHECK(false, "libjpeg-turbo decompression initialization failed."); + } + + auto datap = data.accessor().data(); + + int width, height; + + if (tjDecompressHeader(tjInstance, datap, data.numel(), &width, &height) < 0) { + tjDestroy(tjInstance); + TORCH_CHECK(false, "Error while reading jpeg headers"); + } + auto tensor = + torch::empty({int64_t(height), int64_t(width), int64_t(3)}, torch::kU8); + + auto ptr = tensor.accessor().data(); + + int pixelFormat = TJPF_RGB; + + auto ret = tjDecompress2(tjInstance, datap, data.numel(), ptr, width, 0, height, + pixelFormat, NULL); + if(ret != 0){ + tjDestroy(tjInstance); + TORCH_CHECK(false, "decompressing JPEG image"); + } + + return tensor; +} diff --git a/torchvision/csrc/cpu/image/readjpeg_cpu.h b/torchvision/csrc/cpu/image/readjpeg_cpu.h new file mode 100644 index 00000000000..40404df29b5 --- /dev/null +++ b/torchvision/csrc/cpu/image/readjpeg_cpu.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +torch::Tensor decodeJPEG(const torch::Tensor& data); diff --git a/torchvision/csrc/image.h b/torchvision/csrc/image.h index 01d2063564d..b3394e36462 100644 --- a/torchvision/csrc/image.h +++ b/torchvision/csrc/image.h @@ -1,3 +1,4 @@ #pragma once #include "cpu/image/readpng_cpu.h" +#include "cpu/image/readjpeg_cpu.h" diff --git a/torchvision/csrc/vision.cpp b/torchvision/csrc/vision.cpp index b0d453c4f33..d8010c827b1 100644 --- a/torchvision/csrc/vision.cpp +++ b/torchvision/csrc/vision.cpp @@ -54,4 +54,5 @@ static auto registry = .op("torchvision::ps_roi_pool", &ps_roi_pool) .op("torchvision::deform_conv2d", &deform_conv2d) .op("torchvision::decode_png", &decodePNG) + .op("torchvision::decode_jpeg", &decodeJPEG) .op("torchvision::_cuda_version", &_cuda_version); diff --git a/torchvision/io/image.py b/torchvision/io/image.py index 74dcad09dbe..d712765ad79 100644 --- a/torchvision/io/image.py +++ b/torchvision/io/image.py @@ -45,3 +45,47 @@ def read_png(path): raise ValueError("Expected a non empty file.") data = torch.from_file(path, dtype=torch.uint8, size=size) return decode_png(data) + + +def decode_jpeg(input): + # type: (Tensor) -> Tensor + """ + Decodes a JPEG image into a 3 dimensional RGB Tensor. + The values of the output tensor are uint8 between 0 and 255. + Arguments: + input (Tensor[1]): a one dimensional int8 tensor containing + the raw bytes of the JPEG image. + Returns: + output (Tensor[image_width, image_height, 3]) + """ + if not isinstance(input, torch.Tensor) or len(input) == 0 or input.ndim != 1: + raise ValueError("Expected a non empty 1-dimensional tensor.") + + if not input.dtype == torch.uint8: + raise ValueError("Expected a torch.uint8 tensor.") + + try: + output = torch.ops.torchvision.decode_jpeg(input) + except RuntimeError: + raise ValueError("Invalid jpeg input.") + return output + + +def read_jpeg(path): + # type: (str) -> Tensor + """ + Reads a JPEG image into a 3 dimensional RGB Tensor. + The values of the output tensor are uint8 between 0 and 255. + Arguments: + path (str): path of the JPEG image. + Returns: + output (Tensor[image_width, image_height, 3]) + """ + if not os.path.isfile(path): + raise ValueError("Expected a valid file path.") + + size = os.path.getsize(path) + if size == 0: + raise ValueError("Expected a non empty file.") + data = torch.from_file(path, dtype=torch.uint8, size=size) + return decode_jpeg(data) From 3bb65ba4b3b79fba5683ee5cfb96b30a1769c774 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 15 Jun 2020 17:25:25 -0500 Subject: [PATCH 058/129] Remove submodules --- .gitmodules | 3 --- third_party/libjpeg-turbo | 1 - 2 files changed, 4 deletions(-) delete mode 100644 .gitmodules delete mode 160000 third_party/libjpeg-turbo diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index dac99d261c2..00000000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "third_party/libjpeg-turbo"] - path = third_party/libjpeg-turbo - url = https://github.com/libjpeg-turbo/libjpeg-turbo diff --git a/third_party/libjpeg-turbo b/third_party/libjpeg-turbo deleted file mode 160000 index 166e34213e4..00000000000 --- a/third_party/libjpeg-turbo +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 166e34213e4f4e2363ce058a7bcc69fd03e38b76 From c334d7e25a122424130b4a1069c200c91940f2a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 15 Jun 2020 17:32:29 -0500 Subject: [PATCH 059/129] Regenerate circle config --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index fac5bb866d5..081337bdcf9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1692,4 +1692,4 @@ workflows: only: /v[0-9]+(\.[0-9]+)*-rc[0-9]+/ name: nightly_binary_win_conda_py3.8_cu102_upload requires: - - nightly_binary_win_conda_py3.8_cu102 + - nightly_binary_win_conda_py3.8_cu102 \ No newline at end of file From 5650e5992c094d7f055ea5d1b04eebe7a5bbbe67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 15 Jun 2020 17:36:01 -0500 Subject: [PATCH 060/129] Fix style issues --- test/test_image.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/test_image.py b/test/test_image.py index f2164a7072a..2eaed2f04eb 100644 --- a/test/test_image.py +++ b/test/test_image.py @@ -26,7 +26,8 @@ def test_read_jpeg(self): img_pil = torch.from_numpy(np.array(Image.open(img_path))) img_ljpeg = read_jpeg(img_path) - err = torch.abs(img_ljpeg.flatten().float() - img_pil.flatten().float()).sum().float() / (img_ljpeg.shape[0] * img_ljpeg.shape[1] * img_ljpeg.shape[2] * 255) + norm = img_ljpeg.shape[0] * img_ljpeg.shape[1] * img_ljpeg.shape[2] * 255 + err = torch.abs(img_ljpeg.flatten().float() - img_pil.flatten().float()).sum().float() / (norm) self.assertLessEqual(err, 1e-2) def test_decode_jpeg(self): @@ -35,7 +36,8 @@ def test_decode_jpeg(self): size = os.path.getsize(img_path) img_ljpeg = decode_jpeg(torch.from_file(img_path, dtype=torch.uint8, size=size)) - err = torch.abs(img_ljpeg.flatten().float() - img_pil.flatten().float()).sum().float() / (img_ljpeg.shape[0] * img_ljpeg.shape[1] * img_ljpeg.shape[2] * 255) + norm = img_ljpeg.shape[0] * img_ljpeg.shape[1] * img_ljpeg.shape[2] * 255 + err = torch.abs(img_ljpeg.flatten().float() - img_pil.flatten().float()).sum().float() / (norm) self.assertLessEqual(err, 1e-2) @@ -67,4 +69,3 @@ def test_decode_png(self): if __name__ == '__main__': unittest.main() - From 789483603c30fd426c5d29fc38321c53b9bb07d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 15 Jun 2020 17:38:56 -0500 Subject: [PATCH 061/129] Fix C++ style issues --- torchvision/csrc/cpu/image/readjpeg_cpu.cpp | 17 +++++++++++++---- torchvision/csrc/image.h | 2 +- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/torchvision/csrc/cpu/image/readjpeg_cpu.cpp b/torchvision/csrc/cpu/image/readjpeg_cpu.cpp index ec2a14ee8e6..3890ff7c96c 100644 --- a/torchvision/csrc/cpu/image/readjpeg_cpu.cpp +++ b/torchvision/csrc/cpu/image/readjpeg_cpu.cpp @@ -1,8 +1,8 @@ #include "readjpeg_cpu.h" #include -#include #include +#include torch::Tensor decodeJPEG(const torch::Tensor& data) { @@ -15,7 +15,8 @@ torch::Tensor decodeJPEG(const torch::Tensor& data) { int width, height; - if (tjDecompressHeader(tjInstance, datap, data.numel(), &width, &height) < 0) { + if (tjDecompressHeader(tjInstance, datap, data.numel(), &width, &height) < + 0) { tjDestroy(tjInstance); TORCH_CHECK(false, "Error while reading jpeg headers"); } @@ -26,8 +27,16 @@ torch::Tensor decodeJPEG(const torch::Tensor& data) { int pixelFormat = TJPF_RGB; - auto ret = tjDecompress2(tjInstance, datap, data.numel(), ptr, width, 0, height, - pixelFormat, NULL); + auto ret = tjDecompress2( + tjInstance, + datap, + data.numel(), + ptr, + width, + 0, + height, + pixelFormat, + NULL); if(ret != 0){ tjDestroy(tjInstance); TORCH_CHECK(false, "decompressing JPEG image"); diff --git a/torchvision/csrc/image.h b/torchvision/csrc/image.h index b3394e36462..be8eecf76e8 100644 --- a/torchvision/csrc/image.h +++ b/torchvision/csrc/image.h @@ -1,4 +1,4 @@ #pragma once -#include "cpu/image/readpng_cpu.h" #include "cpu/image/readjpeg_cpu.h" +#include "cpu/image/readpng_cpu.h" From 2da51d489f4040522206491955630e46035ccdf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 15 Jun 2020 17:40:18 -0500 Subject: [PATCH 062/129] More style corrections --- torchvision/csrc/cpu/image/readjpeg_cpu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/torchvision/csrc/cpu/image/readjpeg_cpu.cpp b/torchvision/csrc/cpu/image/readjpeg_cpu.cpp index 3890ff7c96c..4a6032385b2 100644 --- a/torchvision/csrc/cpu/image/readjpeg_cpu.cpp +++ b/torchvision/csrc/cpu/image/readjpeg_cpu.cpp @@ -37,7 +37,7 @@ torch::Tensor decodeJPEG(const torch::Tensor& data) { height, pixelFormat, NULL); - if(ret != 0){ + if (ret != 0) { tjDestroy(tjInstance); TORCH_CHECK(false, "decompressing JPEG image"); } From 78455ae148b31ea65e9d0c4e31d70e92875f8e04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 15 Jun 2020 17:44:55 -0500 Subject: [PATCH 063/129] Add JPEG-turbo to linking libraries --- setup.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/setup.py b/setup.py index ec54615008a..fdfa36b139c 100644 --- a/setup.py +++ b/setup.py @@ -101,11 +101,12 @@ def get_extensions(): include_dirs.append(include_folder) library_dirs.append(lib_folder) else: - # Add LibPNG headers/libraries - png_include = os.path.join(build_prefix, 'include', 'libpng16') - print('PNG found? {0}'.format(os.path.isdir(png_include))) + # Add conda headers/libraries + conda_include = os.path.join(build_prefix, 'include') + png_header = os.path.join(conda_include, 'png.h') + print('PNG found? {0}'.format(os.path.isfile(png_header))) conda_library = os.path.join(build_prefix, 'lib') - include_dirs.append(png_include) + include_dirs.append(conda_include) library_dirs.append(conda_library) else: # Check if using Anaconda to produce wheels @@ -118,17 +119,17 @@ def get_extensions(): env_folder = os.path.dirname(python_executable) env_library_path = os.path.join(env_folder, 'Library') env_include_folder = os.path.join(env_library_path, 'include') - env_png_folder = env_include_folder - png_header = os.path.join(env_include_folder, 'png.h') env_lib_folder = os.path.join(env_library_path, 'lib') + png_header = os.path.join(env_include_folder, 'png.h') print('PNG found? {0}'.format(os.path.isfile(png_header))) else: env_bin_folder = os.path.dirname(python_executable) env_folder = os.path.dirname(env_bin_folder) env_lib_folder = os.path.join(env_folder, 'lib') - env_png_folder = os.path.join(env_folder, 'include', 'libpng16') - print('PNG found? {0}'.format(os.path.isdir(env_png_folder))) - include_dirs.append(env_png_folder) + env_include_folder = os.path.join(env_folder, 'include') + png_header = os.path.join(env_include_folder, 'png.h') + print('PNG found? {0}'.format(os.path.isfile(png_header))) + include_dirs.append(env_include_folder) library_dirs.append(env_lib_folder) this_dir = os.path.dirname(os.path.abspath(__file__)) @@ -208,6 +209,8 @@ def get_extensions(): libraries = [] # Add libPNG libraries.append('png' if os.name != 'nt' else 'libpng') + # Add libJPEG-turbo + libraries.append('turbojpeg' if os.name != 'nt' else 'libturbojpeg') ext_modules = [ extension( From a0a383d96499b519db6cee60d4b5ee628b6e444b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 15 Jun 2020 17:47:41 -0500 Subject: [PATCH 064/129] More style corrections --- torchvision/csrc/cpu/image/readjpeg_cpu.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/torchvision/csrc/cpu/image/readjpeg_cpu.cpp b/torchvision/csrc/cpu/image/readjpeg_cpu.cpp index 4a6032385b2..9d191e3029d 100644 --- a/torchvision/csrc/cpu/image/readjpeg_cpu.cpp +++ b/torchvision/csrc/cpu/image/readjpeg_cpu.cpp @@ -38,8 +38,8 @@ torch::Tensor decodeJPEG(const torch::Tensor& data) { pixelFormat, NULL); if (ret != 0) { - tjDestroy(tjInstance); - TORCH_CHECK(false, "decompressing JPEG image"); + tjDestroy(tjInstance); + TORCH_CHECK(false, "decompressing JPEG image"); } return tensor; From f143e2c580fff1461769292d6fa9627dd79cb873 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 15 Jun 2020 17:49:18 -0500 Subject: [PATCH 065/129] More style corrections --- torchvision/csrc/cpu/image/readjpeg_cpu.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/torchvision/csrc/cpu/image/readjpeg_cpu.cpp b/torchvision/csrc/cpu/image/readjpeg_cpu.cpp index 9d191e3029d..101f169f080 100644 --- a/torchvision/csrc/cpu/image/readjpeg_cpu.cpp +++ b/torchvision/csrc/cpu/image/readjpeg_cpu.cpp @@ -28,15 +28,15 @@ torch::Tensor decodeJPEG(const torch::Tensor& data) { int pixelFormat = TJPF_RGB; auto ret = tjDecompress2( - tjInstance, - datap, - data.numel(), - ptr, - width, - 0, - height, - pixelFormat, - NULL); + tjInstance, + datap, + data.numel(), + ptr, + width, + 0, + height, + pixelFormat, + NULL); if (ret != 0) { tjDestroy(tjInstance); TORCH_CHECK(false, "decompressing JPEG image"); From 95cc9416a7955a2b4b88a49bcc567f1e33ea8776 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 15 Jun 2020 17:51:25 -0500 Subject: [PATCH 066/129] More style corrections --- torchvision/csrc/cpu/image/readjpeg_cpu.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/torchvision/csrc/cpu/image/readjpeg_cpu.cpp b/torchvision/csrc/cpu/image/readjpeg_cpu.cpp index 101f169f080..188da8c74a4 100644 --- a/torchvision/csrc/cpu/image/readjpeg_cpu.cpp +++ b/torchvision/csrc/cpu/image/readjpeg_cpu.cpp @@ -5,7 +5,6 @@ #include torch::Tensor decodeJPEG(const torch::Tensor& data) { - tjhandle tjInstance = tjInitDecompress(); if (tjInstance == NULL) { TORCH_CHECK(false, "libjpeg-turbo decompression initialization failed."); From 1c9270a42b7ebfa0f3115b3d3555864d49910dc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 15 Jun 2020 18:00:12 -0500 Subject: [PATCH 067/129] Install libjpeg-turbo-devel --- packaging/pkg_helpers.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/pkg_helpers.bash b/packaging/pkg_helpers.bash index 1c39af299bc..c3642aaaa3d 100644 --- a/packaging/pkg_helpers.bash +++ b/packaging/pkg_helpers.bash @@ -176,7 +176,7 @@ setup_wheel_python() { conda install -y libjpeg-turbo -c conda-forge else # Install native CentOS libPNG, libJPEG-turbo - yum install -y libpng-devel libjpeg-turbo + yum install -y libpng-devel libjpeg-turbo-devel case "$PYTHON_VERSION" in 2.7) if [[ -n "$UNICODE_ABI" ]]; then From ee388e5e9408197fd55bc280c2be25520469eab0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 15 Jun 2020 18:12:22 -0500 Subject: [PATCH 068/129] Install libturbo-jpeg on typing pipeline --- .circleci/config.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 081337bdcf9..880b417bbf9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -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 . From 462ed6cfce88cbe7518021633a8b778daed36356 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 15 Jun 2020 18:13:51 -0500 Subject: [PATCH 069/129] Update Circle template --- .circleci/config.yml.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.circleci/config.yml.in b/.circleci/config.yml.in index 620e00807d4..3249d67063c 100644 --- a/.circleci/config.yml.in +++ b/.circleci/config.yml.in @@ -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 . From 989db57ba87952b5b91d09a6c0417e9ddcea2417 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 15 Jun 2020 18:25:58 -0500 Subject: [PATCH 070/129] Windows and Unix turbojpeg have the same linking name --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index fdfa36b139c..d33172203b8 100644 --- a/setup.py +++ b/setup.py @@ -210,7 +210,7 @@ def get_extensions(): # Add libPNG libraries.append('png' if os.name != 'nt' else 'libpng') # Add libJPEG-turbo - libraries.append('turbojpeg' if os.name != 'nt' else 'libturbojpeg') + libraries.append('turbojpeg') ext_modules = [ extension( From 6e9ad0efe26019aa36ec370e21d8563388e12a07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 15 Jun 2020 18:44:49 -0500 Subject: [PATCH 071/129] Install turbojpeg-devel instead of libjpeg-turbo --- packaging/pkg_helpers.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/pkg_helpers.bash b/packaging/pkg_helpers.bash index c3642aaaa3d..ead4ca280e1 100644 --- a/packaging/pkg_helpers.bash +++ b/packaging/pkg_helpers.bash @@ -176,7 +176,7 @@ setup_wheel_python() { conda install -y libjpeg-turbo -c conda-forge else # Install native CentOS libPNG, libJPEG-turbo - yum install -y libpng-devel libjpeg-turbo-devel + yum install -y libpng-devel turbojpeg-devel case "$PYTHON_VERSION" in 2.7) if [[ -n "$UNICODE_ABI" ]]; then From dd43bcd4d174b49d49cfcc5d589e85e556d73f18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 15 Jun 2020 18:49:09 -0500 Subject: [PATCH 072/129] Copy TurboJPEG binaries to wheel --- packaging/build_wheel.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packaging/build_wheel.sh b/packaging/build_wheel.sh index 12ac781da09..3ae14f767e2 100755 --- a/packaging/build_wheel.sh +++ b/packaging/build_wheel.sh @@ -29,12 +29,19 @@ if [[ "$(uname)" == Darwin || "$OSTYPE" == "msys" ]]; then 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 From e542e48d71315ee18ee7a47ade614a8017a1673a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 15 Jun 2020 19:05:05 -0500 Subject: [PATCH 073/129] Move test image --- .../imagefolder}/grace_hopper_517x606.jpg | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename test/assets/{ => fakedata/imagefolder}/grace_hopper_517x606.jpg (100%) diff --git a/test/assets/grace_hopper_517x606.jpg b/test/assets/fakedata/imagefolder/grace_hopper_517x606.jpg similarity index 100% rename from test/assets/grace_hopper_517x606.jpg rename to test/assets/fakedata/imagefolder/grace_hopper_517x606.jpg From b5fa45e1b54317ff169ad9fb39aff02875a1294a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 15 Jun 2020 19:17:05 -0500 Subject: [PATCH 074/129] Move back test image --- .../imagefolder => }/grace_hopper_517x606.jpg | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename test/assets/{fakedata/imagefolder => }/grace_hopper_517x606.jpg (100%) diff --git a/test/assets/fakedata/imagefolder/grace_hopper_517x606.jpg b/test/assets/grace_hopper_517x606.jpg similarity index 100% rename from test/assets/fakedata/imagefolder/grace_hopper_517x606.jpg rename to test/assets/grace_hopper_517x606.jpg From 3e905565e9466e9579320f2999f908a67b2095de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 15 Jun 2020 19:18:41 -0500 Subject: [PATCH 075/129] Update JPEG test path --- test/test_image.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/test_image.py b/test/test_image.py index 2eaed2f04eb..8101f772ed6 100644 --- a/test/test_image.py +++ b/test/test_image.py @@ -8,7 +8,8 @@ from torchvision.io.image import read_png, decode_png, read_jpeg, decode_jpeg import numpy as np -IMAGE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "assets", "fakedata", "imagefolder") +IMAGE_ROOT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "assets") +IMAGE_DIR = os.path.join(IMAGE_ROOT, "fakedata", "imagefolder") def get_images(directory, img_ext): @@ -22,7 +23,7 @@ def get_images(directory, img_ext): class ImageTester(unittest.TestCase): def test_read_jpeg(self): - for img_path in get_images(IMAGE_DIR, ".jpg"): + for img_path in get_images(IMAGE_ROOT, ".jpg"): img_pil = torch.from_numpy(np.array(Image.open(img_path))) img_ljpeg = read_jpeg(img_path) @@ -31,7 +32,7 @@ def test_read_jpeg(self): self.assertLessEqual(err, 1e-2) def test_decode_jpeg(self): - for img_path in get_images(IMAGE_DIR, ".jpg"): + for img_path in get_images(IMAGE_ROOT, ".jpg"): img_pil = torch.from_numpy(np.array(Image.open(img_path))) size = os.path.getsize(img_path) img_ljpeg = decode_jpeg(torch.from_file(img_path, dtype=torch.uint8, size=size)) From 4e09af0e2bf1cf45bca91cd38848248f74011323 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 15 Jun 2020 19:21:20 -0500 Subject: [PATCH 076/129] Remove dot from extension --- test/test_image.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_image.py b/test/test_image.py index 8101f772ed6..866c522d8ec 100644 --- a/test/test_image.py +++ b/test/test_image.py @@ -23,7 +23,7 @@ def get_images(directory, img_ext): class ImageTester(unittest.TestCase): def test_read_jpeg(self): - for img_path in get_images(IMAGE_ROOT, ".jpg"): + for img_path in get_images(IMAGE_ROOT, "jpg"): img_pil = torch.from_numpy(np.array(Image.open(img_path))) img_ljpeg = read_jpeg(img_path) @@ -32,7 +32,7 @@ def test_read_jpeg(self): self.assertLessEqual(err, 1e-2) def test_decode_jpeg(self): - for img_path in get_images(IMAGE_ROOT, ".jpg"): + for img_path in get_images(IMAGE_ROOT, "jpg"): img_pil = torch.from_numpy(np.array(Image.open(img_path))) size = os.path.getsize(img_path) img_ljpeg = decode_jpeg(torch.from_file(img_path, dtype=torch.uint8, size=size)) From 8ac335e28889749043cf648107dec3d37770c68b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 18 Jun 2020 18:36:51 -0500 Subject: [PATCH 077/129] Move image functions to extension --- setup.py | 183 ++++++++++++-------- torchvision/csrc/cpu/image/image.cpp | 27 +++ torchvision/csrc/cpu/image/image.h | 7 + torchvision/csrc/cpu/image/readjpeg_cpu.cpp | 10 ++ torchvision/csrc/cpu/image/readpng_cpu.cpp | 7 + torchvision/csrc/image.h | 4 - torchvision/csrc/vision.cpp | 3 - torchvision/io/image.py | 25 ++- 8 files changed, 185 insertions(+), 81 deletions(-) create mode 100644 torchvision/csrc/cpu/image/image.cpp create mode 100644 torchvision/csrc/cpu/image/image.h delete mode 100644 torchvision/csrc/image.h diff --git a/setup.py b/setup.py index d33172203b8..e2e0c7b806a 100644 --- a/setup.py +++ b/setup.py @@ -77,67 +77,20 @@ def write_version_file(): def get_extensions(): - try: - include_dirs = [os.environ['LIBRARY_INC']] - except KeyError: - include_dirs = [] - - try: - library_dirs = [os.environ['LIBRARY_LIB']] - except KeyError: - library_dirs = [] - - # Check if building on Conda - build_prefix = os.environ.get('BUILD_PREFIX', None) - is_conda_build = build_prefix is not None - print('Running build on conda-build: {0}'.format(is_conda_build)) - if is_conda_build: - if os.name == 'nt': - lib_path = os.path.join(build_prefix, 'Library') - include_folder = os.path.join(lib_path, 'include') - png_header = os.path.join(include_folder, 'png.h') - lib_folder = os.path.join(lib_path, 'lib') - print('PNG found? {0}'.format(os.path.isfile(png_header))) - include_dirs.append(include_folder) - library_dirs.append(lib_folder) - else: - # Add conda headers/libraries - conda_include = os.path.join(build_prefix, 'include') - png_header = os.path.join(conda_include, 'png.h') - print('PNG found? {0}'.format(os.path.isfile(png_header))) - conda_library = os.path.join(build_prefix, 'lib') - include_dirs.append(conda_include) - library_dirs.append(conda_library) - 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 - if os.name == 'nt': - env_folder = os.path.dirname(python_executable) - env_library_path = os.path.join(env_folder, 'Library') - env_include_folder = os.path.join(env_library_path, 'include') - env_lib_folder = os.path.join(env_library_path, 'lib') - png_header = os.path.join(env_include_folder, 'png.h') - print('PNG found? {0}'.format(os.path.isfile(png_header))) - else: - env_bin_folder = os.path.dirname(python_executable) - env_folder = os.path.dirname(env_bin_folder) - env_lib_folder = os.path.join(env_folder, 'lib') - env_include_folder = os.path.join(env_folder, 'include') - png_header = os.path.join(env_include_folder, 'png.h') - print('PNG found? {0}'.format(os.path.isfile(png_header))) - include_dirs.append(env_include_folder) - library_dirs.append(env_lib_folder) + 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') main_file = glob.glob(os.path.join(extensions_dir, '*.cpp')) source_cpu = glob.glob(os.path.join(extensions_dir, 'cpu', '*.cpp')) - image_src = glob.glob(os.path.join(extensions_dir, 'cpu', 'image', '*.cpp')) is_rocm_pytorch = False if torch.__version__ >= '1.5': @@ -160,7 +113,7 @@ def get_extensions(): else: source_cuda = glob.glob(os.path.join(extensions_dir, 'cuda', '*.cu')) - sources = main_file + source_cpu + image_src + sources = main_file + source_cpu extension = CppExtension compile_cpp_tests = os.getenv('WITH_CPP_MODELS_TEST', '0') == '1' @@ -206,11 +159,6 @@ def get_extensions(): sources = [os.path.join(extensions_dir, s) for s in sources] include_dirs += [extensions_dir] - libraries = [] - # Add libPNG - libraries.append('png' if os.name != 'nt' else 'libpng') - # Add libJPEG-turbo - libraries.append('turbojpeg') ext_modules = [ extension( @@ -218,7 +166,6 @@ def get_extensions(): sources, include_dirs=include_dirs, library_dirs=library_dirs, - libraries=libraries, define_macros=define_macros, extra_compile_args=extra_compile_args, ) @@ -235,16 +182,108 @@ def get_extensions(): ) # Image reading extension - # image_src_dir = os.path.join(this_dir, 'torchvision', 'csrc', 'cpu', 'image') - # image_src = glob.glob(os.path.join(image_src_dir, "*.cpp")) - # ext_modules.append(extension( - # 'torchvision.image', - # image_src, - # include_dirs=include_dirs + [image_src_dir], - # library_dirs=library_dirs, - # define_macros=define_macros, - # extra_compile_args=extra_compile_args - # )) + 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: + print('Building torchvision with PNG image support') + png_lib = subprocess.run([libpng, '--libdir'], capture_output=True) + png_include = subprocess.run([libpng, '--I_opts'], capture_output=True) + image_library += [png_lib] + image_include += [png_include] + image_link_flags.append('png' if os.name != 'nt' else 'libpng') + + # Locating libjpegturbo + build_prefix = os.environ.get('BUILD_PREFIX', None) + is_conda_build = build_prefix is not None + turbojpeg_found = False + conda_installed = False + turbojpeg_header = 'turbojpeg.h' + print('Running build on conda-build: {0}'.format(is_conda_build)) + if is_conda_build: + if os.name == 'nt': + lib_path = os.path.join(build_prefix, 'Library') + turbo_include_folder = os.path.join(lib_path, 'include') + turbojpeg_header = os.path.join( + turbo_include_folder, 'turbojpeg.h') + turbo_lib_folder = os.path.join(lib_path, 'lib') + turbojpeg_found = os.path.isfile(turbojpeg_header) + else: + # Add conda headers/libraries + turbo_include_folder = os.path.join(build_prefix, 'include') + turbo_lib_folder = os.path.join(build_prefix, 'lib') + turbojpeg_header = os.path.join( + turbo_include_folder, 'turbojpeg.h') + turbojpeg_found = os.path.isfile(turbojpeg_header) + conda_installed = turbojpeg_header + 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 + if os.name == 'nt': + env_folder = os.path.dirname(python_executable) + env_library_path = os.path.join(env_folder, 'Library') + turbo_include_folder = os.path.join( + env_library_path, 'include') + turbojpeg_header = os.path.join( + turbo_include_folder, 'turbojpeg.h') + turbo_lib_folder = os.path.join(env_library_path, 'lib') + turbojpeg_found = os.path.isfile(turbojpeg_header) + else: + env_bin_folder = os.path.dirname(python_executable) + env_folder = os.path.dirname(env_bin_folder) + turbo_lib_folder = os.path.join(env_folder, 'lib') + turbo_include_folder = os.path.join(env_folder, 'include') + turbojpeg_header = os.path.join( + turbo_include_folder, 'turbojpeg.h') + turbojpeg_found = os.path.isfile(turbojpeg_header) + conda_installed = turbojpeg_header + + # Try to locate turbojpeg in Linux standard paths + if not turbojpeg_found: + if sys.platform == 'linux': + turbojpeg_found = os.path.exists('/usr/include/turbojpeg.h') + turbojpeg_found = turbojpeg_found or os.path.exists( + '/usr/local/include/turbojpeg.h') + 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, 'turbojpeg.h') + turbojpeg_found = os.path.exists(candidate_path) + if turbojpeg_found: + break + + 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_src = glob.glob( + os.path.join(extensions_dir, 'cpu', 'image', '*.cpp')) + + ext_modules.append(extension( + 'torchvision.image', + image_src, + include_dirs=include_dirs + [image_src] + image_include, + library_dirs=library_dirs + image_library, + define_macros=image_macros, + extra_compile_args=extra_compile_args + )) ffmpeg_exe = distutils.spawn.find_executable('ffmpeg') has_ffmpeg = ffmpeg_exe is not None diff --git a/torchvision/csrc/cpu/image/image.cpp b/torchvision/csrc/cpu/image/image.cpp new file mode 100644 index 00000000000..9a760c01449 --- /dev/null +++ b/torchvision/csrc/cpu/image/image.cpp @@ -0,0 +1,27 @@ + +#include "image.h" +#include +#include + + +// If we are in a Windows environment, we need to define +// initialization functions for the _custom_ops extension +#ifdef _WIN32 +#if PY_MAJOR_VERSION < 3 +PyMODINIT_FUNC init_image(void) { + // No need to do anything. + return NULL; +} +#else +PyMODINIT_FUNC PyInit_image(void) { + // No need to do anything. + return NULL; +} +#endif +#endif + +static auto registry = torch::RegisterOperators() + .op("image::decode_png", + &decodePNG) + .op("image::decode_jpeg", + &decodeJPEG); diff --git a/torchvision/csrc/cpu/image/image.h b/torchvision/csrc/cpu/image/image.h new file mode 100644 index 00000000000..5278b970264 --- /dev/null +++ b/torchvision/csrc/cpu/image/image.h @@ -0,0 +1,7 @@ + +#pragma once + +#include "readjpeg_cpu.h" +#include "readpng_cpu.h" +#include + diff --git a/torchvision/csrc/cpu/image/readjpeg_cpu.cpp b/torchvision/csrc/cpu/image/readjpeg_cpu.cpp index 188da8c74a4..6a5785ed6c3 100644 --- a/torchvision/csrc/cpu/image/readjpeg_cpu.cpp +++ b/torchvision/csrc/cpu/image/readjpeg_cpu.cpp @@ -1,9 +1,17 @@ #include "readjpeg_cpu.h" +#include #include #include #include +#if !JPEG_FOUND + +torch::Tensor decodeJPEG(const torch::Tensor& data) { + AT_ERROR("decodeJPEG: torchvision not compiled with turboJPEG support"); +} + +#else torch::Tensor decodeJPEG(const torch::Tensor& data) { tjhandle tjInstance = tjInitDecompress(); if (tjInstance == NULL) { @@ -43,3 +51,5 @@ torch::Tensor decodeJPEG(const torch::Tensor& data) { return tensor; } + +#endif // JPEG_FOUND diff --git a/torchvision/csrc/cpu/image/readpng_cpu.cpp b/torchvision/csrc/cpu/image/readpng_cpu.cpp index c6581f168b1..5e4e3ffe17f 100644 --- a/torchvision/csrc/cpu/image/readpng_cpu.cpp +++ b/torchvision/csrc/cpu/image/readpng_cpu.cpp @@ -1,9 +1,15 @@ #include "readpng_cpu.h" +#include #include #include #include +#if !PNG_FOUND +torch::Tensor decodePNG(const torch::Tensor& data) { + AT_ERROR("decodePNG: torchvision not compiled with libPNG support"); +} +#else torch::Tensor decodePNG(const torch::Tensor& data) { auto png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); @@ -73,3 +79,4 @@ torch::Tensor decodePNG(const torch::Tensor& data) { png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); return tensor; } +#endif // PNG_FOUND diff --git a/torchvision/csrc/image.h b/torchvision/csrc/image.h deleted file mode 100644 index be8eecf76e8..00000000000 --- a/torchvision/csrc/image.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -#include "cpu/image/readjpeg_cpu.h" -#include "cpu/image/readpng_cpu.h" diff --git a/torchvision/csrc/vision.cpp b/torchvision/csrc/vision.cpp index d8010c827b1..9debc3da9b6 100644 --- a/torchvision/csrc/vision.cpp +++ b/torchvision/csrc/vision.cpp @@ -14,7 +14,6 @@ #include "ROIAlign.h" #include "ROIPool.h" #include "empty_tensor_op.h" -#include "image.h" #include "nms.h" // If we are in a Windows environment, we need to define @@ -53,6 +52,4 @@ static auto registry = .op("torchvision::ps_roi_align", &ps_roi_align) .op("torchvision::ps_roi_pool", &ps_roi_pool) .op("torchvision::deform_conv2d", &deform_conv2d) - .op("torchvision::decode_png", &decodePNG) - .op("torchvision::decode_jpeg", &decodeJPEG) .op("torchvision::_cuda_version", &_cuda_version); diff --git a/torchvision/io/image.py b/torchvision/io/image.py index d712765ad79..a95aedaf080 100644 --- a/torchvision/io/image.py +++ b/torchvision/io/image.py @@ -1,6 +1,27 @@ import torch from torch import nn, Tensor + import os +import os.path as osp +import importlib + +_HAS_IMAGE_OPT = False + +try: + lib_dir = osp.join(osp.dirname(__file__), "..") + + loader_details = ( + importlib.machinery.ExtensionFileLoader, + importlib.machinery.EXTENSION_SUFFIXES + ) + + extfinder = importlib.machinery.FileFinder(lib_dir, loader_details) + ext_specs = extfinder.find_spec("image") + if ext_specs is not None: + torch.ops.load_library(ext_specs.origin) + _HAS_IMAGE_OPT = True +except (ImportError, OSError): + pass def decode_png(input): @@ -21,7 +42,7 @@ def decode_png(input): if not input.dtype == torch.uint8: raise ValueError("Expected a torch.uint8 tensor.") - output = torch.ops.torchvision.decode_png(input) + output = torch.ops.image.decode_png(input) return output @@ -65,7 +86,7 @@ def decode_jpeg(input): raise ValueError("Expected a torch.uint8 tensor.") try: - output = torch.ops.torchvision.decode_jpeg(input) + output = torch.ops.image.decode_jpeg(input) except RuntimeError: raise ValueError("Invalid jpeg input.") return output From 2adb87e15c5fab55a4f4eed5b91fa1976910ea92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 18 Jun 2020 18:40:00 -0500 Subject: [PATCH 078/129] Use stdout arg in subprocess --- setup.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index e2e0c7b806a..4f05cae2718 100644 --- a/setup.py +++ b/setup.py @@ -194,8 +194,9 @@ def get_extensions(): print('PNG found: {0}'.format(png_found)) if png_found: print('Building torchvision with PNG image support') - png_lib = subprocess.run([libpng, '--libdir'], capture_output=True) - png_include = subprocess.run([libpng, '--I_opts'], capture_output=True) + png_lib = subprocess.run([libpng, '--libdir'], stdout=subprocess.PIPE) + png_include = subprocess.run([libpng, '--I_opts'], + stdout=subprocess.PIPE) image_library += [png_lib] image_include += [png_include] image_link_flags.append('png' if os.name != 'nt' else 'libpng') From 44826a79010b7585603a86bd35364a0041a637e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 18 Jun 2020 18:45:17 -0500 Subject: [PATCH 079/129] Disable image extension if libpng or turbojpeg are not found --- setup.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/setup.py b/setup.py index 4f05cae2718..e9124d37af3 100644 --- a/setup.py +++ b/setup.py @@ -277,14 +277,15 @@ def get_extensions(): image_src = glob.glob( os.path.join(extensions_dir, 'cpu', 'image', '*.cpp')) - ext_modules.append(extension( - 'torchvision.image', - image_src, - include_dirs=include_dirs + [image_src] + image_include, - library_dirs=library_dirs + image_library, - define_macros=image_macros, - extra_compile_args=extra_compile_args - )) + if png_found or turbojpeg_found: + ext_modules.append(extension( + 'torchvision.image', + image_src, + include_dirs=include_dirs + [image_src] + image_include, + library_dirs=library_dirs + image_library, + define_macros=image_macros, + extra_compile_args=extra_compile_args + )) ffmpeg_exe = distutils.spawn.find_executable('ffmpeg') has_ffmpeg = ffmpeg_exe is not None From 1e830eadd2b7da8692a3990168f30e4c49b7f477 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 18 Jun 2020 18:46:23 -0500 Subject: [PATCH 080/129] Append libpng stdout --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index e9124d37af3..27735abe2e6 100644 --- a/setup.py +++ b/setup.py @@ -197,8 +197,8 @@ def get_extensions(): png_lib = subprocess.run([libpng, '--libdir'], stdout=subprocess.PIPE) png_include = subprocess.run([libpng, '--I_opts'], stdout=subprocess.PIPE) - image_library += [png_lib] - image_include += [png_include] + image_library += [png_lib.stdout] + image_include += [png_include.stdout] image_link_flags.append('png' if os.name != 'nt' else 'libpng') # Locating libjpegturbo From 37c889bde6a401e85d5b3ddcc9beddf3abc0c644 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 18 Jun 2020 18:52:20 -0500 Subject: [PATCH 081/129] Prevent list appending on lists --- setup.py | 6 +++--- torchvision/csrc/cpu/image/image.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index 27735abe2e6..ca90a8f21e6 100644 --- a/setup.py +++ b/setup.py @@ -197,8 +197,8 @@ def get_extensions(): png_lib = subprocess.run([libpng, '--libdir'], stdout=subprocess.PIPE) png_include = subprocess.run([libpng, '--I_opts'], stdout=subprocess.PIPE) - image_library += [png_lib.stdout] - image_include += [png_include.stdout] + 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') # Locating libjpegturbo @@ -281,7 +281,7 @@ def get_extensions(): ext_modules.append(extension( 'torchvision.image', image_src, - include_dirs=include_dirs + [image_src] + image_include, + include_dirs=include_dirs + image_src + image_include, library_dirs=library_dirs + image_library, define_macros=image_macros, extra_compile_args=extra_compile_args diff --git a/torchvision/csrc/cpu/image/image.h b/torchvision/csrc/cpu/image/image.h index 5278b970264..993595c0303 100644 --- a/torchvision/csrc/cpu/image/image.h +++ b/torchvision/csrc/cpu/image/image.h @@ -4,4 +4,4 @@ #include "readjpeg_cpu.h" #include "readpng_cpu.h" #include - +#include From 3cca3668ca4776346280352977c77f731508aedb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 18 Jun 2020 18:54:36 -0500 Subject: [PATCH 082/129] Minor path correction --- setup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index ca90a8f21e6..714609592ee 100644 --- a/setup.py +++ b/setup.py @@ -274,14 +274,14 @@ def get_extensions(): image_library += [turbo_lib_folder] image_include += [turbo_include_folder] - image_src = glob.glob( - os.path.join(extensions_dir, 'cpu', 'image', '*.cpp')) + 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_src + image_include, + include_dirs=include_dirs + image_path + image_include, library_dirs=library_dirs + image_library, define_macros=image_macros, extra_compile_args=extra_compile_args From 2a6ff9fa38bcebb3d9ae2ed6edf28e020e3a38ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 18 Jun 2020 18:55:16 -0500 Subject: [PATCH 083/129] Minor error correction --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 714609592ee..90726dd0815 100644 --- a/setup.py +++ b/setup.py @@ -281,7 +281,7 @@ def get_extensions(): ext_modules.append(extension( 'torchvision.image', image_src, - include_dirs=include_dirs + image_path + image_include, + include_dirs=include_dirs + [image_path] + image_include, library_dirs=library_dirs + image_library, define_macros=image_macros, extra_compile_args=extra_compile_args From b89b3499d02582382f0cfaeae3cd28b93b405508 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 18 Jun 2020 18:58:21 -0500 Subject: [PATCH 084/129] Add linking flags --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 90726dd0815..3ea206d2326 100644 --- a/setup.py +++ b/setup.py @@ -284,6 +284,7 @@ def get_extensions(): 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 )) From a0ce4caf9544a4acc5445465f41ed71ae78aac12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 18 Jun 2020 19:22:20 -0500 Subject: [PATCH 085/129] Style issues correction --- torchvision/csrc/cpu/image/image.cpp | 7 ++----- torchvision/csrc/cpu/image/image.h | 4 ++-- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/torchvision/csrc/cpu/image/image.cpp b/torchvision/csrc/cpu/image/image.cpp index 9a760c01449..33f492ae683 100644 --- a/torchvision/csrc/cpu/image/image.cpp +++ b/torchvision/csrc/cpu/image/image.cpp @@ -3,7 +3,6 @@ #include #include - // If we are in a Windows environment, we need to define // initialization functions for the _custom_ops extension #ifdef _WIN32 @@ -21,7 +20,5 @@ PyMODINIT_FUNC PyInit_image(void) { #endif static auto registry = torch::RegisterOperators() - .op("image::decode_png", - &decodePNG) - .op("image::decode_jpeg", - &decodeJPEG); + .op("image::decode_png", &decodePNG) + .op("image::decode_jpeg", &decodeJPEG); diff --git a/torchvision/csrc/cpu/image/image.h b/torchvision/csrc/cpu/image/image.h index 993595c0303..6db97d4729a 100644 --- a/torchvision/csrc/cpu/image/image.h +++ b/torchvision/csrc/cpu/image/image.h @@ -1,7 +1,7 @@ #pragma once +#include +#include #include "readjpeg_cpu.h" #include "readpng_cpu.h" -#include -#include From bd752aa6d1ff1eb3138bb0897c9200f41897e24c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 22 Jun 2020 18:47:54 -0500 Subject: [PATCH 086/129] Address minor review corrections --- torchvision/csrc/cpu/image/readjpeg_cpu.cpp | 5 +++-- torchvision/csrc/cpu/image/readpng_cpu.cpp | 3 ++- torchvision/extension.py | 2 -- torchvision/io/__init__.py | 5 +---- torchvision/io/image.py | 5 +---- 5 files changed, 7 insertions(+), 13 deletions(-) diff --git a/torchvision/csrc/cpu/image/readjpeg_cpu.cpp b/torchvision/csrc/cpu/image/readjpeg_cpu.cpp index 6a5785ed6c3..3f6567d4d4a 100644 --- a/torchvision/csrc/cpu/image/readjpeg_cpu.cpp +++ b/torchvision/csrc/cpu/image/readjpeg_cpu.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #if !JPEG_FOUND @@ -12,13 +11,15 @@ torch::Tensor decodeJPEG(const torch::Tensor& data) { } #else +#include + torch::Tensor decodeJPEG(const torch::Tensor& data) { tjhandle tjInstance = tjInitDecompress(); if (tjInstance == NULL) { TORCH_CHECK(false, "libjpeg-turbo decompression initialization failed."); } - auto datap = data.accessor().data(); + auto datap = data.accessor().data(); int width, height; diff --git a/torchvision/csrc/cpu/image/readpng_cpu.cpp b/torchvision/csrc/cpu/image/readpng_cpu.cpp index 5e4e3ffe17f..b7fb28e2575 100644 --- a/torchvision/csrc/cpu/image/readpng_cpu.cpp +++ b/torchvision/csrc/cpu/image/readpng_cpu.cpp @@ -1,7 +1,6 @@ #include "readpng_cpu.h" #include -#include #include #include @@ -10,6 +9,8 @@ torch::Tensor decodePNG(const torch::Tensor& data) { AT_ERROR("decodePNG: torchvision not compiled with libPNG support"); } #else +#include + torch::Tensor decodePNG(const torch::Tensor& data) { auto png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); diff --git a/torchvision/extension.py b/torchvision/extension.py index 17b606ba9a8..db3356aa67a 100644 --- a/torchvision/extension.py +++ b/torchvision/extension.py @@ -24,8 +24,6 @@ def _register_extensions(): _register_extensions() _HAS_OPS = True except (ImportError, OSError): - import traceback - traceback.print_exc() pass diff --git a/torchvision/io/__init__.py b/torchvision/io/__init__.py index 7bf9db55557..4c47d8a51d5 100644 --- a/torchvision/io/__init__.py +++ b/torchvision/io/__init__.py @@ -15,8 +15,6 @@ write_video, ) -from .image import (read_png) - __all__ = [ "write_video", @@ -32,6 +30,5 @@ "_read_video_clip_from_memory", "_read_video_meta_data", "VideoMetaData", - "Timebase", - "read_png" + "Timebase" ] diff --git a/torchvision/io/image.py b/torchvision/io/image.py index a95aedaf080..60d44380356 100644 --- a/torchvision/io/image.py +++ b/torchvision/io/image.py @@ -85,10 +85,7 @@ def decode_jpeg(input): if not input.dtype == torch.uint8: raise ValueError("Expected a torch.uint8 tensor.") - try: - output = torch.ops.image.decode_jpeg(input) - except RuntimeError: - raise ValueError("Invalid jpeg input.") + output = torch.ops.image.decode_jpeg(input) return output From 5259757f37a848e8a99eb35e458d70338148a76e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 22 Jun 2020 19:14:35 -0500 Subject: [PATCH 087/129] Refactor library search --- setup.py | 125 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 63 insertions(+), 62 deletions(-) diff --git a/setup.py b/setup.py index 3ea206d2326..0caccfd1b87 100644 --- a/setup.py +++ b/setup.py @@ -76,6 +76,66 @@ 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) @@ -202,68 +262,9 @@ def get_extensions(): image_link_flags.append('png' if os.name != 'nt' else 'libpng') # Locating libjpegturbo - build_prefix = os.environ.get('BUILD_PREFIX', None) - is_conda_build = build_prefix is not None - turbojpeg_found = False - conda_installed = False - turbojpeg_header = 'turbojpeg.h' - print('Running build on conda-build: {0}'.format(is_conda_build)) - if is_conda_build: - if os.name == 'nt': - lib_path = os.path.join(build_prefix, 'Library') - turbo_include_folder = os.path.join(lib_path, 'include') - turbojpeg_header = os.path.join( - turbo_include_folder, 'turbojpeg.h') - turbo_lib_folder = os.path.join(lib_path, 'lib') - turbojpeg_found = os.path.isfile(turbojpeg_header) - else: - # Add conda headers/libraries - turbo_include_folder = os.path.join(build_prefix, 'include') - turbo_lib_folder = os.path.join(build_prefix, 'lib') - turbojpeg_header = os.path.join( - turbo_include_folder, 'turbojpeg.h') - turbojpeg_found = os.path.isfile(turbojpeg_header) - conda_installed = turbojpeg_header - 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 - if os.name == 'nt': - env_folder = os.path.dirname(python_executable) - env_library_path = os.path.join(env_folder, 'Library') - turbo_include_folder = os.path.join( - env_library_path, 'include') - turbojpeg_header = os.path.join( - turbo_include_folder, 'turbojpeg.h') - turbo_lib_folder = os.path.join(env_library_path, 'lib') - turbojpeg_found = os.path.isfile(turbojpeg_header) - else: - env_bin_folder = os.path.dirname(python_executable) - env_folder = os.path.dirname(env_bin_folder) - turbo_lib_folder = os.path.join(env_folder, 'lib') - turbo_include_folder = os.path.join(env_folder, 'include') - turbojpeg_header = os.path.join( - turbo_include_folder, 'turbojpeg.h') - turbojpeg_found = os.path.isfile(turbojpeg_header) - conda_installed = turbojpeg_header - - # Try to locate turbojpeg in Linux standard paths - if not turbojpeg_found: - if sys.platform == 'linux': - turbojpeg_found = os.path.exists('/usr/include/turbojpeg.h') - turbojpeg_found = turbojpeg_found or os.path.exists( - '/usr/local/include/turbojpeg.h') - 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, 'turbojpeg.h') - turbojpeg_found = os.path.exists(candidate_path) - if turbojpeg_found: - break + 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)) From 30132472a838e08101bbfbaf56fea9c425d52b27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 22 Jun 2020 19:18:20 -0500 Subject: [PATCH 088/129] Restore access index --- torchvision/csrc/cpu/image/readjpeg_cpu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/torchvision/csrc/cpu/image/readjpeg_cpu.cpp b/torchvision/csrc/cpu/image/readjpeg_cpu.cpp index 3f6567d4d4a..1bf700a2317 100644 --- a/torchvision/csrc/cpu/image/readjpeg_cpu.cpp +++ b/torchvision/csrc/cpu/image/readjpeg_cpu.cpp @@ -19,7 +19,7 @@ torch::Tensor decodeJPEG(const torch::Tensor& data) { TORCH_CHECK(false, "libjpeg-turbo decompression initialization failed."); } - auto datap = data.accessor().data(); + auto datap = data.accessor().data(); int width, height; From 9d8b1b5b76daf5ef0dacb52f0eb3308a63b66dab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 22 Jun 2020 20:42:06 -0500 Subject: [PATCH 089/129] Fix JPEG tests --- test/test_image.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_image.py b/test/test_image.py index 866c522d8ec..603990fffc5 100644 --- a/test/test_image.py +++ b/test/test_image.py @@ -48,7 +48,7 @@ def test_decode_jpeg(self): with self.assertRaisesRegex(ValueError, "Expected a torch.uint8 tensor."): decode_jpeg(torch.empty((100, ), dtype=torch.float16)) - with self.assertRaisesRegex(ValueError, "Invalid jpeg input."): + with self.assertRaisesRegex(RuntimeError, "Error while reading jpeg headers."): decode_jpeg(torch.empty((100), dtype=torch.uint8)) def test_read_png(self): From 7c3ec510ca63a1d75cf8bb17e73d6e5c7479c589 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 22 Jun 2020 20:52:19 -0500 Subject: [PATCH 090/129] Update libpng version in Travis --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 53f66794c48..66e6f737e4a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,6 +18,8 @@ before_install: - conda info -a - conda create -q -n test-environment python=$TRAVIS_PYTHON_VERSION pytorch scipy -c pytorch-nightly + # LibPNG version in Ubuntu 16.04 is outdated + - conda install libpng - source activate test-environment - | if [[ "$IMAGE_BACKEND" == "Pillow-SIMD" ]]; then From 158eec87e0794e3d18cc772b26fcec277a21e127 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 22 Jun 2020 20:59:38 -0500 Subject: [PATCH 091/129] Add -y flag --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 66e6f737e4a..ceb2a7f3cb2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,7 +19,7 @@ before_install: - conda create -q -n test-environment python=$TRAVIS_PYTHON_VERSION pytorch scipy -c pytorch-nightly # LibPNG version in Ubuntu 16.04 is outdated - - conda install libpng + - conda install libpng -y - source activate test-environment - | if [[ "$IMAGE_BACKEND" == "Pillow-SIMD" ]]; then From 269d8e565e99c61f1651cbcb2c0c609e4bb6f024 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 22 Jun 2020 21:22:42 -0500 Subject: [PATCH 092/129] Remove dot --- test/test_image.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_image.py b/test/test_image.py index 603990fffc5..4ac331f399f 100644 --- a/test/test_image.py +++ b/test/test_image.py @@ -48,7 +48,7 @@ def test_decode_jpeg(self): with self.assertRaisesRegex(ValueError, "Expected a torch.uint8 tensor."): decode_jpeg(torch.empty((100, ), dtype=torch.float16)) - with self.assertRaisesRegex(RuntimeError, "Error while reading jpeg headers."): + with self.assertRaisesRegex(RuntimeError, "Error while reading jpeg headers"): decode_jpeg(torch.empty((100), dtype=torch.uint8)) def test_read_png(self): From 273dc1a1e6dd0bae935bfa7805e47ad3ae500fde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Tue, 23 Jun 2020 12:18:01 -0500 Subject: [PATCH 093/129] Update libpng using apt --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ceb2a7f3cb2..d6ca27390a5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,7 @@ matrix: before_install: - sudo apt-get update + - sudo apt-get install -y libpng16-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" @@ -19,7 +20,6 @@ before_install: - conda create -q -n test-environment python=$TRAVIS_PYTHON_VERSION pytorch scipy -c pytorch-nightly # LibPNG version in Ubuntu 16.04 is outdated - - conda install libpng -y - source activate test-environment - | if [[ "$IMAGE_BACKEND" == "Pillow-SIMD" ]]; then From cfc7c7513e0e1709c1a2c02f21d33ef157668f91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Tue, 23 Jun 2020 12:28:51 -0500 Subject: [PATCH 094/129] Check libpng version --- .travis.yml | 2 +- setup.py | 24 ++++++++++++++++++------ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index d6ca27390a5..d2cde15bfa8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ matrix: before_install: - sudo apt-get update - - sudo apt-get install -y libpng16-dev + - sudo apt-get install -y libpng16-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" diff --git a/setup.py b/setup.py index 0caccfd1b87..eedd59537c3 100644 --- a/setup.py +++ b/setup.py @@ -3,6 +3,7 @@ import re import sys from setuptools import setup, find_packages +from packaging import version from pkg_resources import get_distribution, DistributionNotFound import subprocess import distutils.command.clean @@ -253,13 +254,24 @@ def get_extensions(): image_macros += [('PNG_FOUND', str(int(png_found)))] print('PNG found: {0}'.format(png_found)) if png_found: - print('Building torchvision with PNG image support') - png_lib = subprocess.run([libpng, '--libdir'], stdout=subprocess.PIPE) - png_include = subprocess.run([libpng, '--I_opts'], + png_version = subprocess.run([libpng, '--version'], 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') + png_version = png_version.stdout.strip().decode('utf-8') + print('libpng version: {0}'.format(png_version)) + png_version = version.parse(png_version) + if png_version >= version.parse("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) From d32a5f035c14a0e757b51da706ee3098be8853d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Tue, 23 Jun 2020 12:37:18 -0500 Subject: [PATCH 095/129] Change libturbojpeg binary --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d2cde15bfa8..086df71ac34 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,9 @@ matrix: before_install: - sudo apt-get update - - sudo apt-get install -y libpng16-dev libturbojpeg-dev + - sudo apt-get install -y libpng16-dev libturbojpeg + # Prevent static library to be picked up + - sudo ln -s /usr/lib/x86_64-linux-gnu/libturbojpeg.so.0.1.0 /usr/lib/x86_64-linux-gnu/libturbojpeg.so - 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" From 051425b38c7f426a7a196295bc080ede9b3d6f4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Tue, 23 Jun 2020 12:46:43 -0500 Subject: [PATCH 096/129] Update import --- setup.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/setup.py b/setup.py index eedd59537c3..f84f95e834d 100644 --- a/setup.py +++ b/setup.py @@ -3,8 +3,7 @@ import re import sys from setuptools import setup, find_packages -from packaging import version -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 From 3bc73237f39f139a80ccb1e5a240d377a7b3f521 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Tue, 23 Jun 2020 12:48:12 -0500 Subject: [PATCH 097/129] Change call --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index f84f95e834d..b21135969f7 100644 --- a/setup.py +++ b/setup.py @@ -257,8 +257,8 @@ def get_extensions(): stdout=subprocess.PIPE) png_version = png_version.stdout.strip().decode('utf-8') print('libpng version: {0}'.format(png_version)) - png_version = version.parse(png_version) - if png_version >= version.parse("1.6.0"): + 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) From 6b87895411cd305bfc1c5a7b4f62b0718f60ca20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Wed, 24 Jun 2020 11:32:35 -0500 Subject: [PATCH 098/129] Restore av in conda recipe --- packaging/torchvision/meta.yaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packaging/torchvision/meta.yaml b/packaging/torchvision/meta.yaml index 70fd5aa5a29..f46a7704658 100644 --- a/packaging/torchvision/meta.yaml +++ b/packaging/torchvision/meta.yaml @@ -50,9 +50,7 @@ test: requires: - pytest - scipy - # AV is disabled temporarily until the jpeg/jpeg-turbo conflicts are solved - # in conda-forge - # - av + - av - ca-certificates {{ environ.get('CONDA_TYPING_CONSTRAINT') }} commands: From 123fd3f8018aca1e4bbdf09c01f2c61f380eb4e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 29 Jun 2020 15:53:45 -0500 Subject: [PATCH 099/129] Minor error correction --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index f25051a63f6..e454c59e047 100644 --- a/setup.py +++ b/setup.py @@ -126,7 +126,7 @@ def find_library(name, vision_include): '/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') + 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) From 831749caa1cb0ca0823629b36a6adaa675ecfaef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 29 Jun 2020 15:55:25 -0500 Subject: [PATCH 100/129] Remove unused comment in travis.yml --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 08ade7d7134..6d26928ce8d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,7 +25,6 @@ before_install: - conda info -a - conda create -q -n test-environment python=$TRAVIS_PYTHON_VERSION pytorch scipy -c pytorch-nightly - # LibPNG version in Ubuntu 16.04 is outdated - source activate test-environment - | if [[ "$IMAGE_BACKEND" == "Pillow-SIMD" ]]; then From 558b0cbbeb65b115fe79d249bcb3a09538e86af6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 29 Jun 2020 16:12:33 -0500 Subject: [PATCH 101/129] Update README --- README.rst | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 3150a3023ad..e8c21e5542b 100644 --- a/README.rst +++ b/README.rst @@ -78,13 +78,20 @@ 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. + .. _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: @@ -94,7 +101,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: From 8b2f507370cfab43f6f969a60a26175c88212545 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Mon, 29 Jun 2020 16:15:37 -0500 Subject: [PATCH 102/129] Fix missing links --- README.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index e8c21e5542b..d130ad03a78 100644 --- a/README.rst +++ b/README.rst @@ -82,9 +82,11 @@ Torchvision currently supports the following image backends: * `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. +**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 From b560227d34389c46b490b34e3e16b5a3546af0f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Tue, 30 Jun 2020 12:00:52 -0500 Subject: [PATCH 103/129] Remove fixes for 16.04 --- .travis.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6d26928ce8d..0757ad3c159 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,9 +13,7 @@ jobs: before_install: - sudo apt-get update - - sudo apt-get install -y libpng16-dev libturbojpeg - # Prevent static library to be picked up - - sudo ln -s /usr/lib/x86_64-linux-gnu/libturbojpeg.so.0.1.0 /usr/lib/x86_64-linux-gnu/libturbojpeg.so + - 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" From c6d3ebee40dafca11a8dc7013f4a33e90a405b80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Wed, 1 Jul 2020 11:56:25 -0500 Subject: [PATCH 104/129] Remove JPEG-related code --- setup.py | 16 +----- test/test_image.py | 29 ----------- torchvision/csrc/cpu/image/image.cpp | 3 +- torchvision/csrc/cpu/image/image.h | 1 - torchvision/csrc/cpu/image/readjpeg_cpu.cpp | 56 --------------------- torchvision/csrc/cpu/image/readjpeg_cpu.h | 5 -- torchvision/io/image.py | 41 --------------- 7 files changed, 2 insertions(+), 149 deletions(-) delete mode 100644 torchvision/csrc/cpu/image/readjpeg_cpu.cpp delete mode 100644 torchvision/csrc/cpu/image/readjpeg_cpu.h diff --git a/setup.py b/setup.py index e454c59e047..8a6aa6a7dcc 100644 --- a/setup.py +++ b/setup.py @@ -272,24 +272,10 @@ def get_extensions(): '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: + if png_found: ext_modules.append(extension( 'torchvision.image', image_src, diff --git a/test/test_image.py b/test/test_image.py index 4ac331f399f..d0547ac054c 100644 --- a/test/test_image.py +++ b/test/test_image.py @@ -22,35 +22,6 @@ def get_images(directory, img_ext): class ImageTester(unittest.TestCase): - def test_read_jpeg(self): - for img_path in get_images(IMAGE_ROOT, "jpg"): - img_pil = torch.from_numpy(np.array(Image.open(img_path))) - img_ljpeg = read_jpeg(img_path) - - norm = img_ljpeg.shape[0] * img_ljpeg.shape[1] * img_ljpeg.shape[2] * 255 - err = torch.abs(img_ljpeg.flatten().float() - img_pil.flatten().float()).sum().float() / (norm) - self.assertLessEqual(err, 1e-2) - - def test_decode_jpeg(self): - for img_path in get_images(IMAGE_ROOT, "jpg"): - img_pil = torch.from_numpy(np.array(Image.open(img_path))) - size = os.path.getsize(img_path) - img_ljpeg = decode_jpeg(torch.from_file(img_path, dtype=torch.uint8, size=size)) - - norm = img_ljpeg.shape[0] * img_ljpeg.shape[1] * img_ljpeg.shape[2] * 255 - err = torch.abs(img_ljpeg.flatten().float() - img_pil.flatten().float()).sum().float() / (norm) - - self.assertLessEqual(err, 1e-2) - - with self.assertRaisesRegex(ValueError, "Expected a non empty 1-dimensional tensor."): - decode_jpeg(torch.empty((100, 1), dtype=torch.uint8)) - - with self.assertRaisesRegex(ValueError, "Expected a torch.uint8 tensor."): - decode_jpeg(torch.empty((100, ), dtype=torch.float16)) - - with self.assertRaisesRegex(RuntimeError, "Error while reading jpeg headers"): - decode_jpeg(torch.empty((100), dtype=torch.uint8)) - def test_read_png(self): for img_path in get_images(IMAGE_DIR, "png"): img_pil = torch.from_numpy(np.array(Image.open(img_path))) diff --git a/torchvision/csrc/cpu/image/image.cpp b/torchvision/csrc/cpu/image/image.cpp index 33f492ae683..11e04057e76 100644 --- a/torchvision/csrc/cpu/image/image.cpp +++ b/torchvision/csrc/cpu/image/image.cpp @@ -20,5 +20,4 @@ PyMODINIT_FUNC PyInit_image(void) { #endif static auto registry = torch::RegisterOperators() - .op("image::decode_png", &decodePNG) - .op("image::decode_jpeg", &decodeJPEG); + .op("image::decode_png", &decodePNG); diff --git a/torchvision/csrc/cpu/image/image.h b/torchvision/csrc/cpu/image/image.h index 6db97d4729a..3f1c9561b40 100644 --- a/torchvision/csrc/cpu/image/image.h +++ b/torchvision/csrc/cpu/image/image.h @@ -3,5 +3,4 @@ #include #include -#include "readjpeg_cpu.h" #include "readpng_cpu.h" diff --git a/torchvision/csrc/cpu/image/readjpeg_cpu.cpp b/torchvision/csrc/cpu/image/readjpeg_cpu.cpp deleted file mode 100644 index 1bf700a2317..00000000000 --- a/torchvision/csrc/cpu/image/readjpeg_cpu.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include "readjpeg_cpu.h" - -#include -#include -#include - -#if !JPEG_FOUND - -torch::Tensor decodeJPEG(const torch::Tensor& data) { - AT_ERROR("decodeJPEG: torchvision not compiled with turboJPEG support"); -} - -#else -#include - -torch::Tensor decodeJPEG(const torch::Tensor& data) { - tjhandle tjInstance = tjInitDecompress(); - if (tjInstance == NULL) { - TORCH_CHECK(false, "libjpeg-turbo decompression initialization failed."); - } - - auto datap = data.accessor().data(); - - int width, height; - - if (tjDecompressHeader(tjInstance, datap, data.numel(), &width, &height) < - 0) { - tjDestroy(tjInstance); - TORCH_CHECK(false, "Error while reading jpeg headers"); - } - auto tensor = - torch::empty({int64_t(height), int64_t(width), int64_t(3)}, torch::kU8); - - auto ptr = tensor.accessor().data(); - - int pixelFormat = TJPF_RGB; - - auto ret = tjDecompress2( - tjInstance, - datap, - data.numel(), - ptr, - width, - 0, - height, - pixelFormat, - NULL); - if (ret != 0) { - tjDestroy(tjInstance); - TORCH_CHECK(false, "decompressing JPEG image"); - } - - return tensor; -} - -#endif // JPEG_FOUND diff --git a/torchvision/csrc/cpu/image/readjpeg_cpu.h b/torchvision/csrc/cpu/image/readjpeg_cpu.h deleted file mode 100644 index 40404df29b5..00000000000 --- a/torchvision/csrc/cpu/image/readjpeg_cpu.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include - -torch::Tensor decodeJPEG(const torch::Tensor& data); diff --git a/torchvision/io/image.py b/torchvision/io/image.py index 60d44380356..ea12f13e3bd 100644 --- a/torchvision/io/image.py +++ b/torchvision/io/image.py @@ -66,44 +66,3 @@ def read_png(path): raise ValueError("Expected a non empty file.") data = torch.from_file(path, dtype=torch.uint8, size=size) return decode_png(data) - - -def decode_jpeg(input): - # type: (Tensor) -> Tensor - """ - Decodes a JPEG image into a 3 dimensional RGB Tensor. - The values of the output tensor are uint8 between 0 and 255. - Arguments: - input (Tensor[1]): a one dimensional int8 tensor containing - the raw bytes of the JPEG image. - Returns: - output (Tensor[image_width, image_height, 3]) - """ - if not isinstance(input, torch.Tensor) or len(input) == 0 or input.ndim != 1: - raise ValueError("Expected a non empty 1-dimensional tensor.") - - if not input.dtype == torch.uint8: - raise ValueError("Expected a torch.uint8 tensor.") - - output = torch.ops.image.decode_jpeg(input) - return output - - -def read_jpeg(path): - # type: (str) -> Tensor - """ - Reads a JPEG image into a 3 dimensional RGB Tensor. - The values of the output tensor are uint8 between 0 and 255. - Arguments: - path (str): path of the JPEG image. - Returns: - output (Tensor[image_width, image_height, 3]) - """ - if not os.path.isfile(path): - raise ValueError("Expected a valid file path.") - - size = os.path.getsize(path) - if size == 0: - raise ValueError("Expected a non empty file.") - data = torch.from_file(path, dtype=torch.uint8, size=size) - return decode_jpeg(data) From 39a0334dd5b7236d5e969a98d1de1d1f878a54a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Wed, 1 Jul 2020 11:58:23 -0500 Subject: [PATCH 105/129] Remove installation references to turbojpeg --- README.rst | 6 +----- packaging/pkg_helpers.bash | 6 ++---- packaging/torchvision/meta.yaml | 4 ---- 3 files changed, 3 insertions(+), 13 deletions(-) diff --git a/README.rst b/README.rst index d130ad03a78..c2027300a98 100644 --- a/README.rst +++ b/README.rst @@ -80,13 +80,9 @@ Torchvision currently supports the following image backends: * `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. +**Notes:** ``libpng`` must be available at compilation time in order to be available. .. _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 diff --git a/packaging/pkg_helpers.bash b/packaging/pkg_helpers.bash index 88fe52c0b08..16f1a26c300 100644 --- a/packaging/pkg_helpers.bash +++ b/packaging/pkg_helpers.bash @@ -172,11 +172,9 @@ setup_wheel_python() { 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 + # Install native CentOS libPNG + yum install -y libpng-devel case "$PYTHON_VERSION" in 2.7) if [[ -n "$UNICODE_ABI" ]]; then diff --git a/packaging/torchvision/meta.yaml b/packaging/torchvision/meta.yaml index f46a7704658..639b0a254b8 100644 --- a/packaging/torchvision/meta.yaml +++ b/packaging/torchvision/meta.yaml @@ -9,7 +9,6 @@ requirements: build: - {{ compiler('c') }} # [win] - libpng - - libjpeg-turbo host: - python @@ -21,9 +20,6 @@ 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') }} From 4a1357e31ab1f119e00f0d1063d58ec935b7a07e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Wed, 1 Jul 2020 12:02:36 -0500 Subject: [PATCH 106/129] Remove further references to turbojpeg --- .circleci/config.yml | 1 - .circleci/config.yml.in | 1 - .travis.yml | 2 +- packaging/build_wheel.sh | 6 ------ setup.py | 1 - 5 files changed, 1 insertion(+), 10 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f3fc23b7c92..e1fc2d392c8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -108,7 +108,6 @@ jobs: - 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 . diff --git a/.circleci/config.yml.in b/.circleci/config.yml.in index f63c3f408ba..62ef94a1169 100644 --- a/.circleci/config.yml.in +++ b/.circleci/config.yml.in @@ -108,7 +108,6 @@ jobs: - 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 . diff --git a/.travis.yml b/.travis.yml index 0757ad3c159..d8c45c2defe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ jobs: before_install: - sudo apt-get update - - sudo apt-get install -y libpng-dev libturbojpeg-dev + - sudo apt-get install -y libpng-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" diff --git a/packaging/build_wheel.sh b/packaging/build_wheel.sh index 30ace73d231..fae1da2649b 100755 --- a/packaging/build_wheel.sh +++ b/packaging/build_wheel.sh @@ -19,19 +19,13 @@ if [[ "$(uname)" == Darwin || "$OSTYPE" == "msys" ]]; then 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 diff --git a/setup.py b/setup.py index 8a6aa6a7dcc..f7d01dcbf43 100644 --- a/setup.py +++ b/setup.py @@ -117,7 +117,6 @@ def find_library(name, vision_include): 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( From b2fd15100d179c9cdddf4e837524bdc259a6d4ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Wed, 1 Jul 2020 12:04:40 -0500 Subject: [PATCH 107/129] Fix c++ style issues --- torchvision/csrc/cpu/image/image.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/torchvision/csrc/cpu/image/image.cpp b/torchvision/csrc/cpu/image/image.cpp index 11e04057e76..321ff30271d 100644 --- a/torchvision/csrc/cpu/image/image.cpp +++ b/torchvision/csrc/cpu/image/image.cpp @@ -19,5 +19,4 @@ PyMODINIT_FUNC PyInit_image(void) { #endif #endif -static auto registry = torch::RegisterOperators() - .op("image::decode_png", &decodePNG); +static auto registry = torch::RegisterOperators().op("image::decode_png", &decodePNG); From 0fc3135513352eceba2fda1157e80441a8613b21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Wed, 1 Jul 2020 12:05:45 -0500 Subject: [PATCH 108/129] Fix c++ style issues --- torchvision/csrc/cpu/image/image.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/torchvision/csrc/cpu/image/image.cpp b/torchvision/csrc/cpu/image/image.cpp index 321ff30271d..ca9082db3c0 100644 --- a/torchvision/csrc/cpu/image/image.cpp +++ b/torchvision/csrc/cpu/image/image.cpp @@ -19,4 +19,5 @@ PyMODINIT_FUNC PyInit_image(void) { #endif #endif -static auto registry = torch::RegisterOperators().op("image::decode_png", &decodePNG); +static auto registry = + torch::RegisterOperators().op("image::decode_png", &decodePNG); From ce929953f6131097af2cb67104925104def84ef5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Wed, 1 Jul 2020 12:18:30 -0500 Subject: [PATCH 109/129] Fix libpng-config include flag parsing --- setup.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index f7d01dcbf43..923b612aa25 100644 --- a/setup.py +++ b/setup.py @@ -263,8 +263,10 @@ def get_extensions(): stdout=subprocess.PIPE) png_include = subprocess.run([libpng, '--I_opts'], stdout=subprocess.PIPE) + png_include = png_include.stdout.strip().decode('utf-8') + _, png_include = png_include.split('-I') image_library += [png_lib.stdout.strip().decode('utf-8')] - image_include += [png_include.stdout.strip().decode('utf-8')] + image_include += [png_include] image_link_flags.append('png' if os.name != 'nt' else 'libpng') else: print('libpng installed version is less than 1.6.0, ' @@ -278,8 +280,8 @@ def get_extensions(): ext_modules.append(extension( 'torchvision.image', image_src, - include_dirs=include_dirs + [image_path] + image_include, - library_dirs=library_dirs + image_library, + include_dirs=image_include + include_dirs + [image_path], + library_dirs=image_library + library_dirs, define_macros=image_macros, libraries=image_link_flags, extra_compile_args=extra_compile_args From 2614a9c4f30fbf2ba023793578bb778a429fa378 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Wed, 1 Jul 2020 12:20:13 -0500 Subject: [PATCH 110/129] Remove conda-forge --- packaging/torchvision/conda_build_config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/torchvision/conda_build_config.yaml b/packaging/torchvision/conda_build_config.yaml index adbbc732a0c..f745e4f0e5a 100644 --- a/packaging/torchvision/conda_build_config.yaml +++ b/packaging/torchvision/conda_build_config.yaml @@ -1,5 +1,5 @@ channel_sources: - - defaults,conda-forge + - defaults blas_impl: - mkl # [x86_64] From bf41911bf48472743e657c69f4cb62a97bc84958 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Wed, 1 Jul 2020 12:22:10 -0500 Subject: [PATCH 111/129] Remove include dirs from main extension --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index 923b612aa25..aebfef3ad61 100644 --- a/setup.py +++ b/setup.py @@ -224,7 +224,6 @@ def get_extensions(): 'torchvision._C', sources, include_dirs=include_dirs, - library_dirs=library_dirs, define_macros=define_macros, extra_compile_args=extra_compile_args, ) From eb238303036bf1c6343f950a22321ebe50344f38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Wed, 1 Jul 2020 12:41:49 -0500 Subject: [PATCH 112/129] Do not pass extra include and library paths to main torchvision extension --- setup.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/setup.py b/setup.py index aebfef3ad61..5ba9c4e90ae 100644 --- a/setup.py +++ b/setup.py @@ -136,15 +136,6 @@ def find_library(name, vision_include): 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') @@ -217,7 +208,7 @@ 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( @@ -239,6 +230,16 @@ def get_extensions(): ) ) + # ------------------- Torchvision extra 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 + # Image reading extension image_macros = [] image_include = [extensions_dir] From c41800cb2f79caf5728c57e3e9e78c25e5dc39b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Wed, 1 Jul 2020 12:44:38 -0500 Subject: [PATCH 113/129] Add libpng to environment.yml --- .circleci/unittest/linux/scripts/environment.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.circleci/unittest/linux/scripts/environment.yml b/.circleci/unittest/linux/scripts/environment.yml index d664c7c0f7f..2b3604ee1c8 100644 --- a/.circleci/unittest/linux/scripts/environment.yml +++ b/.circleci/unittest/linux/scripts/environment.yml @@ -6,6 +6,7 @@ dependencies: - pytest-cov - codecov - pip + - libpng - ca-certificates - pip: - future From b1ff7839b4943768cce767aec2ec65c884fac23a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Wed, 1 Jul 2020 12:51:13 -0500 Subject: [PATCH 114/129] Remove inexistent imports --- test/test_image.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_image.py b/test/test_image.py index d0547ac054c..f40dacd4be4 100644 --- a/test/test_image.py +++ b/test/test_image.py @@ -5,7 +5,7 @@ import torch import torchvision from PIL import Image -from torchvision.io.image import read_png, decode_png, read_jpeg, decode_jpeg +from torchvision.io.image import read_png, decode_png import numpy as np IMAGE_ROOT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "assets") From 84dad44062a55bc19340019064c774a950ae3e23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Wed, 1 Jul 2020 14:01:04 -0500 Subject: [PATCH 115/129] Add instructions regarding environment variables to README --- .gitignore | 2 ++ README.rst | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 6bea8609b93..6d649a7c019 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,5 @@ htmlcov *.swo gen.yml .mypy_cache +.vscode/ +*.orig diff --git a/README.rst b/README.rst index c2027300a98..0de404c74b3 100644 --- a/README.rst +++ b/README.rst @@ -80,7 +80,8 @@ Torchvision currently supports the following image backends: * `libpng`_ - can be installed via conda :code:`conda install libpng` or any of the package managers for debian-based and RHEL-based Linux distributions. -**Notes:** ``libpng`` must be available at compilation time in order to be available. +**Notes:** ``libpng`` must be available at compilation time in order to be available. Make sure that it is available on the standard library locations, +otherwise, add the include and library paths in the environment variables ``TORCHVISION_INCLUDE`` and ``TORCHVISION_LIBRARY``, respectively. .. _libpng : http://www.libpng.org/pub/png/libpng.html .. _Pillow : https://python-pillow.org/ From 2d0b7522a0dccf2b36ad25a0e7156dde4464da95 Mon Sep 17 00:00:00 2001 From: Francisco Massa Date: Thu, 2 Jul 2020 12:01:08 +0200 Subject: [PATCH 116/129] Fix image extension tests --- test/test_image.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_image.py b/test/test_image.py index f40dacd4be4..07fec3dbf14 100644 --- a/test/test_image.py +++ b/test/test_image.py @@ -23,13 +23,13 @@ def get_images(directory, img_ext): class ImageTester(unittest.TestCase): def test_read_png(self): - for img_path in get_images(IMAGE_DIR, "png"): + for img_path in get_images(IMAGE_DIR, ".png"): img_pil = torch.from_numpy(np.array(Image.open(img_path))) img_lpng = read_png(img_path) self.assertEqual(img_lpng, img_pil) def test_decode_png(self): - for img_path in get_images(IMAGE_DIR, "png"): + for img_path in get_images(IMAGE_DIR, ".png"): img_pil = torch.from_numpy(np.array(Image.open(img_path))) size = os.path.getsize(img_path) img_lpng = decode_png(torch.from_file(img_path, dtype=torch.uint8, size=size)) From ca8853ef7f844bae884de20c0a302ed92e84ee3c Mon Sep 17 00:00:00 2001 From: Francisco Massa Date: Thu, 2 Jul 2020 12:13:29 +0200 Subject: [PATCH 117/129] Add libpng to environment + test fixes --- .circleci/unittest/windows/scripts/environment.yml | 3 ++- test/test_image.py | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.circleci/unittest/windows/scripts/environment.yml b/.circleci/unittest/windows/scripts/environment.yml index fbe92df523d..ddbf7445a92 100644 --- a/.circleci/unittest/windows/scripts/environment.yml +++ b/.circleci/unittest/windows/scripts/environment.yml @@ -6,9 +6,10 @@ dependencies: - pytest-cov - codecov - pip + - libpng - ca-certificates - pip: - future - pillow>=4.1.1 - scipy==1.4.1 - - av \ No newline at end of file + - av diff --git a/test/test_image.py b/test/test_image.py index 07fec3dbf14..34fd11acc43 100644 --- a/test/test_image.py +++ b/test/test_image.py @@ -26,17 +26,17 @@ def test_read_png(self): for img_path in get_images(IMAGE_DIR, ".png"): img_pil = torch.from_numpy(np.array(Image.open(img_path))) img_lpng = read_png(img_path) - self.assertEqual(img_lpng, img_pil) + self.assertTrue(img_lpng.equal(img_pil)) def test_decode_png(self): for img_path in get_images(IMAGE_DIR, ".png"): img_pil = torch.from_numpy(np.array(Image.open(img_path))) size = os.path.getsize(img_path) img_lpng = decode_png(torch.from_file(img_path, dtype=torch.uint8, size=size)) - self.assertEqual(img_lpng, img_pil) + self.assertTrue(img_lpng.equal(img_pil)) - self.assertEqual(decode_png(torch.empty()), torch.empty()) - self.assertEqual(decode_png(torch.randint(3, 5, (300,))), torch.empty()) + self.assertTrue(decode_png(torch.empty()).equal(torch.empty())) + self.assertTrue(decode_png(torch.randint(3, 5, (300,))).equal(torch.empty())) if __name__ == '__main__': From adea0fcfc365d728558bb825a7f99162df579aed Mon Sep 17 00:00:00 2001 From: Francisco Massa Date: Thu, 2 Jul 2020 12:47:43 +0200 Subject: [PATCH 118/129] Minor improvements --- test/test_image.py | 6 ++++-- torchvision/io/image.py | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/test/test_image.py b/test/test_image.py index 34fd11acc43..c368a4f22f8 100644 --- a/test/test_image.py +++ b/test/test_image.py @@ -35,8 +35,10 @@ def test_decode_png(self): img_lpng = decode_png(torch.from_file(img_path, dtype=torch.uint8, size=size)) self.assertTrue(img_lpng.equal(img_pil)) - self.assertTrue(decode_png(torch.empty()).equal(torch.empty())) - self.assertTrue(decode_png(torch.randint(3, 5, (300,))).equal(torch.empty())) + with self.assertRaises(ValueError): + decode_png(torch.empty((), dtype=torch.uint8)) + with self.assertRaises(RuntimeError): + decode_png(torch.randint(3, 5, (300,), dtype=torch.uint8)) if __name__ == '__main__': diff --git a/torchvision/io/image.py b/torchvision/io/image.py index ea12f13e3bd..1ad13ed27ad 100644 --- a/torchvision/io/image.py +++ b/torchvision/io/image.py @@ -37,7 +37,7 @@ def decode_png(input): Returns: output (Tensor[image_width, image_height, 3]) """ - if not isinstance(input, torch.Tensor) or len(input) == 0: + if not isinstance(input, torch.Tensor) or input.numel() == 0 or input.ndim != 1: raise ValueError("Expected a non empty 1-dimensional tensor.") if not input.dtype == torch.uint8: From a0f86b99164cbe19575f1d0f063c8c73eb7f4ff5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 2 Jul 2020 10:43:47 -0500 Subject: [PATCH 119/129] Remove unused Py2 code --- torchvision/csrc/cpu/image/image.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/torchvision/csrc/cpu/image/image.cpp b/torchvision/csrc/cpu/image/image.cpp index ca9082db3c0..0dc82a69827 100644 --- a/torchvision/csrc/cpu/image/image.cpp +++ b/torchvision/csrc/cpu/image/image.cpp @@ -6,18 +6,11 @@ // If we are in a Windows environment, we need to define // initialization functions for the _custom_ops extension #ifdef _WIN32 -#if PY_MAJOR_VERSION < 3 -PyMODINIT_FUNC init_image(void) { - // No need to do anything. - return NULL; -} -#else PyMODINIT_FUNC PyInit_image(void) { // No need to do anything. return NULL; } #endif -#endif static auto registry = torch::RegisterOperators().op("image::decode_png", &decodePNG); From d74cd79415ebeb706a02bf8c04831c25ed620b95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 2 Jul 2020 10:44:48 -0500 Subject: [PATCH 120/129] Add stub comments to prevent deletion while merging --- torchvision/csrc/cpu/image/image.h | 1 + torchvision/csrc/cpu/image/readpng_cpu.cpp | 1 + torchvision/csrc/cpu/image/readpng_cpu.h | 1 + 3 files changed, 3 insertions(+) diff --git a/torchvision/csrc/cpu/image/image.h b/torchvision/csrc/cpu/image/image.h index 3f1c9561b40..f5b86cf683b 100644 --- a/torchvision/csrc/cpu/image/image.h +++ b/torchvision/csrc/cpu/image/image.h @@ -1,6 +1,7 @@ #pragma once +// Comment #include #include #include "readpng_cpu.h" diff --git a/torchvision/csrc/cpu/image/readpng_cpu.cpp b/torchvision/csrc/cpu/image/readpng_cpu.cpp index b7fb28e2575..b284067b1ff 100644 --- a/torchvision/csrc/cpu/image/readpng_cpu.cpp +++ b/torchvision/csrc/cpu/image/readpng_cpu.cpp @@ -1,5 +1,6 @@ #include "readpng_cpu.h" +// Comment #include #include #include diff --git a/torchvision/csrc/cpu/image/readpng_cpu.h b/torchvision/csrc/cpu/image/readpng_cpu.h index d2151a43aa9..38fab84dc7c 100644 --- a/torchvision/csrc/cpu/image/readpng_cpu.h +++ b/torchvision/csrc/cpu/image/readpng_cpu.h @@ -1,5 +1,6 @@ #pragma once +// Comment #include #include From 29547b2cf90fc007ba726c778f333e57dd8b97ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 2 Jul 2020 11:06:21 -0500 Subject: [PATCH 121/129] Reintroduce files in order to prevent deletion during merge --- .circleci/unittest/linux/scripts/environment.yml | 1 + .gitignore | 1 + .travis.yml | 1 + CMakeLists.txt | 1 + README.rst | 2 +- packaging/build_wheel.sh | 2 +- packaging/pkg_helpers.bash | 2 +- packaging/torchvision/meta.yaml | 1 + setup.py | 1 + test/test_image.py | 1 + torchvision/io/image.py | 3 ++- 11 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.circleci/unittest/linux/scripts/environment.yml b/.circleci/unittest/linux/scripts/environment.yml index 2b3604ee1c8..f8f0ea18b26 100644 --- a/.circleci/unittest/linux/scripts/environment.yml +++ b/.circleci/unittest/linux/scripts/environment.yml @@ -6,6 +6,7 @@ dependencies: - pytest-cov - codecov - pip + # Required by the image extension - libpng - ca-certificates - pip: diff --git a/.gitignore b/.gitignore index 6d649a7c019..cc5c3c38985 100644 --- a/.gitignore +++ b/.gitignore @@ -22,4 +22,5 @@ htmlcov gen.yml .mypy_cache .vscode/ +# Introduced during merge errors *.orig diff --git a/.travis.yml b/.travis.yml index d8c45c2defe..b5220bad6cf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,6 +13,7 @@ jobs: before_install: - sudo apt-get update + # Install libpng - sudo apt-get install -y libpng-dev - wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh; - bash miniconda.sh -b -p $HOME/miniconda diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d7e7aaa9d8..2d28bc8a4c5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,7 @@ find_package(PNG REQUIRED) file(GLOB HEADERS torchvision/csrc/*.h) +# Image extension 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) diff --git a/README.rst b/README.rst index 0de404c74b3..edd0dfcd745 100644 --- a/README.rst +++ b/README.rst @@ -78,7 +78,7 @@ 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. +* `libpng`_ - can be installed via conda :code:`conda install libpng` or any of the package managers for debian-based and RHEL-based Linux distributions **Notes:** ``libpng`` must be available at compilation time in order to be available. Make sure that it is available on the standard library locations, otherwise, add the include and library paths in the environment variables ``TORCHVISION_INCLUDE`` and ``TORCHVISION_LIBRARY``, respectively. diff --git a/packaging/build_wheel.sh b/packaging/build_wheel.sh index fae1da2649b..35c5d7d443d 100755 --- a/packaging/build_wheel.sh +++ b/packaging/build_wheel.sh @@ -20,7 +20,7 @@ if [[ "$(uname)" == Darwin || "$OSTYPE" == "msys" ]]; then # Include LibPNG cp "$env_path/lib/libpng16.dylib" torchvision else - # Include libPNG + # Include libPNG lib/dll cp "$bin_path/Library/lib/libpng.lib" torchvision fi else diff --git a/packaging/pkg_helpers.bash b/packaging/pkg_helpers.bash index 16f1a26c300..781346e590f 100644 --- a/packaging/pkg_helpers.bash +++ b/packaging/pkg_helpers.bash @@ -173,7 +173,7 @@ setup_wheel_python() { # Install libPNG from Anaconda (defaults) conda install libpng -y else - # Install native CentOS libPNG + # Install native CentOS libPNG16 yum install -y libpng-devel case "$PYTHON_VERSION" in 2.7) diff --git a/packaging/torchvision/meta.yaml b/packaging/torchvision/meta.yaml index b3fc2a2e9df..3ce357c8873 100644 --- a/packaging/torchvision/meta.yaml +++ b/packaging/torchvision/meta.yaml @@ -8,6 +8,7 @@ source: requirements: build: - {{ compiler('c') }} # [win] + # Required by the image extension - libpng host: diff --git a/setup.py b/setup.py index 5ba9c4e90ae..e227895e202 100644 --- a/setup.py +++ b/setup.py @@ -265,6 +265,7 @@ def get_extensions(): stdout=subprocess.PIPE) png_include = png_include.stdout.strip().decode('utf-8') _, png_include = png_include.split('-I') + print('libpng include path: {0}'.format(png_include)) image_library += [png_lib.stdout.strip().decode('utf-8')] image_include += [png_include] image_link_flags.append('png' if os.name != 'nt' else 'libpng') diff --git a/test/test_image.py b/test/test_image.py index c368a4f22f8..a7f660127b8 100644 --- a/test/test_image.py +++ b/test/test_image.py @@ -23,6 +23,7 @@ def get_images(directory, img_ext): class ImageTester(unittest.TestCase): def test_read_png(self): + # Check across .png for img_path in get_images(IMAGE_DIR, ".png"): img_pil = torch.from_numpy(np.array(Image.open(img_path))) img_lpng = read_png(img_path) diff --git a/torchvision/io/image.py b/torchvision/io/image.py index 1ad13ed27ad..864c76cf7d2 100644 --- a/torchvision/io/image.py +++ b/torchvision/io/image.py @@ -37,7 +37,8 @@ def decode_png(input): Returns: output (Tensor[image_width, image_height, 3]) """ - if not isinstance(input, torch.Tensor) or input.numel() == 0 or input.ndim != 1: + if not (isinstance(input, torch.Tensor) or + input.numel() == 0 or input.ndim != 1): raise ValueError("Expected a non empty 1-dimensional tensor.") if not input.dtype == torch.uint8: From c6afa2f650ba3e969d9a5d3f53acf842773c9d4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 2 Jul 2020 11:15:10 -0500 Subject: [PATCH 122/129] Remove unwanted merge sections --- .circleci/unittest/linux/scripts/environment.yml | 4 ---- .gitignore | 4 ---- .travis.yml | 4 ---- packaging/build_wheel.sh | 4 ---- packaging/pkg_helpers.bash | 5 +---- packaging/torchvision/meta.yaml | 4 ---- 6 files changed, 1 insertion(+), 24 deletions(-) diff --git a/.circleci/unittest/linux/scripts/environment.yml b/.circleci/unittest/linux/scripts/environment.yml index 1d1cdca2646..2b3604ee1c8 100644 --- a/.circleci/unittest/linux/scripts/environment.yml +++ b/.circleci/unittest/linux/scripts/environment.yml @@ -6,11 +6,7 @@ dependencies: - pytest-cov - codecov - pip -<<<<<<< HEAD - # Required by the image extension - libpng -======= ->>>>>>> upstream/master - ca-certificates - pip: - future diff --git a/.gitignore b/.gitignore index 0ea0c766e37..6d649a7c019 100644 --- a/.gitignore +++ b/.gitignore @@ -21,9 +21,5 @@ htmlcov *.swo gen.yml .mypy_cache -<<<<<<< HEAD .vscode/ -# Introduced during merge errors *.orig -======= ->>>>>>> upstream/master diff --git a/.travis.yml b/.travis.yml index 2a526f6c9a0..d8c45c2defe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,11 +13,7 @@ jobs: before_install: - sudo apt-get update -<<<<<<< HEAD - # Install libpng - sudo apt-get install -y libpng-dev -======= ->>>>>>> upstream/master - 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" diff --git a/packaging/build_wheel.sh b/packaging/build_wheel.sh index 68297430cb9..b4dbde6b7e4 100755 --- a/packaging/build_wheel.sh +++ b/packaging/build_wheel.sh @@ -10,7 +10,6 @@ setup_wheel_python pip_install numpy pyyaml future ninja setup_pip_pytorch_version python setup.py clean -<<<<<<< HEAD # Copy binaries to be included in the wheel distribution if [[ "$(uname)" == Darwin || "$OSTYPE" == "msys" ]]; then @@ -21,7 +20,6 @@ if [[ "$(uname)" == Darwin || "$OSTYPE" == "msys" ]]; then # Include LibPNG cp "$env_path/lib/libpng16.dylib" torchvision else - # Include libPNG lib/dll cp "$bin_path/Library/lib/libpng.lib" torchvision fi else @@ -29,8 +27,6 @@ else cp "/usr/lib64/libpng.so" torchvision fi -======= ->>>>>>> upstream/master if [[ "$OSTYPE" == "msys" ]]; then IS_WHEEL=1 "$script_dir/windows/internal/vc_env_helper.bat" python setup.py bdist_wheel else diff --git a/packaging/pkg_helpers.bash b/packaging/pkg_helpers.bash index 56a415b7f8c..ce497b8bf51 100644 --- a/packaging/pkg_helpers.bash +++ b/packaging/pkg_helpers.bash @@ -171,11 +171,8 @@ setup_wheel_python() { conda create -yn "env$PYTHON_VERSION" python="$PYTHON_VERSION" conda activate "env$PYTHON_VERSION" else -<<<<<<< HEAD - # Install native CentOS libPNG16 + # Install native CentOS libPNG yum install -y libpng-devel -======= ->>>>>>> upstream/master case "$PYTHON_VERSION" in 2.7) if [[ -n "$UNICODE_ABI" ]]; then diff --git a/packaging/torchvision/meta.yaml b/packaging/torchvision/meta.yaml index 828377e7559..04738f48891 100644 --- a/packaging/torchvision/meta.yaml +++ b/packaging/torchvision/meta.yaml @@ -8,11 +8,7 @@ source: requirements: build: - {{ compiler('c') }} # [win] -<<<<<<< HEAD - # Required by the image extension - libpng -======= ->>>>>>> upstream/master host: - python From 1ad8e89b750d15ccba681302fe8a55256993f9e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 2 Jul 2020 11:22:49 -0500 Subject: [PATCH 123/129] Restore libpng conda installation on wheel distributions --- packaging/pkg_helpers.bash | 1 + 1 file changed, 1 insertion(+) diff --git a/packaging/pkg_helpers.bash b/packaging/pkg_helpers.bash index ce497b8bf51..f20a2b8265f 100644 --- a/packaging/pkg_helpers.bash +++ b/packaging/pkg_helpers.bash @@ -170,6 +170,7 @@ 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" + conda install libpng -y else # Install native CentOS libPNG yum install -y libpng-devel From 28c1d8b9bae5c76e929a8702253515a2a0a164b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 2 Jul 2020 11:24:02 -0500 Subject: [PATCH 124/129] Restore comment --- packaging/pkg_helpers.bash | 1 + 1 file changed, 1 insertion(+) diff --git a/packaging/pkg_helpers.bash b/packaging/pkg_helpers.bash index f20a2b8265f..128d0b51913 100644 --- a/packaging/pkg_helpers.bash +++ b/packaging/pkg_helpers.bash @@ -170,6 +170,7 @@ 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 else # Install native CentOS libPNG From 0967c3b7bdf1fb751269541fb7d43cba777e08e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 2 Jul 2020 11:31:49 -0500 Subject: [PATCH 125/129] Fix libpng discovery on Windows --- packaging/build_wheel.sh | 2 +- setup.py | 55 ++++++++++++++++++++++++---------------- 2 files changed, 34 insertions(+), 23 deletions(-) diff --git a/packaging/build_wheel.sh b/packaging/build_wheel.sh index b4dbde6b7e4..1a6e1b1761a 100755 --- a/packaging/build_wheel.sh +++ b/packaging/build_wheel.sh @@ -20,7 +20,7 @@ if [[ "$(uname)" == Darwin || "$OSTYPE" == "msys" ]]; then # Include LibPNG cp "$env_path/lib/libpng16.dylib" torchvision else - cp "$bin_path/Library/lib/libpng.lib" torchvision + cp "$bin_path/Library/bin/libpng16.dll" torchvision fi else # Include LibPNG diff --git a/setup.py b/setup.py index e227895e202..dbc9d5e92c9 100644 --- a/setup.py +++ b/setup.py @@ -248,31 +248,42 @@ def get_extensions(): # Locating libPNG libpng = distutils.spawn.find_executable('libpng-config') - png_found = libpng is not None + pngfix = distutils.spawn.find_executable('pngfix') + png_found = libpng is not None or pngfix 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) - png_include = png_include.stdout.strip().decode('utf-8') - _, png_include = png_include.split('-I') - print('libpng include path: {0}'.format(png_include)) - image_library += [png_lib.stdout.strip().decode('utf-8')] - image_include += [png_include] - image_link_flags.append('png' if os.name != 'nt' else 'libpng') + if libpng is not None: + # Linux / Mac + 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) + png_include = png_include.stdout.strip().decode('utf-8') + _, png_include = png_include.split('-I') + print('libpng include path: {0}'.format(png_include)) + image_library += [png_lib.stdout.strip().decode('utf-8')] + image_include += [png_include] + 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 else: - print('libpng installed version is less than 1.6.0, ' - 'disabling PNG support') - png_found = False + # Windows + png_lib = os.path.join( + os.path.dirname(os.path.dirname(pngfix)), 'lib') + png_include = os.path.join(os.path.dirname( + os.path.dirname(pngfix)), 'include', 'libpng16') + image_library += [png_lib] + image_include += [png_include] image_path = os.path.join(extensions_dir, 'cpu', 'image') image_src = glob.glob(os.path.join(image_path, '*.cpp')) @@ -361,7 +372,7 @@ def run(self): # Package info packages=find_packages(exclude=('test',)), package_data={ - package_name: ['*.lib', '*.dylib', '*.so'] + package_name: ['*.dll', '*.dylib', '*.so'] }, zip_safe=False, install_requires=requirements, From b4876e341274446153048741975a051a6aa27078 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 2 Jul 2020 11:33:22 -0500 Subject: [PATCH 126/129] Fix PEP8 style issues --- setup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index dbc9d5e92c9..2eecafa7899 100644 --- a/setup.py +++ b/setup.py @@ -256,16 +256,16 @@ def get_extensions(): if libpng is not None: # Linux / Mac png_version = subprocess.run([libpng, '--version'], - stdout=subprocess.PIPE) + 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) + stdout=subprocess.PIPE) png_include = subprocess.run([libpng, '--I_opts'], - stdout=subprocess.PIPE) + stdout=subprocess.PIPE) png_include = png_include.stdout.strip().decode('utf-8') _, png_include = png_include.split('-I') print('libpng include path: {0}'.format(png_include)) From 095e285b4c64ccb288338a93344718eef322240a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 2 Jul 2020 12:10:00 -0500 Subject: [PATCH 127/129] Add linking flag on Windows --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 2eecafa7899..01276f1893d 100644 --- a/setup.py +++ b/setup.py @@ -271,7 +271,7 @@ def get_extensions(): print('libpng include path: {0}'.format(png_include)) image_library += [png_lib.stdout.strip().decode('utf-8')] image_include += [png_include] - image_link_flags.append('png' if os.name != 'nt' else 'libpng') + image_link_flags.append('png') else: print('libpng installed version is less than 1.6.0, ' 'disabling PNG support') @@ -284,6 +284,7 @@ def get_extensions(): os.path.dirname(pngfix)), 'include', 'libpng16') image_library += [png_lib] image_include += [png_include] + image_link_flags.append('libpng') image_path = os.path.join(extensions_dir, 'cpu', 'image') image_src = glob.glob(os.path.join(image_path, '*.cpp')) From ecea5ebd675be26ac0b9b5287bac13582308ddbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 2 Jul 2020 12:13:30 -0500 Subject: [PATCH 128/129] Remove parenthesis --- torchvision/io/image.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/torchvision/io/image.py b/torchvision/io/image.py index 864c76cf7d2..1ad13ed27ad 100644 --- a/torchvision/io/image.py +++ b/torchvision/io/image.py @@ -37,8 +37,7 @@ def decode_png(input): Returns: output (Tensor[image_width, image_height, 3]) """ - if not (isinstance(input, torch.Tensor) or - input.numel() == 0 or input.ndim != 1): + if not isinstance(input, torch.Tensor) or input.numel() == 0 or input.ndim != 1: raise ValueError("Expected a non empty 1-dimensional tensor.") if not input.dtype == torch.uint8: From d0fd7392bceb4aef40098fdbb9a35041cb8d0c8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Thu, 2 Jul 2020 12:14:11 -0500 Subject: [PATCH 129/129] Restore libpng during runtime --- packaging/torchvision/meta.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/packaging/torchvision/meta.yaml b/packaging/torchvision/meta.yaml index 04738f48891..b3fc2a2e9df 100644 --- a/packaging/torchvision/meta.yaml +++ b/packaging/torchvision/meta.yaml @@ -19,6 +19,7 @@ requirements: run: - python + - libpng - pillow >=4.1.1 - numpy >=1.11 {{ environ.get('CONDA_PYTORCH_CONSTRAINT') }}