diff --git a/modules/core/include/opencv2/core/hal/intrin_neon.hpp b/modules/core/include/opencv2/core/hal/intrin_neon.hpp index 280691b448..06e70b0c30 100644 --- a/modules/core/include/opencv2/core/hal/intrin_neon.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_neon.hpp @@ -62,6 +62,22 @@ CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN #define CV_SIMD128_64F 0 #endif +// The following macro checks if the code is being compiled for the +// AArch64 execution state of Armv8, to enable the 128-bit +// intrinsics. The macro `__ARM_64BIT_STATE` is the one recommended by +// the Arm C Language Extension (ACLE) specifications [1] to check the +// availability of 128-bit intrinsics, and it is supporrted by clang +// and gcc. The macro `_M_ARM64` is the equivalent one for Microsoft +// Visual Studio [2] . +// +// [1] https://developer.arm.com/documentation/101028/0012/13--Advanced-SIMD--Neon--intrinsics +// [2] https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros +#if defined(__ARM_64BIT_STATE) || defined(_M_ARM64) +#define CV_NEON_AARCH64 1 +#else +#define CV_NEON_AARCH64 0 +#endif + // TODO #define CV_NEON_DOT 0 @@ -726,41 +742,61 @@ inline v_float64x2 v_dotprod_expand(const v_int32x4& a, const v_int32x4& b, // 16 >> 32 inline v_int32x4 v_dotprod_fast(const v_int16x8& a, const v_int16x8& b) { +#if CV_NEON_AARCH64 + int32x4_t p = vmull_s16(vget_low_s16(a.val), vget_low_s16(b.val)); + return v_int32x4(vmlal_high_s16(p, a.val, b.val)); +#else int16x4_t a0 = vget_low_s16(a.val); int16x4_t a1 = vget_high_s16(a.val); int16x4_t b0 = vget_low_s16(b.val); int16x4_t b1 = vget_high_s16(b.val); int32x4_t p = vmull_s16(a0, b0); return v_int32x4(vmlal_s16(p, a1, b1)); +#endif } inline v_int32x4 v_dotprod_fast(const v_int16x8& a, const v_int16x8& b, const v_int32x4& c) { +#if CV_NEON_AARCH64 + int32x4_t p = vmlal_s16(c.val, vget_low_s16(a.val), vget_low_s16(b.val)); + return v_int32x4(vmlal_high_s16(p, a.val, b.val)); +#else int16x4_t a0 = vget_low_s16(a.val); int16x4_t a1 = vget_high_s16(a.val); int16x4_t b0 = vget_low_s16(b.val); int16x4_t b1 = vget_high_s16(b.val); int32x4_t p = vmlal_s16(c.val, a0, b0); return v_int32x4(vmlal_s16(p, a1, b1)); +#endif } // 32 >> 64 inline v_int64x2 v_dotprod_fast(const v_int32x4& a, const v_int32x4& b) { +#if CV_NEON_AARCH64 + int64x2_t p = vmull_s32(vget_low_s32(a.val), vget_low_s32(b.val)); + return v_int64x2(vmlal_high_s32(p, a.val, b.val)); +#else int32x2_t a0 = vget_low_s32(a.val); int32x2_t a1 = vget_high_s32(a.val); int32x2_t b0 = vget_low_s32(b.val); int32x2_t b1 = vget_high_s32(b.val); int64x2_t p = vmull_s32(a0, b0); return v_int64x2(vmlal_s32(p, a1, b1)); +#endif } inline v_int64x2 v_dotprod_fast(const v_int32x4& a, const v_int32x4& b, const v_int64x2& c) { +#if CV_NEON_AARCH64 + int64x2_t p = vmlal_s32(c.val, vget_low_s32(a.val), vget_low_s32(b.val)); + return v_int64x2(vmlal_high_s32(p, a.val, b.val)); +#else int32x2_t a0 = vget_low_s32(a.val); int32x2_t a1 = vget_high_s32(a.val); int32x2_t b0 = vget_low_s32(b.val); int32x2_t b1 = vget_high_s32(b.val); int64x2_t p = vmlal_s32(c.val, a0, b0); return v_int64x2(vmlal_s32(p, a1, b1)); +#endif } // 8 >> 32 @@ -1292,7 +1328,7 @@ inline int64 v_reduce_sum(const v_int64x2& a) #if CV_SIMD128_64F inline double v_reduce_sum(const v_float64x2& a) { - return vgetq_lane_f64(a.val, 0) + vgetq_lane_f64(a.val, 1); + return vaddvq_f64(a.val); } #endif diff --git a/modules/core/src/parallel_impl.cpp b/modules/core/src/parallel_impl.cpp index 09579a3b14..25bf4adce8 100644 --- a/modules/core/src/parallel_impl.cpp +++ b/modules/core/src/parallel_impl.cpp @@ -356,6 +356,16 @@ public: }; +// Disable thread sanitization check when CV_USE_GLOBAL_WORKERS_COND_VAR is not +// set because it triggers as the main thread reads isActive while the children +// thread writes it (but it all works out because a mutex is locked in the main +// thread and isActive re-checked). +// This is to solve issue #19463. +#if !defined(CV_USE_GLOBAL_WORKERS_COND_VAR) && defined(__clang__) && defined(__has_feature) +#if __has_feature(thread_sanitizer) +__attribute__((no_sanitize("thread"))) +#endif +#endif void WorkerThread::thread_body() { (void)cv::utils::getThreadID(); // notify OpenCV about new thread diff --git a/modules/imgcodecs/CMakeLists.txt b/modules/imgcodecs/CMakeLists.txt index 8ae85e62c5..7280778686 100644 --- a/modules/imgcodecs/CMakeLists.txt +++ b/modules/imgcodecs/CMakeLists.txt @@ -13,11 +13,6 @@ if(HAVE_WINRT_CX AND NOT WINRT) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /ZW") endif() -if(HAVE_PNG OR HAVE_TIFF OR HAVE_OPENEXR) - ocv_include_directories(${ZLIB_INCLUDE_DIRS}) - list(APPEND GRFMT_LIBS ${ZLIB_LIBRARIES}) -endif() - if(HAVE_JPEG) ocv_include_directories(${JPEG_INCLUDE_DIR} ${${JPEG_LIBRARY}_BINARY_DIR}) list(APPEND GRFMT_LIBS ${JPEG_LIBRARIES}) @@ -63,6 +58,11 @@ if(HAVE_OPENEXR) list(APPEND GRFMT_LIBS ${OPENEXR_LIBRARIES}) endif() +if(HAVE_PNG OR HAVE_TIFF OR HAVE_OPENEXR) + ocv_include_directories(${ZLIB_INCLUDE_DIRS}) + list(APPEND GRFMT_LIBS ${ZLIB_LIBRARIES}) +endif() + if(HAVE_GDAL) include_directories(SYSTEM ${GDAL_INCLUDE_DIR}) list(APPEND GRFMT_LIBS ${GDAL_LIBRARY}) diff --git a/modules/imgcodecs/src/exif.cpp b/modules/imgcodecs/src/exif.cpp index 051999c0fa..28d52047d8 100644 --- a/modules/imgcodecs/src/exif.cpp +++ b/modules/imgcodecs/src/exif.cpp @@ -62,7 +62,7 @@ ExifEntry_t::ExifEntry_t() : /** * @brief ExifReader constructor */ -ExifReader::ExifReader(std::istream& stream) : m_stream(stream), m_format(NONE) +ExifReader::ExifReader() : m_format(NONE) { } @@ -73,25 +73,6 @@ ExifReader::~ExifReader() { } -/** - * @brief Parsing the file and prepare (internally) exif directory structure - * @return true if parsing was successful and exif information exists in JpegReader object - * false in case of unsuccessful parsing - */ -bool ExifReader::parse() -{ - try { - m_exif = getExif(); - if( !m_exif.empty() ) - { - return true; - } - return false; - } catch (ExifParsingError&) { - return false; - } -} - /** * @brief Get tag value by tag number @@ -101,10 +82,10 @@ bool ExifReader::parse() * @return ExifEntru_t structure. Caller has to know what tag it calls in order to extract proper field from the structure ExifEntry_t * */ -ExifEntry_t ExifReader::getTag(const ExifTagName tag) +ExifEntry_t ExifReader::getTag(const ExifTagName tag) const { ExifEntry_t entry; - std::map::iterator it = m_exif.find(tag); + std::map::const_iterator it = m_exif.find(tag); if( it != m_exif.end() ) { @@ -115,100 +96,37 @@ ExifEntry_t ExifReader::getTag(const ExifTagName tag) /** - * @brief Get exif directory structure contained in file (if any) - * This is internal function and is not exposed to client + * @brief Parsing the exif data buffer and prepare (internal) exif directory + * + * @param [in] data The data buffer to read EXIF data starting with endianness + * @param [in] size The size of the data buffer * - * @return Map where key is tag number and value is ExifEntry_t structure + * @return true if parsing was successful + * false in case of unsuccessful parsing */ -std::map ExifReader::getExif() +bool ExifReader::parseExif(unsigned char* data, const size_t size) { - const std::streamsize markerSize = 2; - const std::streamsize offsetToTiffHeader = 6; //bytes from Exif size field to the first TIFF header - unsigned char appMarker[markerSize]; - m_exif.erase( m_exif.begin(), m_exif.end() ); - - std::streamsize count; - - bool exifFound = false, stopSearch = false; - while( ( !m_stream.eof() ) && !exifFound && !stopSearch ) + // Populate m_data, then call parseExif() (private) + if( data && size > 0 ) { - m_stream.read( reinterpret_cast(appMarker), markerSize ); - count = m_stream.gcount(); - if( count < markerSize ) - { - break; - } - unsigned char marker = appMarker[1]; - size_t bytesToSkip; - size_t exifSize; - switch( marker ) - { - //For all the markers just skip bytes in file pointed by followed two bytes (field size) - case SOF0: case SOF2: case DHT: case DQT: case DRI: case SOS: - case RST0: case RST1: case RST2: case RST3: case RST4: case RST5: case RST6: case RST7: - case APP0: case APP2: case APP3: case APP4: case APP5: case APP6: case APP7: case APP8: - case APP9: case APP10: case APP11: case APP12: case APP13: case APP14: case APP15: - case COM: - bytesToSkip = getFieldSize(); - if (bytesToSkip < markerSize) { - throw ExifParsingError(); - } - m_stream.seekg( static_cast( bytesToSkip - markerSize ), m_stream.cur ); - if ( m_stream.fail() ) { - throw ExifParsingError(); - } - break; - - //SOI and EOI don't have the size field after the marker - case SOI: case EOI: - break; - - case APP1: //actual Exif Marker - exifSize = getFieldSize(); - if (exifSize <= offsetToTiffHeader) { - throw ExifParsingError(); - } - m_data.resize( exifSize - offsetToTiffHeader ); - m_stream.seekg( static_cast( offsetToTiffHeader ), m_stream.cur ); - if ( m_stream.fail() ) { - throw ExifParsingError(); - } - m_stream.read( reinterpret_cast(&m_data[0]), exifSize - offsetToTiffHeader ); - exifFound = true; - break; - - default: //No other markers are expected according to standard. May be a signal of error - stopSearch = true; - break; - } + m_data.assign(data, data + size); } - - if( !exifFound ) + else { - return m_exif; + return false; } - parseExif(); - - return m_exif; -} - -/** - * @brief Get the size of exif field (required to properly ready whole exif from the file) - * This is internal function and is not exposed to client - * - * @return size of exif field in the file - */ -size_t ExifReader::getFieldSize () -{ - unsigned char fieldSize[2]; - m_stream.read( reinterpret_cast(fieldSize), 2 ); - std::streamsize count = m_stream.gcount(); - if (count < 2) - { - return 0; + try { + parseExif(); + if( !m_exif.empty() ) + { + return true; + } + return false; + } + catch( ExifParsingError& ) { + return false; } - return ( fieldSize[0] << 8 ) + fieldSize[1]; } /** diff --git a/modules/imgcodecs/src/exif.hpp b/modules/imgcodecs/src/exif.hpp index dc9a58ab0b..6cc95afb1a 100644 --- a/modules/imgcodecs/src/exif.hpp +++ b/modules/imgcodecs/src/exif.hpp @@ -54,24 +54,6 @@ namespace cv { -/** - * @brief Jpeg markers that can encounter in Jpeg file - */ -enum AppMarkerTypes -{ - SOI = 0xD8, SOF0 = 0xC0, SOF2 = 0xC2, DHT = 0xC4, - DQT = 0xDB, DRI = 0xDD, SOS = 0xDA, - - RST0 = 0xD0, RST1 = 0xD1, RST2 = 0xD2, RST3 = 0xD3, - RST4 = 0xD4, RST5 = 0xD5, RST6 = 0xD6, RST7 = 0xD7, - - APP0 = 0xE0, APP1 = 0xE1, APP2 = 0xE2, APP3 = 0xE3, - APP4 = 0xE4, APP5 = 0xE5, APP6 = 0xE6, APP7 = 0xE7, - APP8 = 0xE8, APP9 = 0xE9, APP10 = 0xEA, APP11 = 0xEB, - APP12 = 0xEC, APP13 = 0xED, APP14 = 0xEE, APP15 = 0xEF, - - COM = 0xFE, EOI = 0xD9 -}; /** * @brief Base Exif tags used by IFD0 (main image) @@ -168,19 +150,22 @@ class ExifReader public: /** * @brief ExifReader constructor. Constructs an object of exif reader - * - * @param [in]stream An istream to look for EXIF bytes from */ - explicit ExifReader( std::istream& stream ); + ExifReader(); ~ExifReader(); /** * @brief Parse the file with exif info * - * @return true if parsing was successful and exif information exists in JpegReader object + * @param [in] data The data buffer to read EXIF data starting with endianness + * @param [in] size The size of the data buffer + * + * @return true if successful parsing + * false if parsing error */ - bool parse(); + + bool parseExif(unsigned char* data, const size_t size); /** * @brief Get tag info by tag number @@ -188,10 +173,10 @@ public: * @param [in] tag The tag number * @return ExifEntru_t structure. Caller has to know what tag it calls in order to extract proper field from the structure ExifEntry_t */ - ExifEntry_t getTag( const ExifTagName tag ); + ExifEntry_t getTag( const ExifTagName tag ) const; + private: - std::istream& m_stream; std::vector m_data; std::map m_exif; Endianess_t m_format; @@ -199,7 +184,6 @@ private: void parseExif(); bool checkTagMark() const; - size_t getFieldSize (); size_t getNumDirEntry( const size_t offsetNumDir ) const; uint32_t getStartOffset() const; uint16_t getExifTag( const size_t offset ) const; @@ -215,7 +199,6 @@ private: u_rational_t getURational( const size_t offset ) const; - std::map getExif(); std::string getString( const size_t offset ) const; std::vector getResolution( const size_t offset ) const; std::vector getWhitePoint( const size_t offset ) const; diff --git a/modules/imgcodecs/src/grfmt_base.cpp b/modules/imgcodecs/src/grfmt_base.cpp index 574ce9e87a..88f1c04f30 100644 --- a/modules/imgcodecs/src/grfmt_base.cpp +++ b/modules/imgcodecs/src/grfmt_base.cpp @@ -55,6 +55,11 @@ BaseImageDecoder::BaseImageDecoder() m_scale_denom = 1; } + +ExifEntry_t BaseImageDecoder::getExifTag(const ExifTagName tag) const +{ + return m_exif.getTag(tag); +} bool BaseImageDecoder::setSource( const String& filename ) { m_filename = filename; diff --git a/modules/imgcodecs/src/grfmt_base.hpp b/modules/imgcodecs/src/grfmt_base.hpp index 7d75636cf5..816bef98fb 100644 --- a/modules/imgcodecs/src/grfmt_base.hpp +++ b/modules/imgcodecs/src/grfmt_base.hpp @@ -45,6 +45,7 @@ #include "utils.hpp" #include "bitstrm.hpp" +#include "exif.hpp" namespace cv { @@ -65,6 +66,7 @@ public: int height() const { return m_height; } virtual int type() const { return m_type; } + ExifEntry_t getExifTag(const ExifTagName tag) const; virtual bool setSource( const String& filename ); virtual bool setSource( const Mat& buf ); virtual int setScale( const int& scale_denom ); @@ -87,6 +89,7 @@ protected: String m_signature; Mat m_buf; bool m_buf_supported; + ExifReader m_exif; }; diff --git a/modules/imgcodecs/src/grfmt_jpeg.cpp b/modules/imgcodecs/src/grfmt_jpeg.cpp index cbc1bedaee..3dd9d68771 100644 --- a/modules/imgcodecs/src/grfmt_jpeg.cpp +++ b/modules/imgcodecs/src/grfmt_jpeg.cpp @@ -244,6 +244,7 @@ bool JpegDecoder::readHeader() if (state->cinfo.src != 0) { + jpeg_save_markers(&state->cinfo, APP1, 0xffff); jpeg_read_header( &state->cinfo, TRUE ); state->cinfo.scale_num=1; @@ -456,6 +457,29 @@ bool JpegDecoder::readData( Mat& img ) } } + // Check for Exif marker APP1 + jpeg_saved_marker_ptr exif_marker = NULL; + jpeg_saved_marker_ptr cmarker = cinfo->marker_list; + while( cmarker && exif_marker == NULL ) + { + if (cmarker->marker == APP1) + exif_marker = cmarker; + + cmarker = cmarker->next; + } + + // Parse Exif data + if( exif_marker ) + { + const std::streamsize offsetToTiffHeader = 6; //bytes from Exif size field to the first TIFF header + + if (exif_marker->data_length > offsetToTiffHeader) + { + m_exif.parseExif(exif_marker->data + offsetToTiffHeader, exif_marker->data_length - offsetToTiffHeader); + } + } + + jpeg_start_decompress( cinfo ); buffer = (*cinfo->mem->alloc_sarray)((j_common_ptr)cinfo, diff --git a/modules/imgcodecs/src/grfmt_jpeg.hpp b/modules/imgcodecs/src/grfmt_jpeg.hpp index 90d80b4b59..e7c8c25457 100644 --- a/modules/imgcodecs/src/grfmt_jpeg.hpp +++ b/modules/imgcodecs/src/grfmt_jpeg.hpp @@ -52,6 +52,25 @@ namespace cv { +/** +* @brief Jpeg markers that can be encountered in a Jpeg file +*/ +enum AppMarkerTypes +{ + SOI = 0xD8, SOF0 = 0xC0, SOF2 = 0xC2, DHT = 0xC4, + DQT = 0xDB, DRI = 0xDD, SOS = 0xDA, + + RST0 = 0xD0, RST1 = 0xD1, RST2 = 0xD2, RST3 = 0xD3, + RST4 = 0xD4, RST5 = 0xD5, RST6 = 0xD6, RST7 = 0xD7, + + APP0 = 0xE0, APP1 = 0xE1, APP2 = 0xE2, APP3 = 0xE3, + APP4 = 0xE4, APP5 = 0xE5, APP6 = 0xE6, APP7 = 0xE7, + APP8 = 0xE8, APP9 = 0xE9, APP10 = 0xEA, APP11 = 0xEB, + APP12 = 0xEC, APP13 = 0xED, APP14 = 0xEE, APP15 = 0xEF, + + COM = 0xFE, EOI = 0xD9 +}; + class JpegDecoder CV_FINAL : public BaseImageDecoder { diff --git a/modules/imgcodecs/src/grfmt_png.cpp b/modules/imgcodecs/src/grfmt_png.cpp index b533cd849f..9e1a2d4c71 100644 --- a/modules/imgcodecs/src/grfmt_png.cpp +++ b/modules/imgcodecs/src/grfmt_png.cpp @@ -284,6 +284,22 @@ bool PngDecoder::readData( Mat& img ) png_read_image( png_ptr, buffer ); png_read_end( png_ptr, end_info ); +#ifdef PNG_eXIf_SUPPORTED + png_uint_32 num_exif = 0; + png_bytep exif = 0; + + // Exif info could be in info_ptr (intro_info) or end_info per specification + if( png_get_valid(png_ptr, info_ptr, PNG_INFO_eXIf) ) + png_get_eXIf_1(png_ptr, info_ptr, &num_exif, &exif); + else if( png_get_valid(png_ptr, end_info, PNG_INFO_eXIf) ) + png_get_eXIf_1(png_ptr, end_info, &num_exif, &exif); + + if( exif && num_exif > 0 ) + { + m_exif.parseExif(exif, num_exif); + } +#endif + result = true; } } diff --git a/modules/imgcodecs/src/loadsave.cpp b/modules/imgcodecs/src/loadsave.cpp index 1536debead..350042cd7d 100644 --- a/modules/imgcodecs/src/loadsave.cpp +++ b/modules/imgcodecs/src/loadsave.cpp @@ -361,48 +361,15 @@ static void ExifTransform(int orientation, Mat& img) } } -static void ApplyExifOrientation(const String& filename, Mat& img) +static void ApplyExifOrientation(ExifEntry_t orientationTag, Mat& img) { int orientation = IMAGE_ORIENTATION_TL; - if (filename.size() > 0) + if (orientationTag.tag != INVALID_TAG) { - std::ifstream stream( filename.c_str(), std::ios_base::in | std::ios_base::binary ); - ExifReader reader( stream ); - if( reader.parse() ) - { - ExifEntry_t entry = reader.getTag( ORIENTATION ); - if (entry.tag != INVALID_TAG) - { - orientation = entry.field_u16; //orientation is unsigned short, so check field_u16 - } - } - stream.close(); - } - - ExifTransform(orientation, img); -} - -static void ApplyExifOrientation(const Mat& buf, Mat& img) -{ - int orientation = IMAGE_ORIENTATION_TL; - - if( buf.isContinuous() ) - { - ByteStreamBuffer bsb( reinterpret_cast(buf.data), buf.total() * buf.elemSize() ); - std::istream stream( &bsb ); - ExifReader reader( stream ); - if( reader.parse() ) - { - ExifEntry_t entry = reader.getTag( ORIENTATION ); - if (entry.tag != INVALID_TAG) - { - orientation = entry.field_u16; //orientation is unsigned short, so check field_u16 - } - } + orientation = orientationTag.field_u16; //orientation is unsigned short, so check field_u16 + ExifTransform(orientation, img); } - - ExifTransform(orientation, img); } /** @@ -518,6 +485,12 @@ imread_( const String& filename, int flags, Mat& mat ) resize( mat, mat, Size( size.width / scale_denom, size.height / scale_denom ), 0, 0, INTER_LINEAR_EXACT); } + /// optionally rotate the data if EXIF orientation flag says so + if (!mat.empty() && (flags & IMREAD_IGNORE_ORIENTATION) == 0 && flags != IMREAD_UNCHANGED ) + { + ApplyExifOrientation(decoder->getExifTag(ORIENTATION), mat); + } + return true; } @@ -614,7 +587,7 @@ imreadmulti_(const String& filename, int flags, std::vector& mats) // optionally rotate the data if EXIF' orientation flag says so if( (flags & IMREAD_IGNORE_ORIENTATION) == 0 && flags != IMREAD_UNCHANGED ) { - ApplyExifOrientation(filename, mat); + ApplyExifOrientation(decoder->getExifTag(ORIENTATION), mat); } mats.push_back(mat); @@ -645,12 +618,6 @@ Mat imread( const String& filename, int flags ) /// load the data imread_( filename, flags, img ); - /// optionally rotate the data if EXIF' orientation flag says so - if( !img.empty() && (flags & IMREAD_IGNORE_ORIENTATION) == 0 && flags != IMREAD_UNCHANGED ) - { - ApplyExifOrientation(filename, img); - } - /// return a reference to the data return img; } @@ -889,6 +856,12 @@ imdecode_( const Mat& buf, int flags, Mat& mat ) resize(mat, mat, Size( size.width / scale_denom, size.height / scale_denom ), 0, 0, INTER_LINEAR_EXACT); } + /// optionally rotate the data if EXIF' orientation flag says so + if (!mat.empty() && (flags & IMREAD_IGNORE_ORIENTATION) == 0 && flags != IMREAD_UNCHANGED) + { + ApplyExifOrientation(decoder->getExifTag(ORIENTATION), mat); + } + return true; } @@ -900,12 +873,6 @@ Mat imdecode( InputArray _buf, int flags ) Mat buf = _buf.getMat(), img; imdecode_( buf, flags, img ); - /// optionally rotate the data if EXIF' orientation flag says so - if( !img.empty() && (flags & IMREAD_IGNORE_ORIENTATION) == 0 && flags != IMREAD_UNCHANGED ) - { - ApplyExifOrientation(buf, img); - } - return img; } @@ -917,12 +884,6 @@ Mat imdecode( InputArray _buf, int flags, Mat* dst ) dst = dst ? dst : &img; imdecode_( buf, flags, *dst ); - /// optionally rotate the data if EXIF' orientation flag says so - if( !dst->empty() && (flags & IMREAD_IGNORE_ORIENTATION) == 0 && flags != IMREAD_UNCHANGED ) - { - ApplyExifOrientation(buf, *dst); - } - return *dst; } diff --git a/modules/imgcodecs/test/test_png.cpp b/modules/imgcodecs/test/test_png.cpp index 051cbf69e6..74920ee9ae 100644 --- a/modules/imgcodecs/test/test_png.cpp +++ b/modules/imgcodecs/test/test_png.cpp @@ -7,6 +7,12 @@ namespace opencv_test { namespace { #ifdef HAVE_PNG +#ifdef HAVE_LIBPNG_PNG_H +#include +#else +#include +#endif + TEST(Imgcodecs_Png, write_big) { const string root = cvtest::TS::ptr()->get_data_path(); @@ -93,6 +99,97 @@ TEST(Imgcodecs_Png, read_color_palette_with_alpha) EXPECT_EQ(img.at(0, 1), Vec3b(0, 0, 255)); } +#ifdef PNG_eXIf_SUPPORTED +/** + * Test for check whether reading exif orientation tag was processed successfully or not + * The test info is the set of 8 images named testExifRotate_{1 to 8}.png + * The test image is the square 10x10 points divided by four sub-squares: + * (R corresponds to Red, G to Green, B to Blue, W to white) + * --------- --------- + * | R | G | | G | R | + * |-------| - (tag 1) |-------| - (tag 2) + * | B | W | | W | B | + * --------- --------- + * + * --------- --------- + * | W | B | | B | W | + * |-------| - (tag 3) |-------| - (tag 4) + * | G | R | | R | G | + * --------- --------- + * + * --------- --------- + * | R | B | | G | W | + * |-------| - (tag 5) |-------| - (tag 6) + * | G | W | | R | B | + * --------- --------- + * + * --------- --------- + * | W | G | | B | R | + * |-------| - (tag 7) |-------| - (tag 8) + * | B | R | | W | G | + * --------- --------- + * + * + * Every image contains exif field with orientation tag (0x112) + * After reading each image and applying the orientation tag, + * the resulting image should be: + * --------- + * | R | G | + * |-------| + * | B | W | + * --------- + * + */ + +typedef testing::TestWithParam Imgcodecs_PNG_Exif; + +// Solution to issue 16579: PNG read doesn't support Exif orientation data +TEST_P(Imgcodecs_PNG_Exif, exif_orientation) +{ + const string root = cvtest::TS::ptr()->get_data_path(); + const string filename = root + GetParam(); + const int colorThresholdHigh = 250; + const int colorThresholdLow = 5; + + Mat m_img = imread(filename); + ASSERT_FALSE(m_img.empty()); + Vec3b vec; + + //Checking the first quadrant (with supposed red) + vec = m_img.at(2, 2); //some point inside the square + EXPECT_LE(vec.val[0], colorThresholdLow); + EXPECT_LE(vec.val[1], colorThresholdLow); + EXPECT_GE(vec.val[2], colorThresholdHigh); + + //Checking the second quadrant (with supposed green) + vec = m_img.at(2, 7); //some point inside the square + EXPECT_LE(vec.val[0], colorThresholdLow); + EXPECT_GE(vec.val[1], colorThresholdHigh); + EXPECT_LE(vec.val[2], colorThresholdLow); + + //Checking the third quadrant (with supposed blue) + vec = m_img.at(7, 2); //some point inside the square + EXPECT_GE(vec.val[0], colorThresholdHigh); + EXPECT_LE(vec.val[1], colorThresholdLow); + EXPECT_LE(vec.val[2], colorThresholdLow); +} + +const string exif_files[] = +{ + "readwrite/testExifOrientation_1.png", + "readwrite/testExifOrientation_2.png", + "readwrite/testExifOrientation_3.png", + "readwrite/testExifOrientation_4.png", + "readwrite/testExifOrientation_5.png", + "readwrite/testExifOrientation_6.png", + "readwrite/testExifOrientation_7.png", + "readwrite/testExifOrientation_8.png" +}; + +INSTANTIATE_TEST_CASE_P(ExifFiles, Imgcodecs_PNG_Exif, + testing::ValuesIn(exif_files)); +#endif // PNG_eXIf_SUPPORTED + #endif // HAVE_PNG }} // namespace diff --git a/modules/stitching/perf/opencl/perf_stitch.cpp b/modules/stitching/perf/opencl/perf_stitch.cpp index 1b6e43304a..4eccf8d50b 100644 --- a/modules/stitching/perf/opencl/perf_stitch.cpp +++ b/modules/stitching/perf/opencl/perf_stitch.cpp @@ -19,7 +19,7 @@ namespace ocl { typedef TestBaseWithParam stitch; -#ifdef HAVE_OPENCV_XFEATURES2D +#if defined(HAVE_OPENCV_XFEATURES2D) && defined(OPENCV_ENABLE_NONFREE) #define TEST_DETECTORS testing::Values("surf", "orb", "akaze") #else #define TEST_DETECTORS testing::Values("orb", "akaze") diff --git a/modules/stitching/perf/perf_estimators.cpp b/modules/stitching/perf/perf_estimators.cpp index dd1d44e077..9e8f9e3962 100644 --- a/modules/stitching/perf/perf_estimators.cpp +++ b/modules/stitching/perf/perf_estimators.cpp @@ -8,7 +8,7 @@ using namespace perf; typedef TestBaseWithParam > bundleAdjuster; -#ifdef HAVE_OPENCV_XFEATURES2D +#if defined(HAVE_OPENCV_XFEATURES2D) && defined(OPENCV_ENABLE_NONFREE) #define TEST_DETECTORS testing::Values("surf", "orb") #else #define TEST_DETECTORS testing::Values("orb") diff --git a/modules/stitching/perf/perf_matchers.cpp b/modules/stitching/perf/perf_matchers.cpp index f0fb70630c..975d3395fd 100644 --- a/modules/stitching/perf/perf_matchers.cpp +++ b/modules/stitching/perf/perf_matchers.cpp @@ -17,7 +17,7 @@ typedef TestBaseWithParam matchVector; #define ORB_MATCH_CONFIDENCE 0.3f #define WORK_MEGAPIX 0.6 -#ifdef HAVE_OPENCV_XFEATURES2D +#if defined(HAVE_OPENCV_XFEATURES2D) && defined(OPENCV_ENABLE_NONFREE) #define TEST_DETECTORS testing::Values("surf", "orb") #else #define TEST_DETECTORS testing::Values("orb") diff --git a/modules/stitching/perf/perf_precomp.hpp b/modules/stitching/perf/perf_precomp.hpp index cb89f16eef..45997a3bd9 100644 --- a/modules/stitching/perf/perf_precomp.hpp +++ b/modules/stitching/perf/perf_precomp.hpp @@ -15,7 +15,7 @@ static inline Ptr getFeatureFinder(const std::string& name) { if (name == "orb") return ORB::create(); -#ifdef HAVE_OPENCV_XFEATURES2D +#if defined(HAVE_OPENCV_XFEATURES2D) && defined(OPENCV_ENABLE_NONFREE) else if (name == "surf") return xfeatures2d::SURF::create(); #endif diff --git a/modules/stitching/perf/perf_stich.cpp b/modules/stitching/perf/perf_stich.cpp index 24c000dc91..d8b96974dd 100644 --- a/modules/stitching/perf/perf_stich.cpp +++ b/modules/stitching/perf/perf_stich.cpp @@ -17,7 +17,7 @@ typedef TestBaseWithParam stitchExposureCompensation; typedef TestBaseWithParam > stitchDatasets; typedef TestBaseWithParam> stitchExposureCompMultiFeed; -#ifdef HAVE_OPENCV_XFEATURES2D +#if defined(HAVE_OPENCV_XFEATURES2D) && defined(OPENCV_ENABLE_NONFREE) #define TEST_DETECTORS testing::Values("surf", "orb", "akaze") #else #define TEST_DETECTORS testing::Values("orb", "akaze") diff --git a/platforms/js/build_js.py b/platforms/js/build_js.py index 0981c6656b..d33c62c213 100644 --- a/platforms/js/build_js.py +++ b/platforms/js/build_js.py @@ -248,7 +248,7 @@ if __name__ == "__main__": log.debug("Args: %s", args) - os.environ["OPENCV_JS_WHITELIST"] = args.config + os.environ["OPENCV_JS_WHITELIST"] = os.path.abspath(args.config) if 'EMMAKEN_JUST_CONFIGURE' in os.environ: del os.environ['EMMAKEN_JUST_CONFIGURE'] # avoid linker errors with NODERAWFS message then using 'emcmake' launcher diff --git a/samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp b/samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp index 201466e99b..818fcb4735 100644 --- a/samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp +++ b/samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp @@ -24,23 +24,16 @@ int main(int argc, char *argv[]) return -1; } - // Show source image + // Show the source image imshow("Source Image", src); //! [load_image] //! [black_bg] // Change the background from white to black, since that will help later to extract // better results during the use of Distance Transform - for ( int i = 0; i < src.rows; i++ ) { - for ( int j = 0; j < src.cols; j++ ) { - if ( src.at(i, j) == Vec3b(255,255,255) ) - { - src.at(i, j)[0] = 0; - src.at(i, j)[1] = 0; - src.at(i, j)[2] = 0; - } - } - } + Mat mask; + inRange(src, Scalar(255, 255, 255), Scalar(255, 255, 255), mask); + src.setTo(Scalar(0, 0, 0), mask); // Show output image imshow("Black Background Image", src); @@ -124,7 +117,9 @@ int main(int argc, char *argv[]) // Draw the background marker circle(markers, Point(5,5), 3, Scalar(255), -1); - imshow("Markers", markers*10000); + Mat markers8u; + markers.convertTo(markers8u, CV_8U, 10); + imshow("Markers", markers8u); //! [seeds] //! [watershed] diff --git a/samples/python/tutorial_code/ImgTrans/distance_transformation/imageSegmentation.py b/samples/python/tutorial_code/ImgTrans/distance_transformation/imageSegmentation.py index 2388a414c0..e8add484a4 100644 --- a/samples/python/tutorial_code/ImgTrans/distance_transformation/imageSegmentation.py +++ b/samples/python/tutorial_code/ImgTrans/distance_transformation/imageSegmentation.py @@ -102,7 +102,8 @@ for i in range(len(contours)): # Draw the background marker cv.circle(markers, (5,5), 3, (255,255,255), -1) -cv.imshow('Markers', markers*10000) +markers_8u = (markers * 10).astype('uint8') +cv.imshow('Markers', markers_8u) ## [seeds] ## [watershed]