From b13b5d86f6feaed4e0a8a278586f0db792e34b8d Mon Sep 17 00:00:00 2001 From: Stefan Dragnev Date: Wed, 23 Dec 2020 13:33:33 +0100 Subject: [PATCH] Merge pull request #19109 from tailsu:sd/imdecode-jp2k-codestream * OpenJPEG: decoder for J2K codestreams * code review fixes * exclude .j2c from GDAL tests --- .../imgcodecs/src/grfmt_jpeg2000_openjpeg.cpp | 49 +++++++++++++------ .../imgcodecs/src/grfmt_jpeg2000_openjpeg.hpp | 25 +++++++--- modules/imgcodecs/src/loadsave.cpp | 3 +- modules/imgcodecs/test/test_grfmt.cpp | 6 ++- 4 files changed, 60 insertions(+), 23 deletions(-) diff --git a/modules/imgcodecs/src/grfmt_jpeg2000_openjpeg.cpp b/modules/imgcodecs/src/grfmt_jpeg2000_openjpeg.cpp index 149b34c927..73d49282d7 100644 --- a/modules/imgcodecs/src/grfmt_jpeg2000_openjpeg.cpp +++ b/modules/imgcodecs/src/grfmt_jpeg2000_openjpeg.cpp @@ -495,20 +495,16 @@ detail::StreamPtr opjCreateBufferInputStream(detail::OpjMemoryBuffer* buf) /////////////////////// Jpeg2KOpjDecoder /////////////////// -Jpeg2KOpjDecoder::Jpeg2KOpjDecoder() +namespace detail { + +Jpeg2KOpjDecoderBase::Jpeg2KOpjDecoderBase(OPJ_CODEC_FORMAT format) + : format_(format) { - static const unsigned char signature[] = { 0, 0, 0, 0x0c, 'j', 'P', ' ', ' ', 13, 10, 0x87, 10 }; - m_signature = String((const char*)(signature), sizeof(signature)); m_buf_supported = true; } -ImageDecoder Jpeg2KOpjDecoder::newDecoder() const -{ - return makePtr(); -} - -bool Jpeg2KOpjDecoder::readHeader() +bool Jpeg2KOpjDecoderBase::readHeader() { if (!m_buf.empty()) { opjBuf_ = detail::OpjMemoryBuffer(m_buf); @@ -521,7 +517,7 @@ bool Jpeg2KOpjDecoder::readHeader() if (!stream_) return false; - codec_.reset(opj_create_decompress(OPJ_CODEC_JP2)); + codec_.reset(opj_create_decompress(format_)); if (!codec_) return false; @@ -587,7 +583,7 @@ bool Jpeg2KOpjDecoder::readHeader() return true; } -bool Jpeg2KOpjDecoder::readData( Mat& img ) +bool Jpeg2KOpjDecoderBase::readData( Mat& img ) { using DecodeFunc = bool(*)(const opj_image_t&, cv::Mat&, uint8_t shift); @@ -606,7 +602,9 @@ bool Jpeg2KOpjDecoder::readData( Mat& img ) switch (image_->color_space) { case OPJ_CLRSPC_UNKNOWN: - CV_LOG_WARNING(NULL, "OpenJPEG2000: Image has unknown color space, SRGB is assumed"); + /* FALLTHRU */ + case OPJ_CLRSPC_UNSPECIFIED: + CV_LOG_WARNING(NULL, "OpenJPEG2000: Image has unknown or unspecified color space, SRGB is assumed"); /* FALLTHRU */ case OPJ_CLRSPC_SRGB: decode = decodeSRGBData; @@ -617,8 +615,6 @@ bool Jpeg2KOpjDecoder::readData( Mat& img ) case OPJ_CLRSPC_SYCC: decode = decodeSYCCData; break; - case OPJ_CLRSPC_UNSPECIFIED: - CV_Error(Error::StsNotImplemented, "OpenJPEG2000: Image has unspecified color space"); default: CV_Error(Error::StsNotImplemented, cv::format("OpenJPEG2000: Unsupported color space conversion: %s -> %s", @@ -654,6 +650,31 @@ bool Jpeg2KOpjDecoder::readData( Mat& img ) return decode(*image_, img, shift); } +} // namespace detail + +Jpeg2KJP2OpjDecoder::Jpeg2KJP2OpjDecoder() + : Jpeg2KOpjDecoderBase(OPJ_CODEC_JP2) +{ + static const unsigned char JP2Signature[] = { 0, 0, 0, 0x0c, 'j', 'P', ' ', ' ', 13, 10, 0x87, 10 }; + m_signature = String((const char*) JP2Signature, sizeof(JP2Signature)); +} + +ImageDecoder Jpeg2KJP2OpjDecoder::newDecoder() const +{ + return makePtr(); +} + +Jpeg2KJ2KOpjDecoder::Jpeg2KJ2KOpjDecoder() + : Jpeg2KOpjDecoderBase(OPJ_CODEC_J2K) +{ + static const unsigned char J2KSignature[] = { 0xff, 0x4f, 0xff, 0x51 }; + m_signature = String((const char*) J2KSignature, sizeof(J2KSignature)); +} + +ImageDecoder Jpeg2KJ2KOpjDecoder::newDecoder() const +{ + return makePtr(); +} /////////////////////// Jpeg2KOpjEncoder /////////////////// diff --git a/modules/imgcodecs/src/grfmt_jpeg2000_openjpeg.hpp b/modules/imgcodecs/src/grfmt_jpeg2000_openjpeg.hpp index 14b888620b..7c8a4af396 100644 --- a/modules/imgcodecs/src/grfmt_jpeg2000_openjpeg.hpp +++ b/modules/imgcodecs/src/grfmt_jpeg2000_openjpeg.hpp @@ -59,15 +59,11 @@ using StreamPtr = std::unique_ptr; using CodecPtr = std::unique_ptr; using ImagePtr = std::unique_ptr; -} // namespace detail - -class Jpeg2KOpjDecoder CV_FINAL : public BaseImageDecoder +class Jpeg2KOpjDecoderBase : public BaseImageDecoder { public: - Jpeg2KOpjDecoder(); - ~Jpeg2KOpjDecoder() CV_OVERRIDE = default; + Jpeg2KOpjDecoderBase(OPJ_CODEC_FORMAT format); - ImageDecoder newDecoder() const CV_OVERRIDE; bool readData( Mat& img ) CV_OVERRIDE; bool readHeader() CV_OVERRIDE; @@ -79,6 +75,23 @@ private: detail::OpjMemoryBuffer opjBuf_; OPJ_UINT32 m_maxPrec = 0; + OPJ_CODEC_FORMAT format_; +}; + +} // namespace detail + +class Jpeg2KJP2OpjDecoder CV_FINAL : public detail::Jpeg2KOpjDecoderBase { +public: + Jpeg2KJP2OpjDecoder(); + + ImageDecoder newDecoder() const CV_OVERRIDE; +}; + +class Jpeg2KJ2KOpjDecoder CV_FINAL : public detail::Jpeg2KOpjDecoderBase { +public: + Jpeg2KJ2KOpjDecoder(); + + ImageDecoder newDecoder() const CV_OVERRIDE; }; class Jpeg2KOpjEncoder CV_FINAL : public BaseImageEncoder diff --git a/modules/imgcodecs/src/loadsave.cpp b/modules/imgcodecs/src/loadsave.cpp index b28a861275..1536debead 100644 --- a/modules/imgcodecs/src/loadsave.cpp +++ b/modules/imgcodecs/src/loadsave.cpp @@ -179,7 +179,8 @@ struct ImageCodecInitializer encoders.push_back( makePtr() ); #endif #ifdef HAVE_OPENJPEG - decoders.push_back( makePtr() ); + decoders.push_back( makePtr() ); + decoders.push_back( makePtr() ); encoders.push_back( makePtr() ); #endif #ifdef HAVE_OPENEXR diff --git a/modules/imgcodecs/test/test_grfmt.cpp b/modules/imgcodecs/test/test_grfmt.cpp index 7d6930ef5d..0d28688004 100644 --- a/modules/imgcodecs/test/test_grfmt.cpp +++ b/modules/imgcodecs/test/test_grfmt.cpp @@ -78,7 +78,9 @@ const string all_images[] = "readwrite/Bretagne2.jp2", "readwrite/Grey.jp2", "readwrite/Grey.jp2", + "readwrite/balloon.j2c", #endif + #ifdef HAVE_GDCM "readwrite/int16-mono1.dcm", "readwrite/uint8-mono2.dcm", @@ -109,11 +111,11 @@ INSTANTIATE_TEST_CASE_P(All, Imgcodecs_FileMode, testing::ValuesIn(all_images), testing::ValuesIn(basic_modes))); -// GDAL does not support "hdr", "dcm" and have problems with "jp2" +// GDAL does not support "hdr", "dcm" and has problems with JPEG2000 files (jp2, j2c) struct notForGDAL { bool operator()(const string &name) const { const string &ext = name.substr(name.size() - 3, 3); - return ext == "hdr" || ext == "dcm" || ext == "jp2" || + return ext == "hdr" || ext == "dcm" || ext == "jp2" || ext == "j2c" || name.find("rle8.bmp") != std::string::npos; } };