Skip to content

PR: Read JPEG images directly #2388

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 126 commits into from
Jul 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
126 commits
Select commit Hold shift + click to select a range
2b78943
Add libpng requirement into conda recipe
andfoy Jun 8, 2020
23255aa
Try to install libjpeg-turbo
andfoy Jun 8, 2020
006ab0c
Add PNG reading capabilities
r-zenine Feb 13, 2020
7b9ec24
Remove newline
andfoy Jun 11, 2020
f97a9f0
Add image extension to compilation instructions
andfoy Jun 11, 2020
ac6d26e
Include png functions as part of the main library
andfoy Jun 11, 2020
b14912e
Update CMakeLists
andfoy Jun 11, 2020
770cea5
Detect if building on conda-build
andfoy Jun 11, 2020
0861b80
Debug
andfoy Jun 11, 2020
a42a029
More debug messages
andfoy Jun 11, 2020
b7a19ea
Print globbed libreries
andfoy Jun 12, 2020
1afde4d
Print globbed libreries
andfoy Jun 12, 2020
386fd5b
Point to correct PNG path
andfoy Jun 12, 2020
2b5c469
Remove libJPEG preventively
andfoy Jun 12, 2020
0341aa5
Debug extension loading
andfoy Jun 12, 2020
721e5e3
Link libpng explicitly
andfoy Jun 12, 2020
2186d68
Link with PNG
andfoy Jun 12, 2020
b80fb08
Add libpng requirement into conda recipe
andfoy Jun 8, 2020
3d153f0
Try to install libjpeg-turbo
andfoy Jun 8, 2020
36b0a8f
Add PNG reading capabilities
r-zenine Feb 13, 2020
9d14d9e
Remove newline
andfoy Jun 11, 2020
852a289
Add image extension to compilation instructions
andfoy Jun 11, 2020
3e86f49
Include png functions as part of the main library
andfoy Jun 11, 2020
021e767
Update CMakeLists
andfoy Jun 11, 2020
e734175
Detect if building on conda-build
andfoy Jun 11, 2020
58c6524
Debug
andfoy Jun 11, 2020
b9295c1
More debug messages
andfoy Jun 11, 2020
02fa9d9
Print globbed libreries
andfoy Jun 12, 2020
6c757d4
Print globbed libreries
andfoy Jun 12, 2020
c207eab
Point to correct PNG path
andfoy Jun 12, 2020
a1aa2e6
Remove libJPEG preventively
andfoy Jun 12, 2020
34fc7d6
Debug extension loading
andfoy Jun 12, 2020
3ed2044
Link libpng explicitly
andfoy Jun 12, 2020
eaaf658
Link with PNG
andfoy Jun 12, 2020
3edae46
Merge branch 'add_libpng' of github.com:andfoy/vision into add_libpng
andfoy Jun 12, 2020
c17202e
Install libpng on conda-based wheel distributions
andfoy Jun 12, 2020
741f855
Add -y flag
andfoy Jun 12, 2020
a46f503
Add -y flag to yum
andfoy Jun 12, 2020
4bad033
Locate LibPNG on windows conda
andfoy Jun 12, 2020
b419fc1
Remove empty else
andfoy Jun 12, 2020
83eff79
Copy libpng16.so
andfoy Jun 12, 2020
eb2846f
Copy dylib on Mac
andfoy Jun 12, 2020
3563ef3
Improve check on Windows
andfoy Jun 13, 2020
347383f
Try to install ninja using conda on windows
andfoy Jun 15, 2020
51f6b48
Use libpng on Windows
andfoy Jun 15, 2020
ad00442
Package lib on windows wheel
andfoy Jun 15, 2020
4d82283
Point library to the correct place
andfoy Jun 15, 2020
32b2207
Include binaries as part of wheel
andfoy Jun 15, 2020
9a5aefe
Copy libpng.so on linux
andfoy Jun 15, 2020
a44c3b5
Look for png.h on Windows when using conda-build
andfoy Jun 15, 2020
dfcde68
Do not skip png tests on Mac/Win
andfoy Jun 15, 2020
d8d46d6
Restore libjpeg-turbo
andfoy Jun 15, 2020
eea8552
Install jpeg-turbo on wheel distributions
andfoy Jun 15, 2020
8c7dc31
Install libjpeg-turbo from conda-forge on wheel distributions
andfoy Jun 15, 2020
ee8148a
Do not pull av on conda-build
andfoy Jun 15, 2020
059fa42
Add pillow disclaimer
andfoy Jun 15, 2020
0ed1af6
Vendors libjpeg-turbo 2.0.4
r-zenine Feb 14, 2020
11d1a7a
Merge JPEG work
r-zenine Feb 15, 2020
3bb65ba
Remove submodules
andfoy Jun 15, 2020
c334d7e
Regenerate circle config
andfoy Jun 15, 2020
5650e59
Fix style issues
andfoy Jun 15, 2020
7894836
Fix C++ style issues
andfoy Jun 15, 2020
2da51d4
More style corrections
andfoy Jun 15, 2020
78455ae
Add JPEG-turbo to linking libraries
andfoy Jun 15, 2020
a0a383d
More style corrections
andfoy Jun 15, 2020
f143e2c
More style corrections
andfoy Jun 15, 2020
95cc941
More style corrections
andfoy Jun 15, 2020
1c9270a
Install libjpeg-turbo-devel
andfoy Jun 15, 2020
ee388e5
Install libturbo-jpeg on typing pipeline
andfoy Jun 15, 2020
462ed6c
Update Circle template
andfoy Jun 15, 2020
989db57
Windows and Unix turbojpeg have the same linking name
andfoy Jun 15, 2020
6e9ad0e
Install turbojpeg-devel instead of libjpeg-turbo
andfoy Jun 15, 2020
dd43bcd
Copy TurboJPEG binaries to wheel
andfoy Jun 15, 2020
e542e48
Move test image
andfoy Jun 16, 2020
b5fa45e
Move back test image
andfoy Jun 16, 2020
3e90556
Update JPEG test path
andfoy Jun 16, 2020
4e09af0
Remove dot from extension
andfoy Jun 16, 2020
c2e9bf3
Merge branch 'master' into add_libpng
andfoy Jun 17, 2020
8ac335e
Move image functions to extension
andfoy Jun 18, 2020
2adb87e
Use stdout arg in subprocess
andfoy Jun 18, 2020
44826a7
Disable image extension if libpng or turbojpeg are not found
andfoy Jun 18, 2020
1e830ea
Append libpng stdout
andfoy Jun 18, 2020
37c889b
Prevent list appending on lists
andfoy Jun 18, 2020
3cca366
Minor path correction
andfoy Jun 18, 2020
2a6ff9f
Minor error correction
andfoy Jun 18, 2020
b89b349
Add linking flags
andfoy Jun 18, 2020
264cb74
Merge branch 'add_libpng' of github.com:andfoy/vision into add_libpng
andfoy Jun 19, 2020
a0ce4ca
Style issues correction
andfoy Jun 19, 2020
bd752aa
Address minor review corrections
andfoy Jun 22, 2020
5259757
Refactor library search
andfoy Jun 23, 2020
3013247
Restore access index
andfoy Jun 23, 2020
9d8b1b5
Fix JPEG tests
andfoy Jun 23, 2020
7c3ec51
Update libpng version in Travis
andfoy Jun 23, 2020
158eec8
Add -y flag
andfoy Jun 23, 2020
269d8e5
Remove dot
andfoy Jun 23, 2020
273dc1a
Update libpng using apt
andfoy Jun 23, 2020
cfc7c75
Check libpng version
andfoy Jun 23, 2020
d32a5f0
Change libturbojpeg binary
andfoy Jun 23, 2020
051425b
Update import
andfoy Jun 23, 2020
3bc7323
Change call
andfoy Jun 23, 2020
6b87895
Restore av in conda recipe
andfoy Jun 24, 2020
af61f94
Merge with master
andfoy Jun 29, 2020
123fd3f
Minor error correction
andfoy Jun 29, 2020
831749c
Remove unused comment in travis.yml
andfoy Jun 29, 2020
558b0cb
Update README
andfoy Jun 29, 2020
8b2f507
Fix missing links
andfoy Jun 29, 2020
b560227
Remove fixes for 16.04
andfoy Jun 30, 2020
b043aea
Merge with master
andfoy Jul 3, 2020
703b5cc
Enable JPEG support using libjpeg directly
andfoy Jul 3, 2020
a4b3ec9
Install libjpeg-turbo8 on Travis
andfoy Jul 3, 2020
ac0771b
Fix styling issues
andfoy Jul 3, 2020
9e2907b
Do not append to paths if library found in standard library locations
andfoy Jul 3, 2020
ae947f9
Add macro flag
andfoy Jul 3, 2020
f262046
Use custom error handler
andfoy Jul 4, 2020
9b8e3f9
Use 3?
andfoy Jul 4, 2020
668f57f
Use short-lived buffer
andfoy Jul 7, 2020
0648741
Return TRUE instead of true
andfoy Jul 7, 2020
1946331
Assert RuntimeError
andfoy Jul 7, 2020
1a737b1
Use .jpg extension
andfoy Jul 7, 2020
389b440
Merge remote-tracking branch 'upstream/master' into add_jpeg
andfoy Jul 7, 2020
6247832
Remove conda-forge
andfoy Jul 7, 2020
a0ebe93
Use data_ptr instead of accessor
andfoy Jul 7, 2020
10db492
Use assertTrue for jpeg verification
andfoy Jul 7, 2020
82539ca
Remove unnecessary memcpy
andfoy Jul 7, 2020
376be0c
Debug test on Windows
andfoy Jul 8, 2020
2ace447
Remove PIL from jpeg verification
andfoy Jul 8, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ jobs:
- checkout
- run:
command: |
sudo apt-get update -y
sudo apt install -y libturbojpeg-dev
pip install --user --progress-bar off numpy mypy
pip install --user --progress-bar off --pre torch -f https://download.pytorch.org/whl/nightly/cpu/torch_nightly.html
pip install --user --progress-bar off --editable .
Expand Down
2 changes: 2 additions & 0 deletions .circleci/config.yml.in
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ jobs:
- checkout
- run:
command: |
sudo apt-get update -y
sudo apt install -y libturbojpeg-dev
pip install --user --progress-bar off numpy mypy
pip install --user --progress-bar off --pre torch -f https://download.pytorch.org/whl/nightly/cpu/torch_nightly.html
pip install --user --progress-bar off --editable .
Expand Down
1 change: 1 addition & 0 deletions .circleci/unittest/linux/scripts/environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ dependencies:
- codecov
- pip
- libpng
- jpeg
- ca-certificates
- pip:
- future
Expand Down
1 change: 1 addition & 0 deletions .circleci/unittest/windows/scripts/environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ dependencies:
- codecov
- pip
- libpng
- jpeg
- ca-certificates
- pip:
- future
Expand Down
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:

before_install:
- sudo apt-get update
- sudo apt-get install -y libpng-dev
- sudo apt-get install -y libpng-dev libjpeg-turbo8-dev
- wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh;
- bash miniconda.sh -b -p $HOME/miniconda
- export PATH="$HOME/miniconda/bin:$PATH"
Expand Down
5 changes: 3 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ find_package(Python3 COMPONENTS Development)

find_package(Torch REQUIRED)
find_package(PNG REQUIRED)
find_package(JPEG REQUIRED)


file(GLOB HEADERS torchvision/csrc/*.h)
Expand All @@ -28,12 +29,12 @@ 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} ${IMAGE_SOURCES})
target_link_libraries(${PROJECT_NAME} PRIVATE ${TORCH_LIBRARIES} ${PNG_LIBRARY} Python3::Python)
target_link_libraries(${PROJECT_NAME} PRIVATE ${TORCH_LIBRARIES} ${PNG_LIBRARY} ${JPEG_LIBRARIES} Python3::Python)
# target_link_libraries(${PROJECT_NAME} PRIVATE ${PNG_LIBRARY} Python3::Python)
set_target_properties(${PROJECT_NAME} PROPERTIES EXPORT_NAME TorchVision)

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

include(GNUInstallDirs)
Expand Down
6 changes: 5 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,17 @@ 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. Make sure that it is available on the standard library locations,
* `libjpeg`_ - can be installed via conda :code:`conda install jpeg` or any of the package managers for debian-based and RHEL-based Linux distributions. `libjpeg-turbo`_ can be used as well.

**Notes:** ``libpng`` and ``libjpeg`` 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/
.. _Pillow-SIMD : https://github.com/uploadcare/pillow-simd
.. _accimage: https://github.com/pytorch/accimage
.. _libjpeg: http://ijg.org/
.. _libjpeg-turbo: https://libjpeg-turbo.org/

C++ API
=======
Expand Down
5 changes: 5 additions & 0 deletions packaging/build_wheel.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,17 @@ if [[ "$(uname)" == Darwin || "$OSTYPE" == "msys" ]]; then
if [[ "$(uname)" == Darwin ]]; then
# Include LibPNG
cp "$env_path/lib/libpng16.dylib" torchvision
# Include LibJPEG
cp "$env_path/lib/libjpeg.dylib" torchvision
else
cp "$bin_path/Library/bin/libpng16.dll" torchvision
cp "$bin_path/Library/bin/libjpeg.dll" torchvision
fi
else
# Include LibPNG
cp "/usr/lib64/libpng.so" torchvision
# Include LibJPEG
cp "/usr/lib64/libjpeg.so" torchvision
fi

if [[ "$OSTYPE" == "msys" ]]; then
Expand Down
4 changes: 2 additions & 2 deletions packaging/pkg_helpers.bash
Original file line number Diff line number Diff line change
Expand Up @@ -171,10 +171,10 @@ setup_wheel_python() {
conda create -yn "env$PYTHON_VERSION" python="$PYTHON_VERSION"
conda activate "env$PYTHON_VERSION"
# Install libpng from Anaconda (defaults)
conda install libpng -y
conda install libpng jpeg -y
else
# Install native CentOS libPNG
yum install -y libpng-devel
yum install -y libpng-devel libjpeg-turbo-devel
case "$PYTHON_VERSION" in
2.7)
if [[ -n "$UNICODE_ABI" ]]; then
Expand Down
2 changes: 2 additions & 0 deletions packaging/torchvision/meta.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ requirements:
build:
- {{ compiler('c') }} # [win]
- libpng
- jpeg

host:
- python
Expand All @@ -20,6 +21,7 @@ requirements:
run:
- python
- libpng
- jpeg
- pillow >=4.1.1
- numpy >=1.11
{{ environ.get('CONDA_PYTORCH_CONSTRAINT') }}
Expand Down
15 changes: 14 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,10 +327,23 @@ def get_extensions():
image_include += [png_include]
image_link_flags.append('libpng')

# Locating libjpeg
(jpeg_found, jpeg_conda,
jpeg_include, jpeg_lib) = find_library('jpeglib', vision_include)

print('JPEG found: {0}'.format(jpeg_found))
image_macros += [('JPEG_FOUND', str(int(jpeg_found)))]
if jpeg_found:
print('Building torchvision with JPEG image support')
image_link_flags.append('jpeg')
if jpeg_conda:
image_library += [jpeg_lib]
image_include += [jpeg_include]

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

if png_found:
if png_found or jpeg_found:
ext_modules.append(extension(
'torchvision.image',
image_src,
Expand Down
Binary file added test/assets/grace_hopper_517x606.pth
Binary file not shown.
24 changes: 23 additions & 1 deletion test/test_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import torch
import torchvision
from PIL import Image
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_ROOT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "assets")
Expand All @@ -22,6 +22,28 @@ 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.load(img_path.replace('jpg', 'pth'))
img_ljpeg = read_jpeg(img_path)
self.assertTrue(img_ljpeg.equal(img_pil))

def test_decode_jpeg(self):
for img_path in get_images(IMAGE_ROOT, ".jpg"):
img_pil = torch.load(img_path.replace('jpg', 'pth'))
size = os.path.getsize(img_path)
img_ljpeg = decode_jpeg(torch.from_file(img_path, dtype=torch.uint8, size=size))
self.assertTrue(img_ljpeg.equal(img_pil))

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.assertRaises(RuntimeError):
decode_jpeg(torch.empty((100), dtype=torch.uint8))

def test_read_png(self):
# Check across .png
for img_path in get_images(IMAGE_DIR, ".png"):
Expand Down
5 changes: 3 additions & 2 deletions torchvision/csrc/cpu/image/image.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ PyMODINIT_FUNC PyInit_image(void) {
}
#endif

static auto registry =
torch::RegisterOperators().op("image::decode_png", &decodePNG);
static auto registry = torch::RegisterOperators()
.op("image::decode_png", &decodePNG)
.op("image::decode_jpeg", &decodeJPEG);
1 change: 1 addition & 0 deletions torchvision/csrc/cpu/image/image.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
// Comment
#include <torch/script.h>
#include <torch/torch.h>
#include "readjpeg_cpu.h"
#include "readpng_cpu.h"
140 changes: 140 additions & 0 deletions torchvision/csrc/cpu/image/readjpeg_cpu.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
#include "readjpeg_cpu.h"

#include <ATen/ATen.h>
#include <setjmp.h>
#include <string>

#if !JPEG_FOUND

torch::Tensor decodeJPEG(const torch::Tensor& data) {
AT_ERROR("decodeJPEG: torchvision not compiled with libjpeg support");
}

#else
#include <jpeglib.h>

const static JOCTET EOI_BUFFER[1] = {JPEG_EOI};
char jpegLastErrorMsg[JMSG_LENGTH_MAX];
Comment on lines +16 to +17
Copy link
Member

Choose a reason for hiding this comment

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

for a next PR: it would be great if there was a way of avoiding having those globals.


struct torch_jpeg_error_mgr {
struct jpeg_error_mgr pub; /* "public" fields */
jmp_buf setjmp_buffer; /* for return to caller */
};

typedef struct torch_jpeg_error_mgr* torch_jpeg_error_ptr;

void torch_jpeg_error_exit(j_common_ptr cinfo) {
/* cinfo->err really points to a torch_jpeg_error_mgr struct, so coerce
* pointer */
torch_jpeg_error_ptr myerr = (torch_jpeg_error_ptr)cinfo->err;

/* Always display the message. */
/* We could postpone this until after returning, if we chose. */
// (*cinfo->err->output_message)(cinfo);
/* Create the message */
(*(cinfo->err->format_message))(cinfo, jpegLastErrorMsg);

/* Return control to the setjmp point */
longjmp(myerr->setjmp_buffer, 1);
}

struct torch_jpeg_mgr {
struct jpeg_source_mgr pub;
const JOCTET* data;
size_t len;
};

static void torch_jpeg_init_source(j_decompress_ptr cinfo) {}

static boolean torch_jpeg_fill_input_buffer(j_decompress_ptr cinfo) {
torch_jpeg_mgr* src = (torch_jpeg_mgr*)cinfo->src;
// No more data. Probably an incomplete image; just output EOI.
src->pub.next_input_byte = EOI_BUFFER;
src->pub.bytes_in_buffer = 1;
return TRUE;
}

static void torch_jpeg_skip_input_data(j_decompress_ptr cinfo, long num_bytes) {
torch_jpeg_mgr* src = (torch_jpeg_mgr*)cinfo->src;
if (src->pub.bytes_in_buffer < num_bytes) {
// Skipping over all of remaining data; output EOI.
src->pub.next_input_byte = EOI_BUFFER;
src->pub.bytes_in_buffer = 1;
} else {
// Skipping over only some of the remaining data.
src->pub.next_input_byte += num_bytes;
src->pub.bytes_in_buffer -= num_bytes;
}
}

static void torch_jpeg_term_source(j_decompress_ptr cinfo) {}

static void torch_jpeg_set_source_mgr(
j_decompress_ptr cinfo,
const unsigned char* data,
size_t len) {
torch_jpeg_mgr* src;
if (cinfo->src == 0) { // if this is first time; allocate memory
cinfo->src = (struct jpeg_source_mgr*)(*cinfo->mem->alloc_small)(
(j_common_ptr)cinfo, JPOOL_PERMANENT, sizeof(torch_jpeg_mgr));
}
src = (torch_jpeg_mgr*)cinfo->src;
src->pub.init_source = torch_jpeg_init_source;
src->pub.fill_input_buffer = torch_jpeg_fill_input_buffer;
src->pub.skip_input_data = torch_jpeg_skip_input_data;
src->pub.resync_to_restart = jpeg_resync_to_restart; // default
src->pub.term_source = torch_jpeg_term_source;
// fill the buffers
src->data = (const JOCTET*)data;
src->len = len;
src->pub.bytes_in_buffer = len;
src->pub.next_input_byte = src->data;
}

torch::Tensor decodeJPEG(const torch::Tensor& data) {
struct jpeg_decompress_struct cinfo;
struct torch_jpeg_error_mgr jerr;

auto datap = data.data_ptr<uint8_t>();
// Setup decompression structure
cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = torch_jpeg_error_exit;
/* Establish the setjmp return context for my_error_exit to use. */
if (setjmp(jerr.setjmp_buffer)) {
/* If we get here, the JPEG code has signaled an error.
* We need to clean up the JPEG object.
*/
jpeg_destroy_decompress(&cinfo);
AT_ERROR(jpegLastErrorMsg);
}

jpeg_create_decompress(&cinfo);
torch_jpeg_set_source_mgr(&cinfo, datap, data.numel());

// read info from header.
jpeg_read_header(&cinfo, TRUE);
jpeg_start_decompress(&cinfo);

int height = cinfo.output_height;
int width = cinfo.output_width;
int components = cinfo.output_components;

auto stride = width * components;
auto tensor = torch::empty(
{int64_t(height), int64_t(width), int64_t(components)}, torch::kU8);
auto ptr = tensor.data_ptr<uint8_t>();
while (cinfo.output_scanline < cinfo.output_height) {
/* jpeg_read_scanlines expects an array of pointers to scanlines.
* Here the array is only one element long, but you could ask for
* more than one scanline at a time if that's more convenient.
*/
jpeg_read_scanlines(&cinfo, &ptr, 1);
ptr += stride;
}

jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
return tensor;
}

#endif // JPEG_FOUND
5 changes: 5 additions & 0 deletions torchvision/csrc/cpu/image/readjpeg_cpu.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#pragma once

#include <torch/torch.h>

torch::Tensor decodeJPEG(const torch::Tensor& data);
2 changes: 1 addition & 1 deletion torchvision/io/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,5 @@
"_read_video_clip_from_memory",
"_read_video_meta_data",
"VideoMetaData",
"Timebase",
"Timebase"
]
Loading