From 29e1b68f51d3802b677230b4acdacaaa041ca9e7 Mon Sep 17 00:00:00 2001 From: Nicolas Hug Date: Tue, 19 Oct 2021 18:38:32 +0100 Subject: [PATCH 1/7] WIP --- torchvision/csrc/io/image/cpu/decode_png.cpp | 77 ++++++++++++++++---- 1 file changed, 64 insertions(+), 13 deletions(-) diff --git a/torchvision/csrc/io/image/cpu/decode_png.cpp b/torchvision/csrc/io/image/cpu/decode_png.cpp index ea38272c978..19ff7d6cdd7 100644 --- a/torchvision/csrc/io/image/cpu/decode_png.cpp +++ b/torchvision/csrc/io/image/cpu/decode_png.cpp @@ -72,11 +72,6 @@ torch::Tensor decode_png(const torch::Tensor& data, ImageReadMode mode) { TORCH_CHECK(retval == 1, "Could read image metadata from content.") } - if (bit_depth > 8) { - png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); - TORCH_CHECK(false, "At most 8-bit PNG images are supported currently.") - } - int channels = png_get_channels(png_ptr, info_ptr); if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) @@ -168,15 +163,71 @@ torch::Tensor decode_png(const torch::Tensor& data, ImageReadMode mode) { png_read_update_info(png_ptr, info_ptr); } - auto tensor = - torch::empty({int64_t(height), int64_t(width), channels}, torch::kU8); - auto ptr = tensor.accessor().data(); - for (int pass = 0; pass < number_of_passes; pass++) { - for (png_uint_32 i = 0; i < height; ++i) { - png_read_row(png_ptr, ptr, nullptr); - ptr += width * channels; + auto tensor = torch::empty( + {int64_t(height), int64_t(width), channels}, + bit_depth <= 8 ? torch::kU8 : torch::kI32); + + if (bit_depth <= 8) { + auto ptr = tensor.accessor().data(); + auto num_bytes_per_row = width * channels; + for (int pass = 0; pass < number_of_passes; pass++) { + for (png_uint_32 i = 0; i < height; ++i) { + png_read_row(png_ptr, ptr, nullptr); + ptr += num_bytes_per_row; + } + ptr = tensor.accessor().data(); + } + } else { + // We're reading a 16bits png, but pytorch doesn't support uint16. So we + // read the whole image in an int32 tensor, and we'll convert it later to + // a uint8 + auto ptr = (uint8_t*)tensor.accessor().data(); + auto num_bytes_per_row = width * channels * 4; + for (int pass = 0; pass < number_of_passes; pass++) { + for (png_uint_32 i = 0; i < height; ++i) { + png_read_row(png_ptr, ptr, nullptr); + + // We have a row that looks like this + // [a, b, c, d, ., ., ., .] + // where each letter is one byte - and so each consecutive pair of + // letters corresponds to an actual 16bits pixel value. Half of the + // cells on the right are empty because png_read_row() will read + // num_pixels 16bits values, while our row has num_pixels 32bits cells + // (and 32 = 2 * 16...) + // + // We need to re-organize the row for the int32 entries to be properly + // interpreted with the same value as their 16bits counter-parts. On a + // little-endian arch, we need a mapping like this: + // [b, a, 0, 0, d, c, 0, 0] + // ^^^^ ^^^^ + // uint16 uint16 + // ^^^^^^^^^^ ^^^^^^^^^^ + // int32 int32 + // TODO: support big endian???? + for (int j = num_bytes_per_row - 1; j >= 0; j--) { + switch (j % 4) { + case 0: + ptr[j] = ptr[j / 2 + 1]; + break; + case 1: + ptr[j] = ptr[j / 2]; + break; + case 2: + ptr[j] = 0; + break; + case 3: + ptr[j] = 0; + break; + default: + break; + } + } + ptr += num_bytes_per_row; + } + ptr = (uint8_t*)tensor.accessor().data(); } - ptr = tensor.accessor().data(); + // now let's pretend we had a uint8 tensor all along Date: Wed, 20 Oct 2021 16:30:41 +0100 Subject: [PATCH 2/7] cleaner code --- torchvision/csrc/io/image/cpu/decode_png.cpp | 76 +++++++------------- 1 file changed, 26 insertions(+), 50 deletions(-) diff --git a/torchvision/csrc/io/image/cpu/decode_png.cpp b/torchvision/csrc/io/image/cpu/decode_png.cpp index 19ff7d6cdd7..02181e32c26 100644 --- a/torchvision/csrc/io/image/cpu/decode_png.cpp +++ b/torchvision/csrc/io/image/cpu/decode_png.cpp @@ -11,6 +11,11 @@ torch::Tensor decode_png(const torch::Tensor& data, ImageReadMode mode) { } #else +bool is_little_endian() { + uint32_t x = 1; + return *(uint8_t*)&x; +} + torch::Tensor decode_png(const torch::Tensor& data, ImageReadMode mode) { // Check that the input tensor dtype is uint8 TORCH_CHECK(data.dtype() == torch::kU8, "Expected a torch.uint8 tensor"); @@ -163,71 +168,42 @@ torch::Tensor decode_png(const torch::Tensor& data, ImageReadMode mode) { png_read_update_info(png_ptr, info_ptr); } + auto num_pixels_per_row = width * channels; auto tensor = torch::empty( {int64_t(height), int64_t(width), channels}, bit_depth <= 8 ? torch::kU8 : torch::kI32); if (bit_depth <= 8) { - auto ptr = tensor.accessor().data(); - auto num_bytes_per_row = width * channels; + auto t_ptr = tensor.accessor().data(); for (int pass = 0; pass < number_of_passes; pass++) { for (png_uint_32 i = 0; i < height; ++i) { - png_read_row(png_ptr, ptr, nullptr); - ptr += num_bytes_per_row; + png_read_row(png_ptr, t_ptr, nullptr); + t_ptr += num_pixels_per_row; } - ptr = tensor.accessor().data(); + t_ptr = tensor.accessor().data(); } } else { - // We're reading a 16bits png, but pytorch doesn't support uint16. So we - // read the whole image in an int32 tensor, and we'll convert it later to - // a uint8 - auto ptr = (uint8_t*)tensor.accessor().data(); - auto num_bytes_per_row = width * channels * 4; + // We're reading a 16bits png, but pytorch doesn't support uint16. + // So we read each row in a 16bits tmp_buffer which we then cast into + // a int32 tensor instead. + if (is_little_endian()) { + png_set_swap(png_ptr); + } + int32_t* t_ptr = tensor.accessor().data(); + uint16_t* tmp_buffer = + (uint16_t*)malloc(num_pixels_per_row * sizeof(uint16_t)); + for (int pass = 0; pass < number_of_passes; pass++) { for (png_uint_32 i = 0; i < height; ++i) { - png_read_row(png_ptr, ptr, nullptr); - - // We have a row that looks like this - // [a, b, c, d, ., ., ., .] - // where each letter is one byte - and so each consecutive pair of - // letters corresponds to an actual 16bits pixel value. Half of the - // cells on the right are empty because png_read_row() will read - // num_pixels 16bits values, while our row has num_pixels 32bits cells - // (and 32 = 2 * 16...) - // - // We need to re-organize the row for the int32 entries to be properly - // interpreted with the same value as their 16bits counter-parts. On a - // little-endian arch, we need a mapping like this: - // [b, a, 0, 0, d, c, 0, 0] - // ^^^^ ^^^^ - // uint16 uint16 - // ^^^^^^^^^^ ^^^^^^^^^^ - // int32 int32 - // TODO: support big endian???? - for (int j = num_bytes_per_row - 1; j >= 0; j--) { - switch (j % 4) { - case 0: - ptr[j] = ptr[j / 2 + 1]; - break; - case 1: - ptr[j] = ptr[j / 2]; - break; - case 2: - ptr[j] = 0; - break; - case 3: - ptr[j] = 0; - break; - default: - break; - } + png_read_row(png_ptr, (uint8_t*)tmp_buffer, nullptr); + // Now we copy the uint16 values into the int32 tensor. + for (size_t j = 0; j < num_pixels_per_row; ++j) { + t_ptr[j] = (int32_t)tmp_buffer[j]; } - ptr += num_bytes_per_row; + t_ptr += num_pixels_per_row; } - ptr = (uint8_t*)tensor.accessor().data(); + t_ptr = tensor.accessor().data(); } - // now let's pretend we had a uint8 tensor all along Date: Wed, 20 Oct 2021 18:33:47 +0100 Subject: [PATCH 3/7] Add tests --- test/assets/fakedata/logos/rgb_pytorch16.png | Bin 0 -> 69822 bytes .../fakedata/logos/rgbalpha_pytorch16.png | Bin 0 -> 90497 bytes test/test_image.py | 4 ++++ 3 files changed, 4 insertions(+) create mode 100644 test/assets/fakedata/logos/rgb_pytorch16.png create mode 100644 test/assets/fakedata/logos/rgbalpha_pytorch16.png diff --git a/test/assets/fakedata/logos/rgb_pytorch16.png b/test/assets/fakedata/logos/rgb_pytorch16.png new file mode 100644 index 0000000000000000000000000000000000000000..b5e9e35d9899796c8e07c130365e0852398c0464 GIT binary patch literal 69822 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 literal 0 HcmV?d00001 diff --git a/test/assets/fakedata/logos/rgbalpha_pytorch16.png b/test/assets/fakedata/logos/rgbalpha_pytorch16.png new file mode 100644 index 0000000000000000000000000000000000000000..df1db4d6354f26f9f3d0bd38c89070655a5eff7d GIT binary patch literal 90497 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 literal 0 HcmV?d00001 diff --git a/test/test_image.py b/test/test_image.py index 9c6a73b8362..7ce76d85625 100644 --- a/test/test_image.py +++ b/test/test_image.py @@ -168,6 +168,10 @@ def test_decode_png(img_path, pil_mode, mode): # TODO: remove once fix is released in PIL. Should be > 8.3.1. img_lpng, img_pil = img_lpng[0], img_pil[0] + if "16" in img_path: + # PIL converts 16 bits pngs in uint8 + img_lpng = torch.round(img_lpng / (2 ** 16 - 1) * 255).to(torch.uint8) + torch.testing.assert_close(img_lpng, img_pil, atol=tol, rtol=0) From e1568d344a710b7a346a623894a9201d0a9cb6bd Mon Sep 17 00:00:00 2001 From: Nicolas Hug Date: Wed, 20 Oct 2021 18:39:43 +0100 Subject: [PATCH 4/7] Add docs --- torchvision/io/image.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/torchvision/io/image.py b/torchvision/io/image.py index 2ba1e9eddd9..9c1496930ac 100644 --- a/torchvision/io/image.py +++ b/torchvision/io/image.py @@ -61,7 +61,12 @@ def decode_png(input: torch.Tensor, mode: ImageReadMode = ImageReadMode.UNCHANGE """ Decodes a PNG image into a 3 dimensional RGB Tensor. Optionally converts the image to the desired format. - The values of the output tensor are uint8 between 0 and 255. + The values of the output tensor are uint8 between 0 and 255, except for + 16-bits pngs which are int32 tensors. + + .. warning:: + Should pytorch ever support the uint16 dtype natively, the dtype of the + output tensor of 16-bits pngs will be updated from int32 to uint16. Args: input (Tensor[1]): a one dimensional uint8 tensor containing @@ -188,7 +193,8 @@ def decode_image(input: torch.Tensor, mode: ImageReadMode = ImageReadMode.UNCHAN operation to decode the image into a 3 dimensional RGB Tensor. Optionally converts the image to the desired format. - The values of the output tensor are uint8 between 0 and 255. + The values of the output tensor are uint8 between 0 and 255, except for + 16-bits pngs which are int32 tensors. Args: input (Tensor): a one dimensional uint8 tensor containing the raw bytes of the @@ -209,7 +215,8 @@ def read_image(path: str, mode: ImageReadMode = ImageReadMode.UNCHANGED) -> torc """ Reads a JPEG or PNG image into a 3 dimensional RGB Tensor. Optionally converts the image to the desired format. - The values of the output tensor are uint8 between 0 and 255. + The values of the output tensor are uint8 between 0 and 255, except for + 16-bits pngs which are int32 tensors. Args: path (str): path of the JPEG or PNG image. From 7b14f34955e37c0b0062a01364855adfcdf7c260 Mon Sep 17 00:00:00 2001 From: Nicolas Hug Date: Wed, 20 Oct 2021 18:41:21 +0100 Subject: [PATCH 5/7] Assert dtype --- test/test_image.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test_image.py b/test/test_image.py index 7ce76d85625..7cd74fc915c 100644 --- a/test/test_image.py +++ b/test/test_image.py @@ -170,6 +170,7 @@ def test_decode_png(img_path, pil_mode, mode): if "16" in img_path: # PIL converts 16 bits pngs in uint8 + assert img_lpng.dtype == torch.int32 img_lpng = torch.round(img_lpng / (2 ** 16 - 1) * 255).to(torch.uint8) torch.testing.assert_close(img_lpng, img_pil, atol=tol, rtol=0) From 9cd72a6d4321381f9de2d70be23f5da7bdf721d1 Mon Sep 17 00:00:00 2001 From: Nicolas Hug Date: Wed, 20 Oct 2021 18:53:54 +0100 Subject: [PATCH 6/7] put back check --- torchvision/csrc/io/image/cpu/decode_png.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/torchvision/csrc/io/image/cpu/decode_png.cpp b/torchvision/csrc/io/image/cpu/decode_png.cpp index 02181e32c26..3408e24fb08 100644 --- a/torchvision/csrc/io/image/cpu/decode_png.cpp +++ b/torchvision/csrc/io/image/cpu/decode_png.cpp @@ -77,6 +77,11 @@ torch::Tensor decode_png(const torch::Tensor& data, ImageReadMode mode) { TORCH_CHECK(retval == 1, "Could read image metadata from content.") } + if (bit_depth > 16) { + png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); + TORCH_CHECK(false, "At most 16-bit PNG images are supported currently.") + } + int channels = png_get_channels(png_ptr, info_ptr); if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) From 67ea116eb70ba16f10bf2fe2f3d7d9a149874f39 Mon Sep 17 00:00:00 2001 From: Nicolas Hug Date: Thu, 21 Oct 2021 09:46:20 +0100 Subject: [PATCH 7/7] Address comments --- torchvision/csrc/io/image/cpu/decode_png.cpp | 6 +++++- torchvision/io/image.py | 14 +++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/torchvision/csrc/io/image/cpu/decode_png.cpp b/torchvision/csrc/io/image/cpu/decode_png.cpp index 3408e24fb08..2bd25c3d91a 100644 --- a/torchvision/csrc/io/image/cpu/decode_png.cpp +++ b/torchvision/csrc/io/image/cpu/decode_png.cpp @@ -195,8 +195,12 @@ torch::Tensor decode_png(const torch::Tensor& data, ImageReadMode mode) { png_set_swap(png_ptr); } int32_t* t_ptr = tensor.accessor().data(); + + // We create a tensor instead of malloc-ing for automatic memory management + auto tmp_buffer_tensor = torch::empty( + {int64_t(num_pixels_per_row * sizeof(uint16_t))}, torch::kU8); uint16_t* tmp_buffer = - (uint16_t*)malloc(num_pixels_per_row * sizeof(uint16_t)); + (uint16_t*)tmp_buffer_tensor.accessor().data(); for (int pass = 0; pass < number_of_passes; pass++) { for (png_uint_32 i = 0; i < height; ++i) { diff --git a/torchvision/io/image.py b/torchvision/io/image.py index 9c1496930ac..f0cadc94c5d 100644 --- a/torchvision/io/image.py +++ b/torchvision/io/image.py @@ -61,12 +61,12 @@ def decode_png(input: torch.Tensor, mode: ImageReadMode = ImageReadMode.UNCHANGE """ Decodes a PNG image into a 3 dimensional RGB Tensor. Optionally converts the image to the desired format. - The values of the output tensor are uint8 between 0 and 255, except for - 16-bits pngs which are int32 tensors. + The values of the output tensor are uint8 in [0, 255], except for + 16-bits pngs which are int32 tensors in [0, 65535]. .. warning:: Should pytorch ever support the uint16 dtype natively, the dtype of the - output tensor of 16-bits pngs will be updated from int32 to uint16. + output for 16-bits pngs will be updated from int32 to uint16. Args: input (Tensor[1]): a one dimensional uint8 tensor containing @@ -193,8 +193,8 @@ def decode_image(input: torch.Tensor, mode: ImageReadMode = ImageReadMode.UNCHAN operation to decode the image into a 3 dimensional RGB Tensor. Optionally converts the image to the desired format. - The values of the output tensor are uint8 between 0 and 255, except for - 16-bits pngs which are int32 tensors. + The values of the output tensor are uint8 in [0, 255], except for + 16-bits pngs which are int32 tensors in [0, 65535]. Args: input (Tensor): a one dimensional uint8 tensor containing the raw bytes of the @@ -215,8 +215,8 @@ def read_image(path: str, mode: ImageReadMode = ImageReadMode.UNCHANGED) -> torc """ Reads a JPEG or PNG image into a 3 dimensional RGB Tensor. Optionally converts the image to the desired format. - The values of the output tensor are uint8 between 0 and 255, except for - 16-bits pngs which are int32 tensors. + The values of the output tensor are uint8 in [0, 255], except for + 16-bits pngs which are int32 tensors in [0, 65535]. Args: path (str): path of the JPEG or PNG image.