-
Notifications
You must be signed in to change notification settings - Fork 65
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
Support Aperio SVS with CPU LZW and jpeg2k decoder #141
Conversation
0aed4ed
to
65b5ad3
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks Gigon! 😄
Had a few questions below
{ | ||
throw std::runtime_error( | ||
fmt::format("This format has more than one image with Subfile Type 0 so cannot be loaded!")); | ||
// Aperio SVS format |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there something else expected in this block? If not, should we consolidate the if
/else
here into a single if
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the feedback!
I expected something but not anymore. Will consolidate it.
@@ -41,6 +45,12 @@ struct my_error_mgr | |||
boolean warning, stopOnWarning; | |||
}; | |||
|
|||
enum |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we name this enum
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code is directly from libjpeg-turbo's c code and COMPRESS/DECOMPRESS are used in jpeg_decode_buffer() which is modified from tjDecompress2() in libjpeg-turbo.
Since those constants are used only inside libjpeg_turbo.cpp, I would like to keep them as they are.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok that's fine. See we already have the license in 3rd party. Should we add a note to this file about this code coming from there?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The information is in lines 18-26 of this file
/**
* Code below is derived from the libjpeg-turbo's code which is under three compatible
* BSD-style open source licenses
* - The IJG (Independent JPEG Group) License
* - The Modified (3-clause) BSD License
* - The zlib License
* Please see LICENSE-3rdparty.md for the detail.
* - https://github.com/libjpeg-turbo/libjpeg-turbo/blob/00607ec260efa4cfe10f9b36d6e3d3590ae92d79/tjexample.c
* - https://github.com/libjpeg-turbo/libjpeg-turbo/blob/2.0.6/turbojpeg.c#L1241
*/
And in LICENSE-3rdparty.md,
libjpeg-turbo
- This software is based in part on the work of the Independent JPEG Group.
- License: libjpeg-turbo is covered by three compatible BSD-style open source licenses
- The IJG (Independent JPEG Group) License
- The Modified (3-clause) BSD License
- The zlib License
- https://github.com/libjpeg-turbo/libjpeg-turbo/blob/master/LICENSE.md
- Copyright:
- D. R. Commander
- Viktor Szathmáry
- Files:
- cpp/plugins/cucim.kit.cuslide/src/cuslide/jpeg/libjpeg_turbo.cpp : Implementation of jpeg decoder.
Do you think additional information is needed here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No that seems sufficient
if (flags & TJFLAG_FASTDCT) | ||
instance->dinfo.dct_method = JDCT_FASTEST; | ||
if (flags & TJFLAG_FASTUPSAMPLE) | ||
dinfo->do_fancy_upsampling = FALSE; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We seem to use braces in some places (like above), but not here. Would it make sense to just use braces everywhere?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same reason for above.
int jpeg_decode_buffer(const void* handle, | ||
const unsigned char* jpegBuf, | ||
unsigned long jpegSize, | ||
unsigned char* dstBuf, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we do nullptr
checks for these below?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method (jpeg_decode_buffer()) is from the original function tjDecompress2() and did minimal change to make it work
- line 249-253 are added to replace
GET_DINSTANCE(handle);
macro - line 282-286 are added to set color space
For other code that is borrowed from the external library, I would like to keep the source code as it is so that further changes from the original code are trackable.
Instead, added some more comments for explaining that.
int flags, | ||
int jpegColorSpace) | ||
{ | ||
JSAMPROW* row_pointer = NULL; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we use nullptr
here and below?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same reason for above.
instance->jerr.warning = FALSE; | ||
instance->isInstanceError = FALSE; | ||
|
||
instance->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we use true
& false
for these?
instance->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE; | |
instance->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? true : false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same reason for above.
TRUE/FALSE is defined by build-debug/_deps/deps-libjpeg-turbo-src/jmorecfg.h
which is included by <jpeglib.h> in this file.
30f1298
to
b383ae9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @jakirkham for the review!
The following change is made to address your comments.
diff --git a/cpp/plugins/cucim.kit.cuslide/src/cuslide/cuslide.cpp b/cpp/plugins/cucim.kit.cuslide/src/cuslide/cuslide.cpp
index 4adc1c1..5e7c17d 100644
--- a/cpp/plugins/cucim.kit.cuslide/src/cuslide/cuslide.cpp
+++ b/cpp/plugins/cucim.kit.cuslide/src/cuslide/cuslide.cpp
@@ -103,11 +103,8 @@ static bool CUCIM_ABI parser_parse(CuCIMFileHandle* handle, cucim::io::format::I
size_t ifd_count = tif->ifd_count();
size_t level_count = tif->level_count();
- if (tif->ifd(0)->image_description().rfind("Aperio", 0) == 0)
- {
- // Aperio SVS format
- }
- else
+ // If not Aperio SVS format (== Ordinary Pyramid TIFF image)
+ if (tif->ifd(0)->image_description().rfind("Aperio", 0) != 0)
{
std::vector<size_t> main_ifd_list;
for (size_t i = 0; i < ifd_count; i++)
diff --git a/cpp/plugins/cucim.kit.cuslide/src/cuslide/jpeg/libjpeg_turbo.cpp b/cpp/plugins/cucim.kit.cuslide/src/cuslide/jpeg/libjpeg_turbo.cpp
index c7a1d06..0bf7146 100644
--- a/cpp/plugins/cucim.kit.cuslide/src/cuslide/jpeg/libjpeg_turbo.cpp
+++ b/cpp/plugins/cucim.kit.cuslide/src/cuslide/jpeg/libjpeg_turbo.cpp
@@ -16,13 +16,14 @@
*/
/**
- * Code below is derived from the libjpeg-turbo's example code (tjexample.c) which is under three compatible
+ * Code below is derived from the libjpeg-turbo's code which is under three compatible
* BSD-style open source licenses
* - The IJG (Independent JPEG Group) License
* - The Modified (3-clause) BSD License
* - The zlib License
* Please see LICENSE-3rdparty.md for the detail.
- * (https://github.com/libjpeg-turbo/libjpeg-turbo/blob/00607ec260efa4cfe10f9b36d6e3d3590ae92d79/tjexample.c)
+ * - https://github.com/libjpeg-turbo/libjpeg-turbo/blob/00607ec260efa4cfe10f9b36d6e3d3590ae92d79/tjexample.c
+ * - https://github.com/libjpeg-turbo/libjpeg-turbo/blob/2.0.6/turbojpeg.c#L1241
*/
#include "libjpeg_turbo.h"
@@ -231,7 +232,9 @@ bool read_jpeg_header_tables(const void* handle, const void* jpeg_buf, unsigned
return true;
}
-// tjDecompress2
+// The following implementation is borrowed from tjDecompress2() in libjpeg-turbo
+// (https://github.com/libjpeg-turbo/libjpeg-turbo/blob/2.0.6/turbojpeg.c#L1241)
+// to set color space of the input image from TIFF metadata.
int jpeg_decode_buffer(const void* handle,
const unsigned char* jpegBuf,
unsigned long jpegSize,
@@ -246,6 +249,7 @@ int jpeg_decode_buffer(const void* handle,
JSAMPROW* row_pointer = NULL;
int i, retval = 0, jpegwidth, jpegheight, scaledw, scaledh;
+ // Replace `GET_DINSTANCE(handle);` by cuCIM
tjinstance* instance = (tjinstance*)handle;
j_decompress_ptr dinfo = NULL;
dinfo = &instance->dinfo;
@@ -279,7 +283,7 @@ int jpeg_decode_buffer(const void* handle,
jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
jpeg_read_header(dinfo, TRUE);
- // Modified to set jpeg_color_space for cuCIM
+ // Modified to set jpeg_color_space by cuCIM
if (jpegColorSpace)
{
dinfo->jpeg_color_space = static_cast<J_COLOR_SPACE>(jpegColorSpace);
{ | ||
throw std::runtime_error( | ||
fmt::format("This format has more than one image with Subfile Type 0 so cannot be loaded!")); | ||
// Aperio SVS format |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the feedback!
I expected something but not anymore. Will consolidate it.
@@ -41,6 +45,12 @@ struct my_error_mgr | |||
boolean warning, stopOnWarning; | |||
}; | |||
|
|||
enum |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code is directly from libjpeg-turbo's c code and COMPRESS/DECOMPRESS are used in jpeg_decode_buffer() which is modified from tjDecompress2() in libjpeg-turbo.
Since those constants are used only inside libjpeg_turbo.cpp, I would like to keep them as they are.
int jpeg_decode_buffer(const void* handle, | ||
const unsigned char* jpegBuf, | ||
unsigned long jpegSize, | ||
unsigned char* dstBuf, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method (jpeg_decode_buffer()) is from the original function tjDecompress2() and did minimal change to make it work
- line 249-253 are added to replace
GET_DINSTANCE(handle);
macro - line 282-286 are added to set color space
For other code that is borrowed from the external library, I would like to keep the source code as it is so that further changes from the original code are trackable.
Instead, added some more comments for explaining that.
int flags, | ||
int jpegColorSpace) | ||
{ | ||
JSAMPROW* row_pointer = NULL; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same reason for above.
instance->jerr.warning = FALSE; | ||
instance->isInstanceError = FALSE; | ||
|
||
instance->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same reason for above.
TRUE/FALSE is defined by build-debug/_deps/deps-libjpeg-turbo-src/jmorecfg.h
which is included by <jpeglib.h> in this file.
if (flags & TJFLAG_FASTDCT) | ||
instance->dinfo.dct_method = JDCT_FASTEST; | ||
if (flags & TJFLAG_FASTUPSAMPLE) | ||
dinfo->do_fancy_upsampling = FALSE; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same reason for above.
Copy tif_lzw.c file from libtiff for porting lzw decoder. The code is from the following link: https://gitlab.com/libtiff/libtiff/-/blob/8546f7ee994eacff0a563918096f16e0a6078fa2/libtiff/tif_lzw.c , which is after v4.3.0
- Update Jpeg decoder to consider colorspace of the JPEG image - We need to set a proper color space from TIFF metadata to the decoder - Update tjDecompress2() method call with jpeg_decode_buffer() that modified tjDecompress2() implementation - Add `jpeg_color_space_` to IFD Class. - Move additional includes to their own cmake files - Add Jpeg2k decoder with OpenJpeg - Added fast color conversion logic with pre-calculated table - Add LZW decoder with libtiff's implementation - Take part of the code to provide only LZW decoder part - Add `rows_per_strip_` and `predictor_` attributers to IFD class, to support LZW - Refactor TIFF:resolve_vendor_format() to support Aperio SVS metadata - Support LZW compressed image (with multi strips) for associated image. Addresses rapidsai#17
b383ae9
to
0a9803e
Compare
@gpucibot merge |
Add tif_lzw.c from libtiff as it is
Copy tif_lzw.c file from libtiff for porting lzw decoder.
The code is from the following link:
https://gitlab.com/libtiff/libtiff/-/blob/8546f7ee994eacff0a563918096f16e0a6078fa2/libtiff/tif_lzw.c
, which is after v4.3.0
Support Aperio SVS with CPU LZW and jpeg2k decoder
jpeg_color_space_
to IFD Class.rows_per_strip_
andpredictor_
attributes to IFD class, to support LZWAddresses #17