Merge pull request #23433 from Kumataro:4.x-fix23416

imgcodecs: tiff: Support to encode for CV_32S with compression params

Fix https://github.com/opencv/opencv/issues/23416

### Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [x] The PR is proposed to the proper branch
- [x] There is a reference to the original bug report and related work
- [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [ ] The feature is well documented and sample code can be built with the project CMake
pull/23482/head
Kumataro 2 years ago committed by GitHub
parent 88a7e8cdf5
commit d2dbaa4cd1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 72
      modules/imgcodecs/src/grfmt_tiff.cpp
  2. 95
      modules/imgcodecs/test/test_tiff.cpp

@ -63,6 +63,9 @@ using namespace tiff_dummy_namespace;
namespace cv
{
// to extend cvtColor() to support CV_8S, CV_16S, CV_32S and CV_64F.
static void extend_cvtColor( InputArray _src, OutputArray _dst, int code );
#define CV_TIFF_CHECK_CALL(call) \
if (0 == (call)) { \
CV_LOG_WARNING(NULL, "OpenCV TIFF(line " << __LINE__ << "): failed " #call); \
@ -1039,9 +1042,9 @@ bool TiffDecoder::readData( Mat& img )
Rect roi_tile(0, 0, tile_width, tile_height);
Rect roi_img(x, img_y, tile_width, tile_height);
if (!m_hdr && ncn == 3)
cvtColor(m_tile(roi_tile), img(roi_img), COLOR_RGB2BGR);
extend_cvtColor(m_tile(roi_tile), img(roi_img), COLOR_RGB2BGR);
else if (!m_hdr && ncn == 4)
cvtColor(m_tile(roi_tile), img(roi_img), COLOR_RGBA2BGRA);
extend_cvtColor(m_tile(roi_tile), img(roi_img), COLOR_RGBA2BGRA);
else
m_tile(roi_tile).copyTo(img(roi_img));
break;
@ -1279,13 +1282,17 @@ bool TiffEncoder::writeLibTiff( const std::vector<Mat>& img_vec, const std::vect
break;
}
case CV_32F:
sample_format = SAMPLEFORMAT_IEEEFP;
/* FALLTHRU */
case CV_32S:
{
bitsPerChannel = 32;
sample_format = SAMPLEFORMAT_INT;
break;
}
case CV_32F:
{
bitsPerChannel = 32;
page_compression = COMPRESSION_NONE;
sample_format = SAMPLEFORMAT_IEEEFP;
break;
}
case CV_64F:
@ -1356,13 +1363,13 @@ bool TiffEncoder::writeLibTiff( const std::vector<Mat>& img_vec, const std::vect
case 3:
{
cvtColor(img(Rect(0, y, width, 1)), (const Mat&)m_buffer, COLOR_BGR2RGB);
extend_cvtColor(img(Rect(0, y, width, 1)), (const Mat&)m_buffer, COLOR_BGR2RGB);
break;
}
case 4:
{
cvtColor(img(Rect(0, y, width, 1)), (const Mat&)m_buffer, COLOR_BGRA2RGBA);
extend_cvtColor(img(Rect(0, y, width, 1)), (const Mat&)m_buffer, COLOR_BGRA2RGBA);
break;
}
@ -1424,6 +1431,57 @@ bool TiffEncoder::write( const Mat& img, const std::vector<int>& params)
return writeLibTiff(img_vec, params);
}
static void extend_cvtColor( InputArray _src, OutputArray _dst, int code )
{
CV_Assert( !_src.empty() );
CV_Assert( _src.dims() == 2 );
// This function extend_cvtColor reorders the src channels with only thg limited condition.
// Otherwise, it calls cvtColor.
const int stype = _src.type();
if(!
(
(
( stype == CV_8SC3 ) || ( stype == CV_8SC4 ) ||
( stype == CV_16SC3 ) || ( stype == CV_16SC4 ) ||
( stype == CV_32SC3 ) || ( stype == CV_32SC4 ) ||
( stype == CV_64FC3 ) || ( stype == CV_64FC4 )
)
&&
(
( code == COLOR_BGR2RGB ) || ( code == COLOR_BGRA2RGBA )
)
)
)
{
cvtColor( _src, _dst, code );
return;
}
Mat src = _src.getMat();
// cv::mixChannels requires the output arrays to be pre-allocated before calling the function.
_dst.create( _src.size(), stype );
Mat dst = _dst.getMat();
// BGR to RGB or BGRA to RGBA
// src[0] -> dst[2]
// src[1] -> dst[1]
// src[2] -> dst[0]
// src[3] -> dst[3] if src has alpha channel.
std::vector<int> fromTo;
fromTo.push_back(0); fromTo.push_back(2);
fromTo.push_back(1); fromTo.push_back(1);
fromTo.push_back(2); fromTo.push_back(0);
if ( code == COLOR_BGRA2RGBA )
{
fromTo.push_back(3); fromTo.push_back(3);
}
cv::mixChannels( src, dst, fromTo );
}
} // namespace
#endif

@ -14,6 +14,38 @@ namespace opencv_test { namespace {
#define int64 int64_hack_
#include "tiff.h"
// Re-define Mat type as enum for showing on Google Test.
enum CV_ddtCn{
_CV_8UC1 = CV_8UC1, _CV_8UC3 = CV_8UC3, _CV_8UC4 = CV_8UC4,
_CV_8SC1 = CV_8SC1, _CV_8SC3 = CV_8SC3, _CV_8SC4 = CV_8SC4,
_CV_16UC1 = CV_16UC1, _CV_16UC3 = CV_16UC3, _CV_16UC4 = CV_16UC4,
_CV_16SC1 = CV_16SC1, _CV_16SC3 = CV_16SC3, _CV_16SC4 = CV_16SC4,
_CV_32SC1 = CV_32SC1, _CV_32SC3 = CV_32SC3, _CV_32SC4 = CV_32SC4,
_CV_16FC1 = CV_16FC1, _CV_16FC3 = CV_16FC3, _CV_16FC4 = CV_16FC4,
_CV_32FC1 = CV_32FC1, _CV_32FC3 = CV_32FC3, _CV_32FC4 = CV_32FC4,
_CV_64FC1 = CV_64FC1, _CV_64FC3 = CV_64FC3, _CV_64FC4 = CV_64FC4,
};
static inline
void PrintTo(const CV_ddtCn& val, std::ostream* os)
{
const int val_type = static_cast<int>(val);
switch ( CV_MAT_DEPTH(val_type) )
{
case CV_8U : *os << "CV_8U" ; break;
case CV_16U : *os << "CV_16U" ; break;
case CV_8S : *os << "CV_8S" ; break;
case CV_16S : *os << "CV_16S" ; break;
case CV_32S : *os << "CV_32S" ; break;
case CV_16F : *os << "CV_16F" ; break;
case CV_32F : *os << "CV_32F" ; break;
case CV_64F : *os << "CV_64F" ; break;
default : *os << "CV_???" ; break;
}
*os << "C" << CV_MAT_CN(val_type);
}
#ifdef __ANDROID__
// Test disabled as it uses a lot of memory.
// It is killed with SIGKILL by out of memory killer.
@ -840,6 +872,69 @@ TEST(Imgcodecs_Tiff, readWrite_predictor)
}
}
// See https://github.com/opencv/opencv/issues/23416
typedef std::pair<CV_ddtCn,bool> Imgcodes_Tiff_TypeAndComp;
typedef testing::TestWithParam< Imgcodes_Tiff_TypeAndComp > Imgcodecs_Tiff_Types;
TEST_P(Imgcodecs_Tiff_Types, readWrite_alltypes)
{
const int mat_types = static_cast<int>(get<0>(GetParam()));
const bool isCompAvailable = get<1>(GetParam());
// Create a test image.
const Mat src = cv::Mat::zeros( 120, 160, mat_types );
{
// Add noise to test compression.
cv::Mat roi = cv::Mat(src, cv::Rect(0, 0, src.cols, src.rows/2));
cv::randu(roi, cv::Scalar(0), cv::Scalar(256));
}
// Try to encode/decode the test image with LZW compression.
std::vector<uchar> bufLZW;
{
std::vector<int> params;
params.push_back(IMWRITE_TIFF_COMPRESSION);
params.push_back(COMPRESSION_LZW);
ASSERT_NO_THROW(cv::imencode(".tiff", src, bufLZW, params));
Mat dstLZW;
ASSERT_NO_THROW(cv::imdecode( bufLZW, IMREAD_UNCHANGED, &dstLZW));
ASSERT_EQ(dstLZW.type(), src.type());
ASSERT_EQ(dstLZW.size(), src.size());
ASSERT_LE(cvtest::norm(dstLZW, src, NORM_INF | NORM_RELATIVE), 1e-3);
}
// Try to encode/decode the test image with RAW.
std::vector<uchar> bufRAW;
{
std::vector<int> params;
params.push_back(IMWRITE_TIFF_COMPRESSION);
params.push_back(COMPRESSION_NONE);
ASSERT_NO_THROW(cv::imencode(".tiff", src, bufRAW, params));
Mat dstRAW;
ASSERT_NO_THROW(cv::imdecode( bufRAW, IMREAD_UNCHANGED, &dstRAW));
ASSERT_EQ(dstRAW.type(), src.type());
ASSERT_EQ(dstRAW.size(), src.size());
ASSERT_LE(cvtest::norm(dstRAW, src, NORM_INF | NORM_RELATIVE), 1e-3);
}
// Compare LZW and RAW streams.
EXPECT_EQ(bufLZW == bufRAW, !isCompAvailable);
}
Imgcodes_Tiff_TypeAndComp all_types[] = {
{ _CV_8UC1, true }, { _CV_8UC3, true }, { _CV_8UC4, true },
{ _CV_8SC1, true }, { _CV_8SC3, true }, { _CV_8SC4, true },
{ _CV_16UC1, true }, { _CV_16UC3, true }, { _CV_16UC4, true },
{ _CV_16SC1, true }, { _CV_16SC3, true }, { _CV_16SC4, true },
{ _CV_32SC1, true }, { _CV_32SC3, true }, { _CV_32SC4, true },
{ _CV_32FC1, false }, { _CV_32FC3, false }, { _CV_32FC4, false }, // No compression
{ _CV_64FC1, false }, { _CV_64FC3, false }, { _CV_64FC4, false } // No compression
};
INSTANTIATE_TEST_CASE_P(AllTypes, Imgcodecs_Tiff_Types, testing::ValuesIn(all_types));
//==================================================================================================

Loading…
Cancel
Save