From 8336a96cb99f7074aef11aa6f450509440031673 Mon Sep 17 00:00:00 2001 From: tantei3 <54449449+tantei3@users.noreply.github.com> Date: Thu, 6 Apr 2023 17:04:36 +0530 Subject: [PATCH] Merge pull request #23446 from tantei3:bmp_fix **Merge with extra**: https://github.com/opencv/opencv_extra/pull/1050 For 32 bits per pixel with 3 or 4 channel destination images, apply scale factor and mask to parse BMP files correctly closes #23445 ### Pull Request Readiness Checklist - [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 --- modules/imgcodecs/src/grfmt_bmp.cpp | 77 +++++++++++++++++++-------- modules/imgcodecs/src/grfmt_bmp.hpp | 4 +- modules/imgcodecs/test/test_grfmt.cpp | 31 +++++++++++ 3 files changed, 89 insertions(+), 23 deletions(-) diff --git a/modules/imgcodecs/src/grfmt_bmp.cpp b/modules/imgcodecs/src/grfmt_bmp.cpp index abb272c93e..9c76597908 100644 --- a/modules/imgcodecs/src/grfmt_bmp.cpp +++ b/modules/imgcodecs/src/grfmt_bmp.cpp @@ -127,6 +127,7 @@ bool BmpDecoder::readHeader() ++bit_count; } m_rgba_bit_offset[index_rgba] = bit_count; + m_rgba_scale_factor[index_rgba] = 255.0f / mask; } } m_strm.skip( size - 56 ); @@ -503,21 +504,33 @@ decode_rle8_bad: ; break; /************************* 32 BPP ************************/ case 32: - for( y = 0; y < m_height; y++, data += step ) { - m_strm.getBytes( src, src_pitch ); - - if( !color ) - icvCvt_BGRA2Gray_8u_C4C1R( src, 0, data, 0, Size(m_width,1) ); - else if( img.channels() == 3 ) - icvCvt_BGRA2BGR_8u_C4C3R(src, 0, data, 0, Size(m_width, 1)); - else if ( img.channels() == 4 ) + bool has_bit_mask = (m_rgba_bit_offset[0] >= 0) && (m_rgba_bit_offset[1] >= 0) && (m_rgba_bit_offset[2] >= 0); + for( y = 0; y < m_height; y++, data += step ) { - bool has_bit_mask = (m_rgba_bit_offset[0] >= 0) && (m_rgba_bit_offset[1] >= 0) && (m_rgba_bit_offset[2] >= 0); - if ( has_bit_mask ) - maskBGRA(data, src, m_width); - else - memcpy(data, src, m_width * 4); + m_strm.getBytes( src, src_pitch ); + + if( !color ) + { + if ( has_bit_mask ) + maskBGRAtoGray(data, src, m_width); + else + icvCvt_BGRA2Gray_8u_C4C1R( src, 0, data, 0, Size(m_width,1) ); + } + else if( img.channels() == 3 ) + { + if ( has_bit_mask ) + maskBGRA(data, src, m_width, false); + else + icvCvt_BGRA2BGR_8u_C4C3R(src, 0, data, 0, Size(m_width, 1)); + } + else if ( img.channels() == 4 ) + { + if ( has_bit_mask ) + maskBGRA(data, src, m_width, true); + else + memcpy(data, src, m_width * 4); + } } } result = true; @@ -538,20 +551,40 @@ void BmpDecoder::initMask() { memset(m_rgba_mask, 0, sizeof(m_rgba_mask)); memset(m_rgba_bit_offset, -1, sizeof(m_rgba_bit_offset)); + for (size_t i = 0; i < 4; i++) { + m_rgba_scale_factor[i] = 1.0f; + } } -void BmpDecoder::maskBGRA(uchar* des, uchar* src, int num) +void BmpDecoder::maskBGRA(uchar* des, const uchar* src, int num, bool alpha_required) { - for( int i = 0; i < num; i++, des += 4, src += 4 ) + int dest_stride = alpha_required ? 4 : 3; + for( int i = 0; i < num; i++, des += dest_stride, src += 4 ) { uint data = *((uint*)src); - des[0] = (uchar)((m_rgba_mask[2] & data) >> m_rgba_bit_offset[2]); - des[1] = (uchar)((m_rgba_mask[1] & data) >> m_rgba_bit_offset[1]); - des[2] = (uchar)((m_rgba_mask[0] & data) >> m_rgba_bit_offset[0]); - if (m_rgba_bit_offset[3] >= 0) - des[3] = (uchar)((m_rgba_mask[3] & data) >> m_rgba_bit_offset[3]); - else - des[3] = 255; + des[0] = (uchar)(((m_rgba_mask[2] & data) >> m_rgba_bit_offset[2]) * m_rgba_scale_factor[2]); + des[1] = (uchar)(((m_rgba_mask[1] & data) >> m_rgba_bit_offset[1]) * m_rgba_scale_factor[1]); + des[2] = (uchar)(((m_rgba_mask[0] & data) >> m_rgba_bit_offset[0]) * m_rgba_scale_factor[0]); + if (alpha_required) + { + if (m_rgba_bit_offset[3] >= 0) + des[3] = (uchar)(((m_rgba_mask[3] & data) >> m_rgba_bit_offset[3]) * m_rgba_scale_factor[3]); + else + des[3] = 255; + } + } +} + +void BmpDecoder::maskBGRAtoGray(uchar* des, const uchar* src, int num) +{ + for( int i = 0; i < num; i++, des++, src += 4 ) + { + uint data = *((uint*)src); + int red = (uchar)(((m_rgba_mask[0] & data) >> m_rgba_bit_offset[0]) * m_rgba_scale_factor[0]); + int green = (uchar)(((m_rgba_mask[1] & data) >> m_rgba_bit_offset[1]) * m_rgba_scale_factor[1]); + int blue = (uchar)(((m_rgba_mask[2] & data) >> m_rgba_bit_offset[2]) * m_rgba_scale_factor[2]); + + *des = (uchar)(0.299f * red + 0.587f * green + 0.114f * blue); } } ////////////////////////////////////////////////////////////////////////////////////////// diff --git a/modules/imgcodecs/src/grfmt_bmp.hpp b/modules/imgcodecs/src/grfmt_bmp.hpp index 4b34124bf7..a9f5fe6040 100644 --- a/modules/imgcodecs/src/grfmt_bmp.hpp +++ b/modules/imgcodecs/src/grfmt_bmp.hpp @@ -74,7 +74,8 @@ public: protected: void initMask(); - void maskBGRA(uchar* des, uchar* src, int num); + void maskBGRA(uchar* des, const uchar* src, int num, bool alpha_required); + void maskBGRAtoGray(uchar* des, const uchar* src, int num); enum Origin { @@ -90,6 +91,7 @@ protected: BmpCompression m_rle_code; uint m_rgba_mask[4]; int m_rgba_bit_offset[4]; + float m_rgba_scale_factor[4]; }; diff --git a/modules/imgcodecs/test/test_grfmt.cpp b/modules/imgcodecs/test/test_grfmt.cpp index fa6c54c8d0..4ea3716d32 100644 --- a/modules/imgcodecs/test/test_grfmt.cpp +++ b/modules/imgcodecs/test/test_grfmt.cpp @@ -331,6 +331,37 @@ TEST(Imgcodecs_Bmp, read_32bit_xrgb) ASSERT_EQ(data[3], 255); } +TEST(Imgcodecs_Bmp, rgba_scale) +{ + const string root = cvtest::TS::ptr()->get_data_path(); + const string filenameInput = root + "readwrite/test_rgba_scale.bmp"; + + Mat img = cv::imread(filenameInput, IMREAD_UNCHANGED); + ASSERT_FALSE(img.empty()); + ASSERT_EQ(CV_8UC4, img.type()); + + uchar* data = img.ptr(); + ASSERT_EQ(data[0], 255); + ASSERT_EQ(data[1], 255); + ASSERT_EQ(data[2], 255); + ASSERT_EQ(data[3], 255); + + img = cv::imread(filenameInput, IMREAD_COLOR); + ASSERT_FALSE(img.empty()); + ASSERT_EQ(CV_8UC3, img.type()); + + data = img.ptr(); + ASSERT_EQ(data[0], 255); + ASSERT_EQ(data[1], 255); + ASSERT_EQ(data[2], 255); + + img = cv::imread(filenameInput, IMREAD_GRAYSCALE); + ASSERT_FALSE(img.empty()); + ASSERT_EQ(CV_8UC1, img.type()); + + data = img.ptr(); + ASSERT_EQ(data[0], 255); +} #ifdef HAVE_IMGCODEC_HDR TEST(Imgcodecs_Hdr, regression)