Skip to content

Commit 7e94649

Browse files
committed
Add support for png decoding on linux
- Pipelines are timing out, increasing to 30 minutes. - Vendors zlib and add's libpng as a dependency. - Ship libpng.so and zlib.so with package on linux
1 parent 6c2cda6 commit 7e94649

File tree

14 files changed

+293
-11
lines changed

14 files changed

+293
-11
lines changed

.circleci/config.yml

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ commands:
2121
description: "checkout merge branch"
2222
steps:
2323
- checkout
24+
- run:
25+
name: initialize submodules
26+
command: git submodule update --init --recursive
2427
# - run:
2528
# name: Checkout merge branch
2629
# command: |
@@ -83,7 +86,9 @@ jobs:
8386
resource_class: 2xlarge+
8487
steps:
8588
- checkout_merge
86-
- run: packaging/build_wheel.sh
89+
- run:
90+
command: packaging/build_wheel.sh
91+
no_output_timeout: 30m
8792
- store_artifacts:
8893
path: dist
8994
- persist_to_workspace:
@@ -98,7 +103,10 @@ jobs:
98103
resource_class: 2xlarge+
99104
steps:
100105
- checkout_merge
101-
- run: packaging/build_conda.sh
106+
- run:
107+
command: packaging/build_conda.sh
108+
no_output_timeout: 30m
109+
102110
- store_artifacts:
103111
path: /opt/conda/conda-bld/linux-64
104112
- persist_to_workspace:
@@ -167,6 +175,7 @@ jobs:
167175
168176
- run:
169177
name: Build and run tests
178+
no_output_timeout: 30m
170179
command: |
171180
set -e
172181
@@ -191,6 +200,7 @@ jobs:
191200
conda activate base
192201
conda install -yq conda-build "conda-package-handling!=1.5.0"
193202
bash packaging/build_conda.sh
203+
no_output_timeout: 30m
194204
shell: powershell.exe
195205
- store_test_results:
196206
path: build_results/
@@ -207,6 +217,7 @@ jobs:
207217
conda activate base
208218
conda install -yq conda-build "conda-package-handling!=1.5.0"
209219
bash packaging/build_conda.sh
220+
no_output_timeout: 30m
210221
shell: powershell.exe
211222

212223
binary_macos_wheel:
@@ -219,6 +230,7 @@ jobs:
219230
# Cannot easily deduplicate this as source'ing activate
220231
# will set environment variables which we need to propagate
221232
# to build_wheel.sh
233+
no_output_timeout: 30m
222234
command: |
223235
curl -o conda.sh https://repo.anaconda.com/miniconda/Miniconda3-latest-MacOSX-x86_64.sh
224236
sh conda.sh -b
@@ -238,6 +250,7 @@ jobs:
238250
steps:
239251
- checkout_merge
240252
- run:
253+
no_output_timeout: 30m
241254
command: |
242255
curl -o conda.sh https://repo.anaconda.com/miniconda/Miniconda3-latest-MacOSX-x86_64.sh
243256
sh conda.sh -b

.circleci/config.yml.in

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ commands:
2121
description: "checkout merge branch"
2222
steps:
2323
- checkout
24+
- run:
25+
name: initialize submodules
26+
command: git submodule update --init --recursive
2427
# - run:
2528
# name: Checkout merge branch
2629
# command: |
@@ -83,7 +86,9 @@ jobs:
8386
resource_class: 2xlarge+
8487
steps:
8588
- checkout_merge
86-
- run: packaging/build_wheel.sh
89+
- run:
90+
command: packaging/build_wheel.sh
91+
no_output_timeout: 30m
8792
- store_artifacts:
8893
path: dist
8994
- persist_to_workspace:
@@ -98,7 +103,10 @@ jobs:
98103
resource_class: 2xlarge+
99104
steps:
100105
- checkout_merge
101-
- run: packaging/build_conda.sh
106+
- run:
107+
command: packaging/build_conda.sh
108+
no_output_timeout: 30m
109+
102110
- store_artifacts:
103111
path: /opt/conda/conda-bld/linux-64
104112
- persist_to_workspace:
@@ -167,6 +175,7 @@ jobs:
167175

168176
- run:
169177
name: Build and run tests
178+
no_output_timeout: 30m
170179
command: |
171180
set -e
172181

@@ -191,6 +200,7 @@ jobs:
191200
conda activate base
192201
conda install -yq conda-build "conda-package-handling!=1.5.0"
193202
bash packaging/build_conda.sh
203+
no_output_timeout: 30m
194204
shell: powershell.exe
195205
- store_test_results:
196206
path: build_results/
@@ -207,6 +217,7 @@ jobs:
207217
conda activate base
208218
conda install -yq conda-build "conda-package-handling!=1.5.0"
209219
bash packaging/build_conda.sh
220+
no_output_timeout: 30m
210221
shell: powershell.exe
211222

212223
binary_macos_wheel:
@@ -219,6 +230,7 @@ jobs:
219230
# Cannot easily deduplicate this as source'ing activate
220231
# will set environment variables which we need to propagate
221232
# to build_wheel.sh
233+
no_output_timeout: 30m
222234
command: |
223235
curl -o conda.sh https://repo.anaconda.com/miniconda/Miniconda3-latest-MacOSX-x86_64.sh
224236
sh conda.sh -b
@@ -238,6 +250,7 @@ jobs:
238250
steps:
239251
- checkout_merge
240252
- run:
253+
no_output_timeout: 30m
241254
command: |
242255
curl -o conda.sh https://repo.anaconda.com/miniconda/Miniconda3-latest-MacOSX-x86_64.sh
243256
sh conda.sh -b

.gitmodules

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[submodule "third_party/libpng"]
2+
path = third_party/libpng
3+
url = https://github.com/glennrp/libpng
4+
[submodule "third_party/zlib"]
5+
path = third_party/zlib
6+
url = https://github.com/madler/zlib

.travis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,8 @@ install:
6767
cd -
6868
6969
script:
70-
- pytest --cov-config .coveragerc --cov torchvision --cov $TV_INSTALL_PATH -k 'not TestVideoReader and not TestVideoTransforms' test
71-
- pytest test/test_hub.py
70+
- travis_wait 60 pytest --cov-config .coveragerc --cov torchvision --cov $TV_INSTALL_PATH -k 'not TestVideoReader and not TestVideoTransforms' test
71+
- travis_wait 60 pytest test/test_hub.py
7272

7373
after_success:
7474
# Necessary to run coverage combine to rewrite paths from

CMakeLists.txt

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ if(WITH_CUDA)
1010
add_definitions(-D__CUDA_NO_HALF_OPERATORS__)
1111
endif()
1212

13+
if(Unix)
14+
add_subdirectory("third_party/libpng")
15+
endif()
16+
1317
find_package(Python3 COMPONENTS Development)
1418
find_package(Torch REQUIRED)
1519

@@ -21,8 +25,17 @@ endif()
2125
file(GLOB MODELS_HEADERS torchvision/csrc/models/*.h)
2226
file(GLOB MODELS_SOURCES torchvision/csrc/models/*.h torchvision/csrc/models/*.cpp)
2327

24-
add_library(${PROJECT_NAME} SHARED ${MODELS_SOURCES} ${OPERATOR_SOURCES})
25-
target_link_libraries(${PROJECT_NAME} PRIVATE ${TORCH_LIBRARIES} Python3::Python)
28+
file(GLOB IMAGE_HEADERS torchvision/csrc/image.h)
29+
file(GLOB IMAGE_SOURCES torchvision/csrc/cpu/image/*.h torchvision/csrc/cpu/image/*.cpp)
30+
31+
if(Unix)
32+
add_library(${PROJECT_NAME} SHARED ${MODELS_SOURCES} ${OPERATOR_SOURCES} {IMAGE_SOURCES})
33+
target_link_libraries(${PROJECT_NAME} PRIVATE ${TORCH_LIBRARIES} Python3::Python "${PNG_LIBRARIES}")
34+
else()
35+
add_library(${PROJECT_NAME} SHARED ${MODELS_SOURCES} ${OPERATOR_SOURCES})
36+
target_link_libraries(${PROJECT_NAME} PRIVATE ${TORCH_LIBRARIES} Python3::Python)
37+
endif()
38+
2639
set_target_properties(${PROJECT_NAME} PROPERTIES EXPORT_NAME TorchVision)
2740

2841
target_include_directories(${PROJECT_NAME} INTERFACE

setup.py

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import subprocess
99
import distutils.command.clean
1010
import distutils.spawn
11+
import multiprocessing
1112
import glob
1213
import shutil
1314

@@ -83,9 +84,20 @@ def get_extensions():
8384

8485
main_file = glob.glob(os.path.join(extensions_dir, '*.cpp'))
8586
source_cpu = glob.glob(os.path.join(extensions_dir, 'cpu', '*.cpp'))
87+
source_image_cpu = glob.glob(os.path.join(extensions_dir, 'cpu', 'image', '*.cpp'))
8688
source_cuda = glob.glob(os.path.join(extensions_dir, 'cuda', '*.cu'))
8789

8890
sources = main_file + source_cpu
91+
92+
libraries = []
93+
extra_compile_args = {}
94+
third_party_search_directories = []
95+
96+
if sys.platform.startswith('linux'):
97+
sources = sources + source_image_cpu
98+
libraries.append('png')
99+
third_party_search_directories.append(os.path.join(cwd, "third_party/libpng"))
100+
89101
extension = CppExtension
90102

91103
compile_cpp_tests = os.getenv('WITH_CPP_MODELS_TEST', '0') == '1'
@@ -142,9 +154,12 @@ def get_extensions():
142154
extension(
143155
'torchvision._C',
144156
sources,
145-
include_dirs=include_dirs,
157+
libraries=libraries,
158+
library_dirs=third_party_search_directories,
159+
include_dirs=include_dirs + third_party_search_directories,
146160
define_macros=define_macros,
147161
extra_compile_args=extra_compile_args,
162+
runtime_library_dirs=['lib']
148163
)
149164
]
150165
if compile_cpp_tests:
@@ -197,6 +212,43 @@ def run(self):
197212
distutils.command.clean.clean.run(self)
198213

199214

215+
def throw_of_failure(command):
216+
ret = os.system(command)
217+
if ret != 0:
218+
raise Exception("{} failed".format(command))
219+
220+
221+
def build_deps():
222+
this_dir = os.path.dirname(os.path.abspath(__file__))
223+
if sys.platform.startswith('linux'):
224+
cpu_count = multiprocessing.cpu_count()
225+
os.chdir("third_party/zlib/")
226+
throw_of_failure('cmake .')
227+
throw_of_failure("cmake --build . -- -j {}".format(cpu_count))
228+
os.chdir(this_dir)
229+
230+
zlib_path = os.path.join(this_dir, "third_party/zlib")
231+
libpng_cmake_options = "-DPNG_BUILD_ZLIB=ON -DPNG_STATIC=OFF -DZLIB_INCLUDE_DIR:PATH={zlib_path} -DZLIB_LIBRARY:FILEPATH={zlib_path}/libz.so".format(zlib_path=zlib_path)
232+
os.chdir("third_party/libpng/")
233+
os.system('cmake {} .'.format(libpng_cmake_options))
234+
throw_of_failure("cmake --build . -- -j {}".format(cpu_count))
235+
os.chdir(this_dir)
236+
237+
238+
def build_ext_with_dependencies(self):
239+
build_deps()
240+
return BuildExtension.with_options(no_python_abi_suffix=True)(self)
241+
242+
243+
data_files = []
244+
if sys.platform.startswith('linux'):
245+
data_files = [
246+
('torchvision/lib', [
247+
'third_party/zlib/libz.so',
248+
'third_party/libpng/libpng.so'])
249+
]
250+
251+
200252
setup(
201253
# Metadata
202254
name=package_name,
@@ -218,7 +270,8 @@ def run(self):
218270
},
219271
ext_modules=get_extensions(),
220272
cmdclass={
221-
'build_ext': BuildExtension.with_options(no_python_abi_suffix=True),
273+
'build_ext': build_ext_with_dependencies,
222274
'clean': clean,
223-
}
275+
},
276+
data_files=data_files
224277
)

test/test_image.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import os
2+
import unittest
3+
import sys
4+
5+
import torch
6+
from PIL import Image
7+
if sys.platform.startswith('linux'):
8+
from torchvision.io.image import read_png, decode_png
9+
import numpy as np
10+
11+
IMAGE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "assets", "fakedata", "imagefolder")
12+
13+
14+
def get_images(directory, img_ext):
15+
assert os.path.isdir(directory)
16+
for root, dir, files in os.walk(directory):
17+
for fl in files:
18+
_, ext = os.path.splitext(fl)
19+
if ext == img_ext:
20+
yield os.path.join(root, fl)
21+
22+
23+
class ImageTester(unittest.TestCase):
24+
@unittest.skipUnless(sys.platform.startswith("linux"), "Support only available on linux for now.")
25+
def test_read_png(self):
26+
for img_path in get_images(IMAGE_DIR, "png"):
27+
img_pil = torch.from_numpy(np.array(Image.open(img_path)))
28+
img_lpng = read_png(img_path)
29+
self.assertEqual(img_lpng, img_pil)
30+
31+
@unittest.skipUnless(sys.platform.startswith("linux"), "Support only available on linux for now.")
32+
def test_decode_png(self):
33+
for img_path in get_images(IMAGE_DIR, "png"):
34+
img_pil = torch.from_numpy(np.array(Image.open(img_path)))
35+
size = os.path.getsize(img_path)
36+
img_lpng = decode_png(torch.from_file(img_path, dtype=torch.uint8, size=size))
37+
self.assertEqual(img_lpng, img_pil)
38+
39+
self.assertEqual(decode_png(torch.empty()), torch.empty())
40+
self.assertEqual(decode_png(torch.randint(3, 5, (300,))), torch.empty())
41+
42+
43+
if __name__ == '__main__':
44+
unittest.main()

third_party/libpng

Submodule libpng added at a40189c

third_party/zlib

Submodule zlib added at cacf7f1

0 commit comments

Comments
 (0)