Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
23 changes: 18 additions & 5 deletions torchvision/csrc/io/image/cpu/decode_jpeg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include "common_jpeg.h"
#include "exif.h"

#include <optional>

namespace vision {
namespace image {

Expand Down Expand Up @@ -141,12 +143,23 @@ torch::Tensor decode_jpeg(
struct jpeg_decompress_struct cinfo;
struct torch_jpeg_error_mgr jerr;

// NOTE: libjpeg uses setjmp/longjmp for error handling. longjmp does not
// unwind C++ stack frames, so destructors of objects created after setjmp
// won't run. We use std::optional to declare tensors before setjmp while
// deferring construction, and explicitly reset them on the error path.
std::optional<torch::Tensor> tensor;
std::optional<torch::Tensor> cmyk_line_tensor;

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)) {
// Release any tensors that may have been allocated after setjmp.
cmyk_line_tensor.reset();
tensor.reset();

/* If we get here, the JPEG code has signaled an error.
* We need to clean up the JPEG object.
*/
Expand Down Expand Up @@ -210,10 +223,10 @@ torch::Tensor decode_jpeg(
int width = cinfo.output_width;

int stride = width * channels;
auto tensor =
tensor =
torch::empty({int64_t(height), int64_t(width), channels}, torch::kU8);
auto ptr = tensor.data_ptr<uint8_t>();
torch::Tensor cmyk_line_tensor;
auto ptr = tensor->data_ptr<uint8_t>();

if (cmyk_to_rgb_or_gray) {
cmyk_line_tensor = torch::empty({int64_t(width), 4}, torch::kU8);
}
Expand All @@ -224,7 +237,7 @@ torch::Tensor decode_jpeg(
* more than one scanline at a time if that's more convenient.
*/
if (cmyk_to_rgb_or_gray) {
auto cmyk_line_ptr = cmyk_line_tensor.data_ptr<uint8_t>();
auto cmyk_line_ptr = cmyk_line_tensor->data_ptr<uint8_t>();
jpeg_read_scanlines(&cinfo, &cmyk_line_ptr, 1);

if (channels == 3) {
Expand All @@ -240,7 +253,7 @@ torch::Tensor decode_jpeg(

jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
auto output = tensor.permute({2, 0, 1});
auto output = tensor->permute({2, 0, 1});

if (apply_exif_orientation) {
return exif_orientation_transform(output, exif_orientation);
Expand Down
17 changes: 13 additions & 4 deletions torchvision/csrc/io/image/cpu/decode_png.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include "common_png.h"
#include "exif.h"

#include <optional>

namespace vision {
namespace image {

Expand Down Expand Up @@ -45,7 +47,14 @@ torch::Tensor decode_png(
auto datap = accessor.data();
auto datap_len = accessor.size(0);

// NOTE: libpng uses setjmp/longjmp for error handling. longjmp does not
// unwind C++ stack frames, so destructors of objects created after setjmp
// won't run. We use std::optional to declare tensors before setjmp while
// deferring construction, and explicitly reset them on the error path.
std::optional<torch::Tensor> tensor;

if (setjmp(png_jmpbuf(png_ptr)) != 0) {
tensor.reset();
png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
STD_TORCH_CHECK(false, "Internal error.");
}
Expand Down Expand Up @@ -197,19 +206,19 @@ torch::Tensor decode_png(

auto num_pixels_per_row = width * channels;
auto is_16_bits = bit_depth == 16;
auto tensor = torch::empty(
tensor = torch::empty(
{int64_t(height), int64_t(width), channels},
is_16_bits ? at::kUInt16 : torch::kU8);
if (is_little_endian()) {
png_set_swap(png_ptr);
}
auto t_ptr = (uint8_t*)tensor.data_ptr();
auto t_ptr = (uint8_t*)tensor->data_ptr();
for (int pass = 0; pass < number_of_passes; pass++) {
for (png_uint_32 i = 0; i < height; ++i) {
png_read_row(png_ptr, t_ptr, nullptr);
t_ptr += num_pixels_per_row * (is_16_bits ? 2 : 1);
}
t_ptr = (uint8_t*)tensor.data_ptr();
t_ptr = (uint8_t*)tensor->data_ptr();
}

int exif_orientation = -1;
Expand All @@ -219,7 +228,7 @@ torch::Tensor decode_png(

png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);

auto output = tensor.permute({2, 0, 1});
auto output = tensor->permute({2, 0, 1});
if (apply_exif_orientation) {
return exif_orientation_transform(output, exif_orientation);
}
Expand Down
13 changes: 11 additions & 2 deletions torchvision/csrc/io/image/cpu/encode_jpeg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#include <torch/headeronly/util/Exception.h>

#include <optional>

#include "common_jpeg.h"

namespace vision {
Expand Down Expand Up @@ -37,6 +39,12 @@ torch::Tensor encode_jpeg(const torch::Tensor& data, int64_t quality) {
JpegSizeType jpegSize = 0;
uint8_t* jpegBuf = nullptr;

// NOTE: libjpeg uses setjmp/longjmp for error handling. longjmp does not
// unwind C++ stack frames, so destructors of objects created after setjmp
// won't run. We use std::optional to declare tensors before setjmp while
// deferring construction, and explicitly reset them on the error path.
std::optional<torch::Tensor> input;

cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = torch_jpeg_error_exit;

Expand All @@ -45,6 +53,7 @@ torch::Tensor encode_jpeg(const torch::Tensor& data, int64_t quality) {
/* If we get here, the JPEG code has signaled an error.
* We need to clean up the JPEG object and the buffer.
*/
input.reset();
jpeg_destroy_compress(&cinfo);
if (jpegBuf != nullptr) {
free(jpegBuf);
Expand All @@ -69,7 +78,7 @@ torch::Tensor encode_jpeg(const torch::Tensor& data, int64_t quality) {
int channels = data.size(0);
int height = data.size(1);
int width = data.size(2);
auto input = data.permute({1, 2, 0}).contiguous();
input = data.permute({1, 2, 0}).contiguous();

STD_TORCH_CHECK(
channels == 1 || channels == 3,
Expand All @@ -95,7 +104,7 @@ torch::Tensor encode_jpeg(const torch::Tensor& data, int64_t quality) {
jpeg_start_compress(&cinfo, TRUE);

auto stride = width * channels;
auto ptr = input.data_ptr<uint8_t>();
auto ptr = input->data_ptr<uint8_t>();

// Encode JPEG file
while (cinfo.next_scanline < cinfo.image_height) {
Expand Down
14 changes: 12 additions & 2 deletions torchvision/csrc/io/image/cpu/encode_png.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#include <torch/headeronly/util/Exception.h>

#include <optional>

#include "common_png.h"

namespace vision {
Expand Down Expand Up @@ -78,11 +80,19 @@ torch::Tensor encode_png(const torch::Tensor& data, int64_t compression_level) {
buf_info.buffer = nullptr;
buf_info.size = 0;

// NOTE: libpng uses setjmp/longjmp for error handling. longjmp does not
// unwind C++ stack frames, so destructors of objects created after setjmp
// won't run. We use std::optional to declare tensors before setjmp while
// deferring construction, and explicitly reset them on the error path.
std::optional<torch::Tensor> input;

/* Establish the setjmp return context for my_error_exit to use. */
if (setjmp(err_ptr.setjmp_buffer)) {
/* If we get here, the PNG code has signaled an error.
* We need to clean up the PNG object and the buffer.
*/
input.reset();

if (info_ptr != nullptr) {
png_destroy_info_struct(png_write, &info_ptr);
}
Expand Down Expand Up @@ -119,7 +129,7 @@ torch::Tensor encode_png(const torch::Tensor& data, int64_t compression_level) {
int channels = data.size(0);
int height = data.size(1);
int width = data.size(2);
auto input = data.permute({1, 2, 0}).contiguous();
input = data.permute({1, 2, 0}).contiguous();

STD_TORCH_CHECK(
channels == 1 || channels == 3,
Expand Down Expand Up @@ -155,7 +165,7 @@ torch::Tensor encode_png(const torch::Tensor& data, int64_t compression_level) {
png_write_info(png_write, info_ptr);

auto stride = width * channels;
auto ptr = input.data_ptr<uint8_t>();
auto ptr = input->data_ptr<uint8_t>();

// Encode PNG file
for (int y = 0; y < height; ++y) {
Expand Down
Loading