Merge remote-tracking branch 'upstream/3.4' into merge-3.4

pull/19517/head
Alexander Alekhin 4 years ago
commit e5d78960c6
  1. 38
      modules/core/include/opencv2/core/hal/intrin_neon.hpp
  2. 10
      modules/core/src/parallel_impl.cpp
  3. 10
      modules/imgcodecs/CMakeLists.txt
  4. 132
      modules/imgcodecs/src/exif.cpp
  5. 37
      modules/imgcodecs/src/exif.hpp
  6. 5
      modules/imgcodecs/src/grfmt_base.cpp
  7. 3
      modules/imgcodecs/src/grfmt_base.hpp
  8. 24
      modules/imgcodecs/src/grfmt_jpeg.cpp
  9. 19
      modules/imgcodecs/src/grfmt_jpeg.hpp
  10. 16
      modules/imgcodecs/src/grfmt_png.cpp
  11. 73
      modules/imgcodecs/src/loadsave.cpp
  12. 97
      modules/imgcodecs/test/test_png.cpp
  13. 2
      modules/stitching/perf/opencl/perf_stitch.cpp
  14. 2
      modules/stitching/perf/perf_estimators.cpp
  15. 2
      modules/stitching/perf/perf_matchers.cpp
  16. 2
      modules/stitching/perf/perf_precomp.hpp
  17. 2
      modules/stitching/perf/perf_stich.cpp
  18. 2
      platforms/js/build_js.py
  19. 19
      samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp
  20. 3
      samples/python/tutorial_code/ImgTrans/distance_transformation/imageSegmentation.py

@ -62,6 +62,22 @@ CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN
#define CV_SIMD128_64F 0 #define CV_SIMD128_64F 0
#endif #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 // TODO
#define CV_NEON_DOT 0 #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 // 16 >> 32
inline v_int32x4 v_dotprod_fast(const v_int16x8& a, const v_int16x8& b) 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 a0 = vget_low_s16(a.val);
int16x4_t a1 = vget_high_s16(a.val); int16x4_t a1 = vget_high_s16(a.val);
int16x4_t b0 = vget_low_s16(b.val); int16x4_t b0 = vget_low_s16(b.val);
int16x4_t b1 = vget_high_s16(b.val); int16x4_t b1 = vget_high_s16(b.val);
int32x4_t p = vmull_s16(a0, b0); int32x4_t p = vmull_s16(a0, b0);
return v_int32x4(vmlal_s16(p, a1, b1)); 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) 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 a0 = vget_low_s16(a.val);
int16x4_t a1 = vget_high_s16(a.val); int16x4_t a1 = vget_high_s16(a.val);
int16x4_t b0 = vget_low_s16(b.val); int16x4_t b0 = vget_low_s16(b.val);
int16x4_t b1 = vget_high_s16(b.val); int16x4_t b1 = vget_high_s16(b.val);
int32x4_t p = vmlal_s16(c.val, a0, b0); int32x4_t p = vmlal_s16(c.val, a0, b0);
return v_int32x4(vmlal_s16(p, a1, b1)); return v_int32x4(vmlal_s16(p, a1, b1));
#endif
} }
// 32 >> 64 // 32 >> 64
inline v_int64x2 v_dotprod_fast(const v_int32x4& a, const v_int32x4& b) 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 a0 = vget_low_s32(a.val);
int32x2_t a1 = vget_high_s32(a.val); int32x2_t a1 = vget_high_s32(a.val);
int32x2_t b0 = vget_low_s32(b.val); int32x2_t b0 = vget_low_s32(b.val);
int32x2_t b1 = vget_high_s32(b.val); int32x2_t b1 = vget_high_s32(b.val);
int64x2_t p = vmull_s32(a0, b0); int64x2_t p = vmull_s32(a0, b0);
return v_int64x2(vmlal_s32(p, a1, b1)); 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) 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 a0 = vget_low_s32(a.val);
int32x2_t a1 = vget_high_s32(a.val); int32x2_t a1 = vget_high_s32(a.val);
int32x2_t b0 = vget_low_s32(b.val); int32x2_t b0 = vget_low_s32(b.val);
int32x2_t b1 = vget_high_s32(b.val); int32x2_t b1 = vget_high_s32(b.val);
int64x2_t p = vmlal_s32(c.val, a0, b0); int64x2_t p = vmlal_s32(c.val, a0, b0);
return v_int64x2(vmlal_s32(p, a1, b1)); return v_int64x2(vmlal_s32(p, a1, b1));
#endif
} }
// 8 >> 32 // 8 >> 32
@ -1292,7 +1328,7 @@ inline int64 v_reduce_sum(const v_int64x2& a)
#if CV_SIMD128_64F #if CV_SIMD128_64F
inline double v_reduce_sum(const v_float64x2& a) 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 #endif

@ -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 WorkerThread::thread_body()
{ {
(void)cv::utils::getThreadID(); // notify OpenCV about new thread (void)cv::utils::getThreadID(); // notify OpenCV about new thread

@ -13,11 +13,6 @@ if(HAVE_WINRT_CX AND NOT WINRT)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /ZW") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /ZW")
endif() 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) if(HAVE_JPEG)
ocv_include_directories(${JPEG_INCLUDE_DIR} ${${JPEG_LIBRARY}_BINARY_DIR}) ocv_include_directories(${JPEG_INCLUDE_DIR} ${${JPEG_LIBRARY}_BINARY_DIR})
list(APPEND GRFMT_LIBS ${JPEG_LIBRARIES}) list(APPEND GRFMT_LIBS ${JPEG_LIBRARIES})
@ -63,6 +58,11 @@ if(HAVE_OPENEXR)
list(APPEND GRFMT_LIBS ${OPENEXR_LIBRARIES}) list(APPEND GRFMT_LIBS ${OPENEXR_LIBRARIES})
endif() 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) if(HAVE_GDAL)
include_directories(SYSTEM ${GDAL_INCLUDE_DIR}) include_directories(SYSTEM ${GDAL_INCLUDE_DIR})
list(APPEND GRFMT_LIBS ${GDAL_LIBRARY}) list(APPEND GRFMT_LIBS ${GDAL_LIBRARY})

@ -62,7 +62,7 @@ ExifEntry_t::ExifEntry_t() :
/** /**
* @brief ExifReader constructor * @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 * @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 * @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; ExifEntry_t entry;
std::map<int, ExifEntry_t>::iterator it = m_exif.find(tag); std::map<int, ExifEntry_t>::const_iterator it = m_exif.find(tag);
if( it != m_exif.end() ) 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) * @brief Parsing the exif data buffer and prepare (internal) exif directory
* This is internal function and is not exposed to client *
* @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<int, ExifEntry_t > ExifReader::getExif() bool ExifReader::parseExif(unsigned char* data, const size_t size)
{ {
const std::streamsize markerSize = 2; // Populate m_data, then call parseExif() (private)
const std::streamsize offsetToTiffHeader = 6; //bytes from Exif size field to the first TIFF header if( data && size > 0 )
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 )
{ {
m_stream.read( reinterpret_cast<char*>(appMarker), markerSize ); m_data.assign(data, data + size);
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<long>( 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<long>( offsetToTiffHeader ), m_stream.cur );
if ( m_stream.fail() ) {
throw ExifParsingError();
}
m_stream.read( reinterpret_cast<char*>(&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;
}
} }
else
if( !exifFound )
{ {
return m_exif; return false;
} }
parseExif(); try {
parseExif();
return m_exif; if( !m_exif.empty() )
} {
return true;
/** }
* @brief Get the size of exif field (required to properly ready whole exif from the file) return false;
* This is internal function and is not exposed to client }
* catch( ExifParsingError& ) {
* @return size of exif field in the file return false;
*/
size_t ExifReader::getFieldSize ()
{
unsigned char fieldSize[2];
m_stream.read( reinterpret_cast<char*>(fieldSize), 2 );
std::streamsize count = m_stream.gcount();
if (count < 2)
{
return 0;
} }
return ( fieldSize[0] << 8 ) + fieldSize[1];
} }
/** /**

@ -54,24 +54,6 @@
namespace cv 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) * @brief Base Exif tags used by IFD0 (main image)
@ -168,19 +150,22 @@ class ExifReader
public: public:
/** /**
* @brief ExifReader constructor. Constructs an object of exif reader * @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(); ~ExifReader();
/** /**
* @brief Parse the file with exif info * @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 * @brief Get tag info by tag number
@ -188,10 +173,10 @@ public:
* @param [in] tag The tag number * @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 * @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: private:
std::istream& m_stream;
std::vector<unsigned char> m_data; std::vector<unsigned char> m_data;
std::map<int, ExifEntry_t > m_exif; std::map<int, ExifEntry_t > m_exif;
Endianess_t m_format; Endianess_t m_format;
@ -199,7 +184,6 @@ private:
void parseExif(); void parseExif();
bool checkTagMark() const; bool checkTagMark() const;
size_t getFieldSize ();
size_t getNumDirEntry( const size_t offsetNumDir ) const; size_t getNumDirEntry( const size_t offsetNumDir ) const;
uint32_t getStartOffset() const; uint32_t getStartOffset() const;
uint16_t getExifTag( const size_t offset ) const; uint16_t getExifTag( const size_t offset ) const;
@ -215,7 +199,6 @@ private:
u_rational_t getURational( const size_t offset ) const; u_rational_t getURational( const size_t offset ) const;
std::map<int, ExifEntry_t > getExif();
std::string getString( const size_t offset ) const; std::string getString( const size_t offset ) const;
std::vector<u_rational_t> getResolution( const size_t offset ) const; std::vector<u_rational_t> getResolution( const size_t offset ) const;
std::vector<u_rational_t> getWhitePoint( const size_t offset ) const; std::vector<u_rational_t> getWhitePoint( const size_t offset ) const;

@ -55,6 +55,11 @@ BaseImageDecoder::BaseImageDecoder()
m_scale_denom = 1; m_scale_denom = 1;
} }
ExifEntry_t BaseImageDecoder::getExifTag(const ExifTagName tag) const
{
return m_exif.getTag(tag);
}
bool BaseImageDecoder::setSource( const String& filename ) bool BaseImageDecoder::setSource( const String& filename )
{ {
m_filename = filename; m_filename = filename;

@ -45,6 +45,7 @@
#include "utils.hpp" #include "utils.hpp"
#include "bitstrm.hpp" #include "bitstrm.hpp"
#include "exif.hpp"
namespace cv namespace cv
{ {
@ -65,6 +66,7 @@ public:
int height() const { return m_height; } int height() const { return m_height; }
virtual int type() const { return m_type; } virtual int type() const { return m_type; }
ExifEntry_t getExifTag(const ExifTagName tag) const;
virtual bool setSource( const String& filename ); virtual bool setSource( const String& filename );
virtual bool setSource( const Mat& buf ); virtual bool setSource( const Mat& buf );
virtual int setScale( const int& scale_denom ); virtual int setScale( const int& scale_denom );
@ -87,6 +89,7 @@ protected:
String m_signature; String m_signature;
Mat m_buf; Mat m_buf;
bool m_buf_supported; bool m_buf_supported;
ExifReader m_exif;
}; };

@ -244,6 +244,7 @@ bool JpegDecoder::readHeader()
if (state->cinfo.src != 0) if (state->cinfo.src != 0)
{ {
jpeg_save_markers(&state->cinfo, APP1, 0xffff);
jpeg_read_header( &state->cinfo, TRUE ); jpeg_read_header( &state->cinfo, TRUE );
state->cinfo.scale_num=1; 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 ); jpeg_start_decompress( cinfo );
buffer = (*cinfo->mem->alloc_sarray)((j_common_ptr)cinfo, buffer = (*cinfo->mem->alloc_sarray)((j_common_ptr)cinfo,

@ -52,6 +52,25 @@
namespace cv 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 class JpegDecoder CV_FINAL : public BaseImageDecoder
{ {

@ -284,6 +284,22 @@ bool PngDecoder::readData( Mat& img )
png_read_image( png_ptr, buffer ); png_read_image( png_ptr, buffer );
png_read_end( png_ptr, end_info ); 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; result = true;
} }
} }

@ -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; 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 ); orientation = orientationTag.field_u16; //orientation is unsigned short, so check field_u16
ExifReader reader( stream ); ExifTransform(orientation, img);
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<char*>(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
}
}
} }
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); 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; return true;
} }
@ -614,7 +587,7 @@ imreadmulti_(const String& filename, int flags, std::vector<Mat>& mats)
// optionally rotate the data if EXIF' orientation flag says so // optionally rotate the data if EXIF' orientation flag says so
if( (flags & IMREAD_IGNORE_ORIENTATION) == 0 && flags != IMREAD_UNCHANGED ) if( (flags & IMREAD_IGNORE_ORIENTATION) == 0 && flags != IMREAD_UNCHANGED )
{ {
ApplyExifOrientation(filename, mat); ApplyExifOrientation(decoder->getExifTag(ORIENTATION), mat);
} }
mats.push_back(mat); mats.push_back(mat);
@ -645,12 +618,6 @@ Mat imread( const String& filename, int flags )
/// load the data /// load the data
imread_( filename, flags, img ); 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 a reference to the data
return img; 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); 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; return true;
} }
@ -900,12 +873,6 @@ Mat imdecode( InputArray _buf, int flags )
Mat buf = _buf.getMat(), img; Mat buf = _buf.getMat(), img;
imdecode_( buf, flags, 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; return img;
} }
@ -917,12 +884,6 @@ Mat imdecode( InputArray _buf, int flags, Mat* dst )
dst = dst ? dst : &img; dst = dst ? dst : &img;
imdecode_( buf, flags, *dst ); 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; return *dst;
} }

@ -7,6 +7,12 @@ namespace opencv_test { namespace {
#ifdef HAVE_PNG #ifdef HAVE_PNG
#ifdef HAVE_LIBPNG_PNG_H
#include <libpng/png.h>
#else
#include <png.h>
#endif
TEST(Imgcodecs_Png, write_big) TEST(Imgcodecs_Png, write_big)
{ {
const string root = cvtest::TS::ptr()->get_data_path(); 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<Vec3b>(0, 1), Vec3b(0, 0, 255)); EXPECT_EQ(img.at<Vec3b>(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<string> 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<Vec3b>(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<Vec3b>(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<Vec3b>(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 #endif // HAVE_PNG
}} // namespace }} // namespace

@ -19,7 +19,7 @@ namespace ocl {
typedef TestBaseWithParam<string> stitch; typedef TestBaseWithParam<string> stitch;
#ifdef HAVE_OPENCV_XFEATURES2D #if defined(HAVE_OPENCV_XFEATURES2D) && defined(OPENCV_ENABLE_NONFREE)
#define TEST_DETECTORS testing::Values("surf", "orb", "akaze") #define TEST_DETECTORS testing::Values("surf", "orb", "akaze")
#else #else
#define TEST_DETECTORS testing::Values("orb", "akaze") #define TEST_DETECTORS testing::Values("orb", "akaze")

@ -8,7 +8,7 @@ using namespace perf;
typedef TestBaseWithParam<tuple<string, string> > bundleAdjuster; typedef TestBaseWithParam<tuple<string, string> > bundleAdjuster;
#ifdef HAVE_OPENCV_XFEATURES2D #if defined(HAVE_OPENCV_XFEATURES2D) && defined(OPENCV_ENABLE_NONFREE)
#define TEST_DETECTORS testing::Values("surf", "orb") #define TEST_DETECTORS testing::Values("surf", "orb")
#else #else
#define TEST_DETECTORS testing::Values<string>("orb") #define TEST_DETECTORS testing::Values<string>("orb")

@ -17,7 +17,7 @@ typedef TestBaseWithParam<matchVector_t> matchVector;
#define ORB_MATCH_CONFIDENCE 0.3f #define ORB_MATCH_CONFIDENCE 0.3f
#define WORK_MEGAPIX 0.6 #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") #define TEST_DETECTORS testing::Values("surf", "orb")
#else #else
#define TEST_DETECTORS testing::Values<string>("orb") #define TEST_DETECTORS testing::Values<string>("orb")

@ -15,7 +15,7 @@ static inline Ptr<Feature2D> getFeatureFinder(const std::string& name)
{ {
if (name == "orb") if (name == "orb")
return ORB::create(); return ORB::create();
#ifdef HAVE_OPENCV_XFEATURES2D #if defined(HAVE_OPENCV_XFEATURES2D) && defined(OPENCV_ENABLE_NONFREE)
else if (name == "surf") else if (name == "surf")
return xfeatures2d::SURF::create(); return xfeatures2d::SURF::create();
#endif #endif

@ -17,7 +17,7 @@ typedef TestBaseWithParam<int> stitchExposureCompensation;
typedef TestBaseWithParam<tuple<string, string> > stitchDatasets; typedef TestBaseWithParam<tuple<string, string> > stitchDatasets;
typedef TestBaseWithParam<tuple<string, int>> stitchExposureCompMultiFeed; typedef TestBaseWithParam<tuple<string, int>> stitchExposureCompMultiFeed;
#ifdef HAVE_OPENCV_XFEATURES2D #if defined(HAVE_OPENCV_XFEATURES2D) && defined(OPENCV_ENABLE_NONFREE)
#define TEST_DETECTORS testing::Values("surf", "orb", "akaze") #define TEST_DETECTORS testing::Values("surf", "orb", "akaze")
#else #else
#define TEST_DETECTORS testing::Values("orb", "akaze") #define TEST_DETECTORS testing::Values("orb", "akaze")

@ -248,7 +248,7 @@ if __name__ == "__main__":
log.debug("Args: %s", args) 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: if 'EMMAKEN_JUST_CONFIGURE' in os.environ:
del os.environ['EMMAKEN_JUST_CONFIGURE'] # avoid linker errors with NODERAWFS message then using 'emcmake' launcher del os.environ['EMMAKEN_JUST_CONFIGURE'] # avoid linker errors with NODERAWFS message then using 'emcmake' launcher

@ -24,23 +24,16 @@ int main(int argc, char *argv[])
return -1; return -1;
} }
// Show source image // Show the source image
imshow("Source Image", src); imshow("Source Image", src);
//! [load_image] //! [load_image]
//! [black_bg] //! [black_bg]
// Change the background from white to black, since that will help later to extract // Change the background from white to black, since that will help later to extract
// better results during the use of Distance Transform // better results during the use of Distance Transform
for ( int i = 0; i < src.rows; i++ ) { Mat mask;
for ( int j = 0; j < src.cols; j++ ) { inRange(src, Scalar(255, 255, 255), Scalar(255, 255, 255), mask);
if ( src.at<Vec3b>(i, j) == Vec3b(255,255,255) ) src.setTo(Scalar(0, 0, 0), mask);
{
src.at<Vec3b>(i, j)[0] = 0;
src.at<Vec3b>(i, j)[1] = 0;
src.at<Vec3b>(i, j)[2] = 0;
}
}
}
// Show output image // Show output image
imshow("Black Background Image", src); imshow("Black Background Image", src);
@ -124,7 +117,9 @@ int main(int argc, char *argv[])
// Draw the background marker // Draw the background marker
circle(markers, Point(5,5), 3, Scalar(255), -1); 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] //! [seeds]
//! [watershed] //! [watershed]

@ -102,7 +102,8 @@ for i in range(len(contours)):
# Draw the background marker # Draw the background marker
cv.circle(markers, (5,5), 3, (255,255,255), -1) 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] ## [seeds]
## [watershed] ## [watershed]

Loading…
Cancel
Save