From 238781342826aa9decae3b5501e440c6fa08f06a Mon Sep 17 00:00:00 2001 From: Suleyman TURKMEN Date: Sun, 7 Feb 2021 05:42:55 +0300 Subject: [PATCH 1/7] Update imageSegmentation.cpp, imageSegmentation.py --- .../ImgTrans/imageSegmentation.cpp | 19 +++++++------------ .../imageSegmentation.py | 3 ++- 2 files changed, 9 insertions(+), 13 deletions(-) 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 0ab56cfb30..e5ed43f115 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] From 847b16fb769b18d1c317fc9e6dde328f2e1367d2 Mon Sep 17 00:00:00 2001 From: Vincent Rabaud Date: Mon, 8 Feb 2021 16:53:37 +0100 Subject: [PATCH 2/7] Disable thread sanitization when CV_USE_GLOBAL_WORKERS_COND_VAR is not set. This fixes #19463 --- modules/core/src/parallel_impl.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/modules/core/src/parallel_impl.cpp b/modules/core/src/parallel_impl.cpp index 8638af9d67..7f30c9d5f3 100644 --- a/modules/core/src/parallel_impl.cpp +++ b/modules/core/src/parallel_impl.cpp @@ -382,6 +382,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 From 3b9aebee118ed2e467aebc277f721f3c91345b94 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Tue, 9 Feb 2021 16:54:06 +0300 Subject: [PATCH 3/7] Not not test stitiching with SURF, if NONFREE is disabled --- modules/stitching/perf/opencl/perf_stitch.cpp | 2 +- modules/stitching/perf/perf_estimators.cpp | 2 +- modules/stitching/perf/perf_matchers.cpp | 2 +- modules/stitching/perf/perf_precomp.hpp | 2 ++ modules/stitching/perf/perf_stich.cpp | 2 +- 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/modules/stitching/perf/opencl/perf_stitch.cpp b/modules/stitching/perf/opencl/perf_stitch.cpp index 9a84d3edc1..6e791a9530 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 7b2b533927..9d0a0426a9 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 0ab4340673..bd21de82ae 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 fcbdc68c25..7359f8f600 100644 --- a/modules/stitching/perf/perf_precomp.hpp +++ b/modules/stitching/perf/perf_precomp.hpp @@ -11,8 +11,10 @@ static inline Ptr getFeatureFinder(const std::string& na { if (name == "orb") return makePtr(); +#if defined(HAVE_OPENCV_XFEATURES2D) && defined(OPENCV_ENABLE_NONFREE) else if (name == "surf") return makePtr(); +#endif else if (name == "akaze") return makePtr(); else diff --git a/modules/stitching/perf/perf_stich.cpp b/modules/stitching/perf/perf_stich.cpp index 22fb3e35e6..0708b6ea79 100644 --- a/modules/stitching/perf/perf_stich.cpp +++ b/modules/stitching/perf/perf_stich.cpp @@ -16,7 +16,7 @@ typedef TestBaseWithParam stitch; typedef TestBaseWithParam stitchExposureCompensation; typedef TestBaseWithParam > stitchDatasets; -#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") From 650836d70068f77bc6e7d322a101d27755c459cc Mon Sep 17 00:00:00 2001 From: Rachel A Date: Mon, 1 Feb 2021 12:16:44 -0800 Subject: [PATCH 4/7] Exif parsing for PNG files to support Exif orientation tag. Moved decoder specific Exif parsing to JPEG and PNG decoders, respectively. Issue 16579 --- modules/imgcodecs/src/exif.cpp | 132 +++++---------------------- modules/imgcodecs/src/exif.hpp | 37 ++------ modules/imgcodecs/src/grfmt_base.cpp | 5 + modules/imgcodecs/src/grfmt_base.hpp | 3 + modules/imgcodecs/src/grfmt_jpeg.cpp | 24 +++++ modules/imgcodecs/src/grfmt_jpeg.hpp | 19 ++++ modules/imgcodecs/src/grfmt_png.cpp | 16 ++++ modules/imgcodecs/src/loadsave.cpp | 73 ++++----------- modules/imgcodecs/test/test_png.cpp | 97 ++++++++++++++++++++ 9 files changed, 216 insertions(+), 190 deletions(-) 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 b7032c1723..c1a8854a42 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 ba5dab41df..758ac512e8 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 44c458c727..c8fcbea7ee 100644 --- a/modules/imgcodecs/src/loadsave.cpp +++ b/modules/imgcodecs/src/loadsave.cpp @@ -354,48 +354,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); } /** @@ -537,6 +504,12 @@ imread_( const String& filename, int flags, int hdrtype, Mat* mat=0 ) 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 && !mat->empty() && (flags & IMREAD_IGNORE_ORIENTATION) == 0 && flags != IMREAD_UNCHANGED ) + { + ApplyExifOrientation(decoder->getExifTag(ORIENTATION), *mat); + } + return hdrtype == LOAD_CVMAT ? (void*)matrix : hdrtype == LOAD_IMAGE ? (void*)image : (void*)mat; } @@ -634,7 +607,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); @@ -665,12 +638,6 @@ Mat imread( const String& filename, int flags ) /// load the data imread_( filename, flags, LOAD_MAT, &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; } @@ -932,6 +899,12 @@ imdecode_( const Mat& buf, int flags, int hdrtype, Mat* mat=0 ) 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); + } + decoder.release(); return hdrtype == LOAD_CVMAT ? (void*)matrix : @@ -946,12 +919,6 @@ Mat imdecode( InputArray _buf, int flags ) Mat buf = _buf.getMat(), img; imdecode_( buf, flags, LOAD_MAT, &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; } @@ -963,12 +930,6 @@ Mat imdecode( InputArray _buf, int flags, Mat* dst ) dst = dst ? dst : &img; imdecode_( buf, flags, LOAD_MAT, 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 From a3fb64d1cdc9838699f040731d5e812de0431e87 Mon Sep 17 00:00:00 2001 From: Mieszko Boczkowski Date: Wed, 10 Feb 2021 11:42:29 +0100 Subject: [PATCH 5/7] Pass custom config file path as absolute --- platforms/js/build_js.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platforms/js/build_js.py b/platforms/js/build_js.py index 37ff956de2..7f9ce3ea8b 100644 --- a/platforms/js/build_js.py +++ b/platforms/js/build_js.py @@ -246,7 +246,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 From 6ee23c9b85e33e0c9af417467e39a8165ef9c89e Mon Sep 17 00:00:00 2001 From: Francesco Petrogalli <25690309+fpetrogalli@users.noreply.github.com> Date: Thu, 11 Feb 2021 13:24:09 +0000 Subject: [PATCH 6/7] Merge pull request #19486 from fpetrogalli:dotprod_fast-3.4 * [hal][neon] Optimize the v_dotprod_fast intrinsics for aarch64. On Armv8 in AArch64 execution mode, we can skip the sequence v_(vget_high_(x), vget_high_(y)) in favour of v_high_(x, y) This has better changes for recent compilers to use less data movement operations and better register allocation. See for example: https://godbolt.org/z/bPq7vd * [hal][neon] Fix build failure on armv7. * [hal][neon] Address review comments in PR. PR: https://github.com/opencv/opencv/pull/19486 * [hal][neon] Define macro to check for the AArch64 execution state of Armv8. * [hal][neon] Fix macro definition for AArch64. The fix is needed to prevent warnings when building for Armv7. --- .../include/opencv2/core/hal/intrin_neon.hpp | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) 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 From 84a290097bc5fcef4272567ad293fde614cff00b Mon Sep 17 00:00:00 2001 From: Zhuo Zhang Date: Fri, 12 Feb 2021 22:19:35 +0800 Subject: [PATCH 7/7] fix link order: put zlib after png/tiff/openexr Previous link dependency: imgcodecs --> zlib --> libpng this can generate imgcodecs shared lib, until Visual Studio integrated with vcpkg, which will additionally specify LIBPATH, pointing to vcpkg installed zlib (if any), which links the wrong zlib. Fixed link dependency: imgcodecs --> libpng --> zlib in this fixed case, symbols in zlib referenced in libpng will be found in the build-from-source static zlib, instead of the vcpkg one. related discussion: - https://github.com/microsoft/vcpkg/issues/16165 - https://github.com/opencv/opencv/issues/17051 - https://github.com/opencv/opencv/issues/10576 MSVC linking order reference pages: - https://docs.microsoft.com/en-us/cpp/build/reference/link-input-files?view=msvc-160 for link order - https://docs.microsoft.com/en-us/cpp/build/reference/linking?view=msvc-160 LIB environment variable, for library file searching - https://docs.microsoft.com/en-us/cpp/build/reference/libpath-additional-libpath?view=msvc-160 LIBPATH option, for library file searching --- modules/imgcodecs/CMakeLists.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/imgcodecs/CMakeLists.txt b/modules/imgcodecs/CMakeLists.txt index 92f380ea22..1b832eaa3a 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}) @@ -58,6 +53,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})