mirror of
https://github.com/opencv/opencv.git
synced 2026-07-21 19:33:03 +04:00
Merge branch 4.x
This commit is contained in:
@@ -118,6 +118,9 @@ namespace calib
|
||||
double solverEps;
|
||||
int solverMaxIters;
|
||||
bool fastSolving;
|
||||
bool rationalModel;
|
||||
bool thinPrismModel;
|
||||
bool tiltedModel;
|
||||
double filterAlpha;
|
||||
|
||||
internalParameters()
|
||||
@@ -125,6 +128,9 @@ namespace calib
|
||||
solverEps = 1e-7;
|
||||
solverMaxIters = 30;
|
||||
fastSolving = false;
|
||||
rationalModel = false;
|
||||
thinPrismModel = false;
|
||||
tiltedModel = false;
|
||||
filterAlpha = 0.1;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -135,6 +135,10 @@ int main(int argc, char** argv)
|
||||
|
||||
int calibrationFlags = 0;
|
||||
if(intParams.fastSolving) calibrationFlags |= cv::CALIB_USE_QR;
|
||||
if(intParams.rationalModel) calibrationFlags |= cv::CALIB_RATIONAL_MODEL;
|
||||
if(intParams.thinPrismModel) calibrationFlags |= cv::CALIB_THIN_PRISM_MODEL;
|
||||
if(intParams.tiltedModel) calibrationFlags |= cv::CALIB_TILTED_MODEL;
|
||||
|
||||
cv::Ptr<calibController> controller(new calibController(globalData, calibrationFlags,
|
||||
parser.get<bool>("ft"), capParams.minFramesNum));
|
||||
cv::Ptr<calibDataController> dataController(new calibDataController(globalData, capParams.maxFramesNum,
|
||||
|
||||
@@ -49,6 +49,9 @@ bool calib::parametersController::loadFromFile(const std::string &inputFileName)
|
||||
readFromNode(reader["solver_eps"], mInternalParameters.solverEps);
|
||||
readFromNode(reader["solver_max_iters"], mInternalParameters.solverMaxIters);
|
||||
readFromNode(reader["fast_solver"], mInternalParameters.fastSolving);
|
||||
readFromNode(reader["rational_model"], mInternalParameters.rationalModel);
|
||||
readFromNode(reader["thin_prism_model"], mInternalParameters.thinPrismModel);
|
||||
readFromNode(reader["tiltedModel"], mInternalParameters.tiltedModel);
|
||||
readFromNode(reader["frame_filter_conv_param"], mInternalParameters.filterAlpha);
|
||||
|
||||
bool retValue =
|
||||
|
||||
@@ -386,8 +386,13 @@ macro(ocv_nvcc_flags)
|
||||
set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} -Xcompiler=-fno-finite-math-only)
|
||||
endif()
|
||||
|
||||
if(WIN32 AND NOT (CUDA_VERSION VERSION_LESS "11.2"))
|
||||
set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} -Xcudafe --display_error_number --diag-suppress 1394,1388)
|
||||
if(WIN32)
|
||||
if (NOT (CUDA_VERSION VERSION_LESS "11.2"))
|
||||
set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} -Xcudafe --display_error_number --diag-suppress 1394,1388)
|
||||
endif()
|
||||
if(CUDA_VERSION GREATER "12.8")
|
||||
set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} -Xcompiler=/Zc:preprocessor)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CMAKE_CROSSCOMPILING AND (ARM OR AARCH64))
|
||||
|
||||
@@ -623,9 +623,7 @@ public:
|
||||
}
|
||||
|
||||
if( len == 4 && memcmp( beg, "null", 4 ) == 0 )
|
||||
{
|
||||
CV_PARSE_ERROR_CPP( "Value 'null' is not supported by this parser" );
|
||||
}
|
||||
;
|
||||
else if( (len == 4 && memcmp( beg, "true", 4 ) == 0) ||
|
||||
(len == 5 && memcmp( beg, "false", 5 ) == 0) )
|
||||
{
|
||||
|
||||
@@ -1578,6 +1578,30 @@ TEST(Core_InputOutput, FileStorage_format_yml_gz)
|
||||
EXPECT_EQ(FileStorage::FORMAT_YAML, fs.getFormat());
|
||||
}
|
||||
|
||||
TEST(Core_InputOutput, FileStorage_json_null_object)
|
||||
{
|
||||
std::string test =
|
||||
"{ "
|
||||
"\"padding\": null,"
|
||||
"\"truncation\": null,"
|
||||
"\"version\": \"1.0\""
|
||||
"}";
|
||||
FileStorage fs(test, FileStorage::READ | FileStorage::MEMORY);
|
||||
|
||||
ASSERT_TRUE(fs["padding"].isNone());
|
||||
ASSERT_TRUE(fs["truncation"].isNone());
|
||||
ASSERT_TRUE(fs["version"].isString());
|
||||
|
||||
ASSERT_EQ(fs["padding"].name(), "padding");
|
||||
ASSERT_EQ(fs["truncation"].name(), "truncation");
|
||||
ASSERT_EQ(fs["version"].name(), "version");
|
||||
|
||||
ASSERT_EQ(fs["padding"].string(), "");
|
||||
ASSERT_EQ(fs["truncation"].string(), "");
|
||||
ASSERT_EQ(fs["version"].string(), "1.0");
|
||||
fs.release();
|
||||
}
|
||||
|
||||
TEST(Core_InputOutput, FileStorage_json_named_nodes)
|
||||
{
|
||||
std::string test =
|
||||
|
||||
@@ -94,9 +94,10 @@ enum ImwriteFlags {
|
||||
IMWRITE_JPEG_CHROMA_QUALITY = 6, //!< Separate chroma quality level, 0 - 100, default is -1 - don't use. If JPEG_LIB_VERSION < 70, Not supported.
|
||||
IMWRITE_JPEG_SAMPLING_FACTOR = 7, //!< For JPEG, set sampling factor. See cv::ImwriteJPEGSamplingFactorParams.
|
||||
IMWRITE_PNG_COMPRESSION = 16, //!< For PNG, it can be the compression level from 0 to 9. A higher value means a smaller size and longer compression time. If specified, strategy is changed to IMWRITE_PNG_STRATEGY_DEFAULT (Z_DEFAULT_STRATEGY). Default value is 1 (best speed setting).
|
||||
IMWRITE_PNG_STRATEGY = 17, //!< One of cv::ImwritePNGFlags, default is IMWRITE_PNG_STRATEGY_RLE.
|
||||
IMWRITE_PNG_BILEVEL = 18, //!< Binary level PNG, 0 or 1, default is 0.
|
||||
IMWRITE_PNG_FILTER = 19, //!< One of cv::ImwritePNGFilterFlags, default is IMWRITE_PNG_FILTER_SUB.
|
||||
IMWRITE_PNG_STRATEGY = 17, //!< For PNG, One of cv::ImwritePNGFlags, default is IMWRITE_PNG_STRATEGY_RLE.
|
||||
IMWRITE_PNG_BILEVEL = 18, //!< For PNG, Binary level PNG, 0 or 1, default is 0.
|
||||
IMWRITE_PNG_FILTER = 19, //!< For PNG, One of cv::ImwritePNGFilterFlags, default is IMWRITE_PNG_FILTER_SUB.
|
||||
IMWRITE_PNG_ZLIBBUFFER_SIZE = 20, //!< For PNG, sets the size of the internal zlib compression buffer in bytes.
|
||||
IMWRITE_PXM_BINARY = 32, //!< For PPM, PGM, or PBM, it can be a binary format flag, 0 or 1. Default value is 1.
|
||||
IMWRITE_EXR_TYPE = (3 << 4) + 0 /* 48 */, //!< override EXR storage type (FLOAT (FP32) is default)
|
||||
IMWRITE_EXR_COMPRESSION = (3 << 4) + 1 /* 49 */, //!< override EXR compression type (ZIP_COMPRESSION = 3 is default)
|
||||
@@ -253,11 +254,13 @@ enum ImwriteGIFCompressionFlags {
|
||||
|
||||
enum ImageMetadataType
|
||||
{
|
||||
IMAGE_METADATA_UNKNOWN = -1,
|
||||
IMAGE_METADATA_EXIF = 0,
|
||||
IMAGE_METADATA_XMP = 1,
|
||||
IMAGE_METADATA_ICCP = 2,
|
||||
IMAGE_METADATA_MAX = 2
|
||||
IMAGE_METADATA_UNKNOWN = -1, // Used when metadata type is unrecognized or not set
|
||||
|
||||
IMAGE_METADATA_EXIF = 0, // EXIF metadata (e.g., camera info, GPS, orientation)
|
||||
IMAGE_METADATA_XMP = 1, // XMP metadata (eXtensible Metadata Platform - Adobe format)
|
||||
IMAGE_METADATA_ICCP = 2, // ICC Profile (color profile for color management)
|
||||
|
||||
IMAGE_METADATA_MAX = 2 // Highest valid index (usually used for bounds checking)
|
||||
};
|
||||
|
||||
//! @} imgcodecs_flags
|
||||
@@ -354,7 +357,7 @@ Currently, the following file formats are supported:
|
||||
the environment variable `OPENCV_IO_MAX_IMAGE_PIXELS`. See @ref tutorial_env_reference.
|
||||
|
||||
@param filename Name of the file to be loaded.
|
||||
@param flags Flag that can take values of `cv::ImreadModes`.
|
||||
@param flags Flag that can take values of cv::ImreadModes, default with cv::IMREAD_COLOR_BGR.
|
||||
*/
|
||||
CV_EXPORTS_W Mat imread( const String& filename, int flags = IMREAD_COLOR_BGR );
|
||||
|
||||
@@ -363,19 +366,25 @@ CV_EXPORTS_W Mat imread( const String& filename, int flags = IMREAD_COLOR_BGR );
|
||||
This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts and the return value.
|
||||
@param filename Name of file to be loaded.
|
||||
@param dst object in which the image will be loaded.
|
||||
@param flags Flag that can take values of cv::ImreadModes
|
||||
@param flags Flag that can take values of cv::ImreadModes, default with cv::IMREAD_COLOR_BGR.
|
||||
@note
|
||||
The image passing through the img parameter can be pre-allocated. The memory is reused if the shape and the type match with the load image.
|
||||
*/
|
||||
CV_EXPORTS_W void imread( const String& filename, OutputArray dst, int flags = IMREAD_COLOR_BGR );
|
||||
|
||||
/** @brief Reads an image from a file together with associated metadata.
|
||||
/** @brief Reads an image from a file along with associated metadata.
|
||||
|
||||
The function imreadWithMetadata reads image from the specified file. It does the same thing as imread, but additionally reads metadata if the corresponding file contains any.
|
||||
This function behaves similarly to cv::imread(), loading an image from the specified file.
|
||||
In addition to the image pixel data, it also attempts to extract any available metadata
|
||||
embedded in the file (such as EXIF, XMP, etc.), depending on file format support.
|
||||
|
||||
@note In the case of color images, the decoded images will have the channels stored in **B G R** order.
|
||||
@param filename Name of the file to be loaded.
|
||||
@param metadataTypes Output vector with types of metadata chucks returned in metadata, see ImageMetadataType.
|
||||
@param metadata Output vector of vectors or vector of matrices to store the retrieved metadata
|
||||
@param flags Flag that can take values of cv::ImreadModes
|
||||
@param metadataTypes Output vector with types of metadata chunks returned in metadata, see ImageMetadataType.
|
||||
@param metadata Output vector of vectors or vector of matrices to store the retrieved metadata.
|
||||
@param flags Flag that can take values of cv::ImreadModes, default with cv::IMREAD_ANYCOLOR.
|
||||
|
||||
@return The loaded image as a cv::Mat object. If the image cannot be read, the function returns an empty matrix.
|
||||
*/
|
||||
CV_EXPORTS_W Mat imreadWithMetadata( const String& filename, CV_OUT std::vector<int>& metadataTypes,
|
||||
OutputArrayOfArrays metadata, int flags = IMREAD_ANYCOLOR);
|
||||
@@ -560,29 +569,31 @@ See cv::imread for the list of supported formats and flags description.
|
||||
|
||||
@note In the case of color images, the decoded images will have the channels stored in **B G R** order.
|
||||
@param buf Input array or vector of bytes.
|
||||
@param flags The same flags as in cv::imread, see cv::ImreadModes.
|
||||
@param flags Flag that can take values of cv::ImreadModes.
|
||||
*/
|
||||
CV_EXPORTS_W Mat imdecode( InputArray buf, int flags );
|
||||
|
||||
/** @brief Reads an image from a buffer in memory together with associated metadata.
|
||||
/** @brief Reads an image from a memory buffer and extracts associated metadata.
|
||||
|
||||
The function imdecode reads an image from the specified buffer in the memory. If the buffer is too short or
|
||||
This function decodes an image from the specified memory buffer. If the buffer is too short or
|
||||
contains invalid data, the function returns an empty matrix ( Mat::data==NULL ).
|
||||
|
||||
See cv::imread for the list of supported formats and flags description.
|
||||
|
||||
@note In the case of color images, the decoded images will have the channels stored in **B G R** order.
|
||||
@param buf Input array or vector of bytes.
|
||||
@param metadataTypes Output vector with types of metadata chucks returned in metadata, see ImageMetadataType.
|
||||
@param buf Input array or vector of bytes containing the encoded image data.
|
||||
@param metadataTypes Output vector with types of metadata chucks returned in metadata, see cv::ImageMetadataType
|
||||
@param metadata Output vector of vectors or vector of matrices to store the retrieved metadata
|
||||
@param flags The same flags as in cv::imread, see cv::ImreadModes.
|
||||
@param flags Flag that can take values of cv::ImreadModes, default with cv::IMREAD_ANYCOLOR.
|
||||
|
||||
@return The decoded image as a cv::Mat object. If decoding fails, the function returns an empty matrix.
|
||||
*/
|
||||
CV_EXPORTS_W Mat imdecodeWithMetadata( InputArray buf, CV_OUT std::vector<int>& metadataTypes,
|
||||
OutputArrayOfArrays metadata, int flags = IMREAD_ANYCOLOR );
|
||||
|
||||
/** @overload
|
||||
@param buf Input array or vector of bytes.
|
||||
@param flags The same flags as in cv::imread, see cv::ImreadModes.
|
||||
@param flags Flag that can take values of cv::ImreadModes, default with cv::IMREAD_ANYCOLOR.
|
||||
@param dst The optional output placeholder for the decoded matrix. It can save the image
|
||||
reallocations when the function is called repeatedly for images of the same size. In case of decoder
|
||||
failure the function returns empty cv::Mat object, but does not release user-provided dst buffer.
|
||||
@@ -598,7 +609,7 @@ See cv::imreadmulti for the list of supported formats and flags description.
|
||||
|
||||
@note In the case of color images, the decoded images will have the channels stored in **B G R** order.
|
||||
@param buf Input array or vector of bytes.
|
||||
@param flags The same flags as in cv::imread, see cv::ImreadModes.
|
||||
@param flags Flag that can take values of cv::ImreadModes.
|
||||
@param mats A vector of Mat objects holding each page, if more than one.
|
||||
@param range A continuous selection of pages.
|
||||
*/
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "exif.hpp"
|
||||
#include "opencv2/core/utils/logger.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -53,6 +54,43 @@ namespace {
|
||||
namespace cv
|
||||
{
|
||||
|
||||
static std::string HexStringToBytes(const char* hexstring, size_t expected_length);
|
||||
|
||||
// Converts the NULL terminated 'hexstring' which contains 2-byte character
|
||||
// representations of hex values to raw data.
|
||||
// 'hexstring' may contain values consisting of [A-F][a-f][0-9] in pairs,
|
||||
// e.g., 7af2..., separated by any number of newlines.
|
||||
// 'expected_length' is the anticipated processed size.
|
||||
// On success the raw buffer is returned with its length equivalent to
|
||||
// 'expected_length'. NULL is returned if the processed length is less than
|
||||
// 'expected_length' or any character aside from those above is encountered.
|
||||
// The returned buffer must be freed by the caller.
|
||||
static std::string HexStringToBytes(const char* hexstring,
|
||||
size_t expected_length) {
|
||||
const char* src = hexstring;
|
||||
size_t actual_length = 0;
|
||||
std::string raw_data;
|
||||
raw_data.resize(expected_length);
|
||||
char* dst = const_cast<char*>(raw_data.data());
|
||||
|
||||
for (; actual_length < expected_length && *src != '\0'; ++src) {
|
||||
char* end;
|
||||
char val[3];
|
||||
if (*src == '\n') continue;
|
||||
val[0] = *src++;
|
||||
val[1] = *src;
|
||||
val[2] = '\0';
|
||||
*dst++ = static_cast<uint8_t>(strtol(val, &end, 16));
|
||||
if (end != val + 2) break;
|
||||
++actual_length;
|
||||
}
|
||||
|
||||
if (actual_length != expected_length) {
|
||||
raw_data.clear();
|
||||
}
|
||||
return raw_data;
|
||||
}
|
||||
|
||||
ExifEntry_t::ExifEntry_t() :
|
||||
field_float(0), field_double(0), field_u32(0), field_s32(0),
|
||||
tag(INVALID_TAG), field_u16(0), field_s16(0), field_u8(0), field_s8(0)
|
||||
@@ -99,6 +137,36 @@ const std::vector<unsigned char>& ExifReader::getData() const
|
||||
return m_data;
|
||||
}
|
||||
|
||||
bool ExifReader::processRawProfile(const char* profile, size_t profile_len) {
|
||||
const char* src = profile;
|
||||
char* end;
|
||||
int expected_length;
|
||||
|
||||
if (profile == nullptr || profile_len == 0) return false;
|
||||
|
||||
// ImageMagick formats 'raw profiles' as
|
||||
// '\n<name>\n<length>(%8lu)\n<hex payload>\n'.
|
||||
if (*src != '\n') {
|
||||
CV_LOG_WARNING(NULL, cv::format("Malformed raw profile, expected '\\n' got '\\x%.2X'", *src));
|
||||
return false;
|
||||
}
|
||||
++src;
|
||||
// skip the profile name and extract the length.
|
||||
while (*src != '\0' && *src++ != '\n') {}
|
||||
expected_length = static_cast<int>(strtol(src, &end, 10));
|
||||
if (*end != '\n') {
|
||||
CV_LOG_WARNING(NULL, cv::format("Malformed raw profile, expected '\\n' got '\\x%.2X'", *src));
|
||||
return false;
|
||||
}
|
||||
++end;
|
||||
|
||||
// 'end' now points to the profile payload.
|
||||
std::string payload = HexStringToBytes(end, expected_length);
|
||||
if (payload.size() == 0) return false;
|
||||
|
||||
return parseExif((unsigned char*)payload.c_str() + 6, expected_length - 6);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Parsing the exif data buffer and prepare (internal) exif directory
|
||||
*
|
||||
|
||||
@@ -154,6 +154,7 @@ public:
|
||||
ExifReader();
|
||||
~ExifReader();
|
||||
|
||||
bool processRawProfile(const char* profile, size_t profile_len);
|
||||
|
||||
/**
|
||||
* @brief Parse the file with exif info
|
||||
|
||||
@@ -56,6 +56,8 @@ BaseImageDecoder::BaseImageDecoder()
|
||||
m_scale_denom = 1;
|
||||
m_use_rgb = false;
|
||||
m_frame_count = 1;
|
||||
m_read_options = 0;
|
||||
m_metadata.resize(IMAGE_METADATA_MAX + 1);
|
||||
}
|
||||
|
||||
bool BaseImageDecoder::haveMetadata(ImageMetadataType type) const
|
||||
@@ -67,12 +69,21 @@ bool BaseImageDecoder::haveMetadata(ImageMetadataType type) const
|
||||
|
||||
Mat BaseImageDecoder::getMetadata(ImageMetadataType type) const
|
||||
{
|
||||
if (type == IMAGE_METADATA_EXIF) {
|
||||
const std::vector<unsigned char>& exif = m_exif.getData();
|
||||
if (!exif.empty()) {
|
||||
Mat exifmat(1, (int)exif.size(), CV_8U, (void*)exif.data());
|
||||
return exifmat;
|
||||
}
|
||||
auto makeMat = [](const std::vector<unsigned char>& data) -> Mat {
|
||||
return data.empty() ? Mat() : Mat(1, (int)data.size(), CV_8U, (void*)data.data());
|
||||
};
|
||||
|
||||
switch (type) {
|
||||
case IMAGE_METADATA_EXIF:
|
||||
return makeMat(m_exif.getData());
|
||||
|
||||
case IMAGE_METADATA_XMP:
|
||||
case IMAGE_METADATA_ICCP:
|
||||
return makeMat(m_metadata[type]);
|
||||
|
||||
default:
|
||||
CV_LOG_WARNING(NULL, "Unknown metadata type requested: " << static_cast<int>(type));
|
||||
break;
|
||||
}
|
||||
return Mat();
|
||||
}
|
||||
@@ -116,6 +127,13 @@ int BaseImageDecoder::setScale( const int& scale_denom )
|
||||
return temp;
|
||||
}
|
||||
|
||||
int BaseImageDecoder::setReadOptions(int read_options)
|
||||
{
|
||||
int temp = m_read_options;
|
||||
m_read_options = read_options;
|
||||
return temp;
|
||||
}
|
||||
|
||||
void BaseImageDecoder::setRGB(bool useRGB)
|
||||
{
|
||||
m_use_rgb = useRGB;
|
||||
|
||||
@@ -109,7 +109,24 @@ public:
|
||||
* @param scale_denom The denominator of the scale factor (image is scaled down by 1/scale_denom).
|
||||
* @return The scale factor that was set.
|
||||
*/
|
||||
virtual int setScale(const int& scale_denom);
|
||||
int setScale(const int& scale_denom);
|
||||
|
||||
/**
|
||||
* @brief Set read options for image decoding.
|
||||
*
|
||||
* This function sets internal flags that control various read-time behaviors
|
||||
* such as metadata extraction (e.g., XMP, ICC profiles, textual data)
|
||||
* during image decoding. The flags can be combined using bitwise OR to enable
|
||||
* multiple options simultaneously.
|
||||
*
|
||||
* @param options Bitwise OR of read option flags to enable.
|
||||
*
|
||||
* @return The previous value of the read options flags.
|
||||
*
|
||||
* @note Setting this has no effect unless the image format and decoder support
|
||||
* the selected options. Unknown flags will be ignored.
|
||||
*/
|
||||
int setReadOptions(int read_options);
|
||||
|
||||
/**
|
||||
* @brief Read the image header to extract basic properties (width, height, type).
|
||||
@@ -173,6 +190,8 @@ protected:
|
||||
ExifReader m_exif; ///< Object for reading EXIF metadata from the image.
|
||||
size_t m_frame_count; ///< Number of frames in the image (for animations and multi-page images).
|
||||
Animation m_animation;
|
||||
int m_read_options;
|
||||
std::vector<std::vector<unsigned char> > m_metadata;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -218,16 +218,14 @@ bool PngDecoder::InitPngPtr() {
|
||||
return false;
|
||||
|
||||
m_info_ptr = png_create_info_struct(m_png_ptr);
|
||||
m_end_info = png_create_info_struct(m_png_ptr);
|
||||
return (m_info_ptr && m_end_info);
|
||||
return (m_info_ptr != nullptr);
|
||||
}
|
||||
|
||||
void PngDecoder::ClearPngPtr() {
|
||||
if (m_png_ptr)
|
||||
png_destroy_read_struct(&m_png_ptr, &m_info_ptr, &m_end_info);
|
||||
png_destroy_read_struct(&m_png_ptr, &m_info_ptr, nullptr);
|
||||
m_png_ptr = nullptr;
|
||||
m_info_ptr = nullptr;
|
||||
m_end_info = nullptr;
|
||||
}
|
||||
|
||||
ImageDecoder PngDecoder::newDecoder() const
|
||||
@@ -573,7 +571,7 @@ bool PngDecoder::readData( Mat& img )
|
||||
volatile bool result = false;
|
||||
bool color = img.channels() > 1;
|
||||
|
||||
if( m_png_ptr && m_info_ptr && m_end_info && m_width && m_height )
|
||||
if( m_png_ptr && m_info_ptr && m_width && m_height )
|
||||
{
|
||||
if( setjmp( png_jmpbuf ( m_png_ptr ) ) == 0 )
|
||||
{
|
||||
@@ -623,17 +621,49 @@ bool PngDecoder::readData( Mat& img )
|
||||
buffer[y] = img.data + y*img.step;
|
||||
|
||||
png_read_image( m_png_ptr, buffer );
|
||||
png_read_end( m_png_ptr, m_end_info );
|
||||
png_read_end( m_png_ptr, m_info_ptr);
|
||||
|
||||
if (m_read_options) {
|
||||
// Get tEXt chunks
|
||||
png_textp text_ptr;
|
||||
int num_text = 0;
|
||||
|
||||
png_get_text(m_png_ptr, m_info_ptr, &text_ptr, &num_text);
|
||||
|
||||
for (size_t i = 0; i < static_cast<size_t>(num_text); ++i) {
|
||||
const char* key = text_ptr[i].key;
|
||||
const char* value = text_ptr[i].text;
|
||||
size_t len = text_ptr[i].text_length;
|
||||
|
||||
if (key && (!std::strcmp(key, "Raw profile type exif") || !std::strcmp(key, "Raw profile type APP1"))) {
|
||||
m_exif.processRawProfile(value, len);
|
||||
}
|
||||
else if (key && !std::strcmp(key, "XML:com.adobe.xmp")) {
|
||||
auto& out = m_metadata[IMAGE_METADATA_XMP];
|
||||
out.insert(out.end(),
|
||||
reinterpret_cast<const unsigned char*>(value),
|
||||
reinterpret_cast<const unsigned char*>(value) + std::strlen(value) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
png_charp icc_name;
|
||||
int compression_type;
|
||||
png_bytep icc_profile;
|
||||
png_uint_32 icc_length;
|
||||
|
||||
if (png_get_iCCP(m_png_ptr, m_info_ptr, &icc_name, &compression_type, &icc_profile, &icc_length)) {
|
||||
auto& out = m_metadata[IMAGE_METADATA_ICCP];
|
||||
out.insert(out.end(),
|
||||
reinterpret_cast<const unsigned char*>(icc_profile),
|
||||
reinterpret_cast<const unsigned char*>(icc_profile) + icc_length);
|
||||
}
|
||||
}
|
||||
#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(m_png_ptr, m_info_ptr, PNG_INFO_eXIf) )
|
||||
png_get_eXIf_1(m_png_ptr, m_info_ptr, &num_exif, &exif);
|
||||
else if( png_get_valid(m_png_ptr, m_end_info, PNG_INFO_eXIf) )
|
||||
png_get_eXIf_1(m_png_ptr, m_end_info, &num_exif, &exif);
|
||||
|
||||
if( exif && num_exif > 0 )
|
||||
{
|
||||
@@ -865,6 +895,8 @@ PngEncoder::PngEncoder()
|
||||
m_buf_supported = true;
|
||||
m_support_metadata.assign((size_t)IMAGE_METADATA_MAX+1, false);
|
||||
m_support_metadata[IMAGE_METADATA_EXIF] = true;
|
||||
m_support_metadata[IMAGE_METADATA_XMP] = true;
|
||||
m_support_metadata[IMAGE_METADATA_ICCP] = true;
|
||||
op_zstream1.zalloc = NULL;
|
||||
op_zstream2.zalloc = NULL;
|
||||
next_seq_num = 0;
|
||||
@@ -947,26 +979,36 @@ bool PngEncoder::write( const Mat& img, const std::vector<int>& params )
|
||||
|
||||
for( size_t i = 0; i < params.size(); i += 2 )
|
||||
{
|
||||
if( params[i] == IMWRITE_PNG_COMPRESSION )
|
||||
switch (params[i])
|
||||
{
|
||||
case IMWRITE_PNG_COMPRESSION:
|
||||
m_compression_strategy = IMWRITE_PNG_STRATEGY_DEFAULT; // Default strategy
|
||||
m_compression_level = params[i+1];
|
||||
m_compression_level = MIN(MAX(m_compression_level, 0), Z_BEST_COMPRESSION);
|
||||
set_compression_level = true;
|
||||
}
|
||||
if( params[i] == IMWRITE_PNG_STRATEGY )
|
||||
{
|
||||
break;
|
||||
|
||||
case IMWRITE_PNG_STRATEGY:
|
||||
m_compression_strategy = params[i+1];
|
||||
m_compression_strategy = MIN(MAX(m_compression_strategy, 0), Z_FIXED);
|
||||
}
|
||||
if( params[i] == IMWRITE_PNG_BILEVEL )
|
||||
{
|
||||
break;
|
||||
|
||||
case IMWRITE_PNG_BILEVEL:
|
||||
m_isBilevel = params[i+1] != 0;
|
||||
}
|
||||
if( params[i] == IMWRITE_PNG_FILTER )
|
||||
{
|
||||
break;
|
||||
|
||||
case IMWRITE_PNG_FILTER:
|
||||
m_filter = params[i+1];
|
||||
set_filter = true;
|
||||
break;
|
||||
|
||||
case IMWRITE_PNG_ZLIBBUFFER_SIZE:
|
||||
png_set_compression_buffer_size(png_ptr, params[i+1]);
|
||||
break;
|
||||
|
||||
default:
|
||||
CV_LOG_WARNING(NULL, "An unknown or unsupported ImwriteFlags value was specified and has been ignored.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -983,6 +1025,42 @@ bool PngEncoder::write( const Mat& img, const std::vector<int>& params )
|
||||
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
|
||||
PNG_FILTER_TYPE_DEFAULT );
|
||||
|
||||
if (!m_metadata.empty()) {
|
||||
std::vector<uchar>& exif = m_metadata[IMAGE_METADATA_EXIF];
|
||||
if (!exif.empty()) {
|
||||
png_set_eXIf_1(png_ptr, info_ptr, static_cast<png_uint_32>(exif.size()), exif.data());
|
||||
}
|
||||
|
||||
std::vector<uchar>& xmp = m_metadata[IMAGE_METADATA_XMP];
|
||||
if (!xmp.empty()) {
|
||||
png_text text_chunk;
|
||||
text_chunk.compression = PNG_TEXT_COMPRESSION_NONE;
|
||||
text_chunk.key = const_cast<char*>("XML:com.adobe.xmp");
|
||||
text_chunk.text = reinterpret_cast<char*>(xmp.data());
|
||||
text_chunk.text_length = static_cast<png_size_t>(xmp.size());
|
||||
|
||||
png_set_text(png_ptr, info_ptr, &text_chunk, 1);
|
||||
}
|
||||
|
||||
std::vector<uchar> iccp = m_metadata[IMAGE_METADATA_ICCP];
|
||||
if (!iccp.empty()) {
|
||||
// PNG standard requires a profile name (null-terminated, max 79 characters, printable Latin-1)
|
||||
const char* iccp_profile_name = "ICC Profile";
|
||||
|
||||
// Compression type must be 0 (deflate) as per libpng docs
|
||||
int compression_type = PNG_COMPRESSION_TYPE_BASE;
|
||||
|
||||
// Some ICC profiles are already compressed (e.g., if saved from Photoshop),
|
||||
// but png_set_iCCP still expects uncompressed input, and it compresses it internally.
|
||||
|
||||
png_set_iCCP(png_ptr, info_ptr,
|
||||
iccp_profile_name,
|
||||
compression_type,
|
||||
reinterpret_cast<png_const_bytep>(iccp.data()),
|
||||
static_cast<png_uint_32>(iccp.size()));
|
||||
}
|
||||
}
|
||||
|
||||
png_write_info( png_ptr, info_ptr );
|
||||
|
||||
if (m_isBilevel)
|
||||
@@ -996,16 +1074,6 @@ bool PngEncoder::write( const Mat& img, const std::vector<int>& params )
|
||||
for( y = 0; y < height; y++ )
|
||||
buffer[y] = img.data + y*img.step;
|
||||
|
||||
if (!m_metadata.empty()) {
|
||||
std::vector<uchar>& exif = m_metadata[IMAGE_METADATA_EXIF];
|
||||
if (!exif.empty()) {
|
||||
writeChunk(f, "eXIf", exif.data(), (uint32_t)exif.size());
|
||||
}
|
||||
// [TODO] add xmp and icc. They need special handling,
|
||||
// see https://dev.exiv2.org/projects/exiv2/wiki/The_Metadata_in_PNG_files and
|
||||
// https://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html.
|
||||
}
|
||||
|
||||
png_write_image( png_ptr, buffer.data() );
|
||||
png_write_end( png_ptr, info_ptr );
|
||||
|
||||
|
||||
@@ -150,7 +150,6 @@ private:
|
||||
|
||||
png_structp m_png_ptr = nullptr; // pointer to decompression structure
|
||||
png_infop m_info_ptr = nullptr; // pointer to image information structure
|
||||
png_infop m_end_info = nullptr; // pointer to one more image information structure
|
||||
int m_bit_depth;
|
||||
FILE* m_f;
|
||||
int m_color_type;
|
||||
|
||||
@@ -422,6 +422,48 @@ bool SPngDecoder::readData(Mat &img)
|
||||
result = m_exif.parseExif((unsigned char *)exif_s.data, exif_s.length);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_read_options)
|
||||
{
|
||||
uint32_t text_count;
|
||||
|
||||
// Retrieve all text chunks
|
||||
if (spng_get_text(png_ptr, NULL, &text_count) == SPNG_OK)
|
||||
{
|
||||
std::vector<spng_text> texts(text_count);
|
||||
spng_get_text(png_ptr, texts.data(), &text_count);
|
||||
|
||||
for (size_t i = 0; i < text_count; ++i)
|
||||
{
|
||||
char* key = texts[i].keyword;
|
||||
char* value = texts[i].text;
|
||||
size_t len = texts[i].length;
|
||||
|
||||
if (key && (!std::strcmp(key, "Raw profile type exif") || !std::strcmp(key, "Raw profile type APP1")))
|
||||
{
|
||||
m_exif.processRawProfile(value, len);
|
||||
}
|
||||
else if (key && !std::strcmp(key, "XML:com.adobe.xmp"))
|
||||
{
|
||||
auto& out = m_metadata[IMAGE_METADATA_XMP];
|
||||
out.insert(out.end(),
|
||||
value,
|
||||
value + len + 1); // include null terminator
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ICC Profile
|
||||
spng_iccp iccp_data;
|
||||
|
||||
if (spng_get_iccp(png_ptr, &iccp_data) == SPNG_OK && iccp_data.profile_len > 0)
|
||||
{
|
||||
auto& out = m_metadata[IMAGE_METADATA_ICCP];
|
||||
out.insert(out.end(),
|
||||
iccp_data.profile,
|
||||
iccp_data.profile + iccp_data.profile_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -435,6 +477,10 @@ SPngEncoder::SPngEncoder()
|
||||
{
|
||||
m_description = "Portable Network Graphics files (*.png)";
|
||||
m_buf_supported = true;
|
||||
m_support_metadata.assign((size_t)IMAGE_METADATA_MAX + 1, false);
|
||||
m_support_metadata[IMAGE_METADATA_EXIF] = true;
|
||||
m_support_metadata[IMAGE_METADATA_XMP] = true;
|
||||
m_support_metadata[IMAGE_METADATA_ICCP] = true;
|
||||
}
|
||||
|
||||
SPngEncoder::~SPngEncoder()
|
||||
@@ -536,6 +582,37 @@ bool SPngEncoder::write(const Mat &img, const std::vector<int> ¶ms)
|
||||
spng_set_option(ctx, SPNG_IMG_COMPRESSION_LEVEL, compression_level);
|
||||
spng_set_option(ctx, SPNG_IMG_COMPRESSION_STRATEGY, compression_strategy);
|
||||
|
||||
if (!m_metadata.empty()) {
|
||||
std::vector<uchar>& exif = m_metadata[IMAGE_METADATA_EXIF];
|
||||
if (!exif.empty()) {
|
||||
spng_exif s_exif;
|
||||
s_exif.data = reinterpret_cast<char*>(exif.data());
|
||||
s_exif.length = exif.size();
|
||||
spng_set_exif(ctx, &s_exif);
|
||||
}
|
||||
|
||||
std::vector<uchar>& xmp = m_metadata[IMAGE_METADATA_XMP];
|
||||
if (!xmp.empty()) {
|
||||
spng_text text_chunk;
|
||||
strncpy(text_chunk.keyword, "XML:com.adobe.xmp", sizeof(text_chunk.keyword) - 1);
|
||||
text_chunk.keyword[sizeof(text_chunk.keyword) - 1] = '\0';
|
||||
text_chunk.type = SPNG_TEXT;
|
||||
text_chunk.text = reinterpret_cast<char*>(xmp.data());
|
||||
text_chunk.length = xmp.size();
|
||||
spng_set_text(ctx, &text_chunk, 1);
|
||||
}
|
||||
|
||||
std::vector<uchar>& iccp = m_metadata[IMAGE_METADATA_ICCP];
|
||||
if (!iccp.empty()) {
|
||||
spng_iccp s_iccp;
|
||||
strncpy(s_iccp.profile_name, "ICC Profile", sizeof(s_iccp.profile_name) - 1);
|
||||
s_iccp.profile_name[sizeof(s_iccp.profile_name) - 1] = '\0';
|
||||
s_iccp.profile_len = iccp.size();
|
||||
s_iccp.profile = reinterpret_cast<char*>(iccp.data());
|
||||
spng_set_iccp(ctx, &s_iccp);
|
||||
}
|
||||
}
|
||||
|
||||
int ret;
|
||||
spng_encode_chunks(ctx);
|
||||
ret = spng_encode_image(ctx, nullptr, 0, SPNG_FMT_PNG, SPNG_ENCODE_PROGRESSIVE);
|
||||
|
||||
@@ -68,6 +68,7 @@ WebPDecoder::WebPDecoder()
|
||||
fs_size = 0;
|
||||
m_has_animation = false;
|
||||
m_previous_timestamp = 0;
|
||||
m_read_options = 1;
|
||||
}
|
||||
|
||||
WebPDecoder::~WebPDecoder() {}
|
||||
@@ -197,6 +198,47 @@ bool WebPDecoder::readData(Mat &img)
|
||||
}
|
||||
CV_Assert(data.type() == CV_8UC1); CV_Assert(data.rows == 1);
|
||||
|
||||
if (m_read_options) {
|
||||
WebPData webp_data;
|
||||
webp_data.bytes = (const uint8_t*)data.ptr();
|
||||
webp_data.size = data.total();
|
||||
|
||||
std::vector<uchar> metadata;
|
||||
WebPDemuxer* demux = WebPDemux(&webp_data);
|
||||
|
||||
if (demux)
|
||||
{
|
||||
WebPChunkIterator chunk_iter;
|
||||
|
||||
if (WebPDemuxGetChunk(demux, "EXIF", 1, &chunk_iter))
|
||||
{
|
||||
metadata = std::vector<uchar>(chunk_iter.chunk.bytes,
|
||||
chunk_iter.chunk.bytes + chunk_iter.chunk.size);
|
||||
WebPDemuxReleaseChunkIterator(&chunk_iter);
|
||||
m_exif.parseExif(metadata.data(), metadata.size());
|
||||
}
|
||||
|
||||
if (WebPDemuxGetChunk(demux, "ICCP", 1, &chunk_iter))
|
||||
{
|
||||
metadata = std::vector<uchar>(chunk_iter.chunk.bytes,
|
||||
chunk_iter.chunk.bytes + chunk_iter.chunk.size);
|
||||
WebPDemuxReleaseChunkIterator(&chunk_iter);
|
||||
m_metadata[IMAGE_METADATA_ICCP] = metadata;
|
||||
}
|
||||
|
||||
if (WebPDemuxGetChunk(demux, "XMP ", 1, &chunk_iter)) // note the space in "XMP "
|
||||
{
|
||||
metadata = std::vector<uchar>(chunk_iter.chunk.bytes,
|
||||
chunk_iter.chunk.bytes + chunk_iter.chunk.size);
|
||||
WebPDemuxReleaseChunkIterator(&chunk_iter);
|
||||
m_metadata[IMAGE_METADATA_XMP] = metadata;
|
||||
}
|
||||
|
||||
WebPDemuxDelete(demux);
|
||||
m_read_options = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Mat read_img;
|
||||
CV_CheckType(img.type(), img.type() == CV_8UC1 || img.type() == CV_8UC3 || img.type() == CV_8UC4, "");
|
||||
if (img.type() != m_type || img.cols != m_width || img.rows != m_height)
|
||||
@@ -292,6 +334,10 @@ WebPEncoder::WebPEncoder()
|
||||
{
|
||||
m_description = "WebP files (*.webp)";
|
||||
m_buf_supported = true;
|
||||
m_support_metadata.assign((size_t)IMAGE_METADATA_MAX + 1, false);
|
||||
m_support_metadata[IMAGE_METADATA_EXIF] = true;
|
||||
m_support_metadata[IMAGE_METADATA_XMP] = true;
|
||||
m_support_metadata[IMAGE_METADATA_ICCP] = true;
|
||||
}
|
||||
|
||||
WebPEncoder::~WebPEncoder() { }
|
||||
@@ -364,6 +410,45 @@ bool WebPEncoder::write(const Mat& img, const std::vector<int>& params)
|
||||
size = WebPEncodeBGRA(image->ptr(), width, height, (int)image->step, quality, &out);
|
||||
}
|
||||
}
|
||||
|
||||
WebPData finalData = { out, size };
|
||||
if (!m_metadata.empty()) {
|
||||
|
||||
WebPMux* mux = WebPMuxNew();
|
||||
WebPData imageData = { out, size };
|
||||
WebPMuxSetImage(mux, &imageData, 0);
|
||||
|
||||
WebPData metadata;
|
||||
if (m_metadata[IMAGE_METADATA_EXIF].size() > 0)
|
||||
{
|
||||
metadata.bytes = m_metadata[IMAGE_METADATA_EXIF].data();
|
||||
metadata.size = m_metadata[IMAGE_METADATA_EXIF].size();
|
||||
WebPMuxSetChunk(mux, "EXIF", &metadata, 1);
|
||||
}
|
||||
if (m_metadata[IMAGE_METADATA_XMP].size() > 0)
|
||||
{
|
||||
metadata.bytes = m_metadata[IMAGE_METADATA_XMP].data();
|
||||
metadata.size = m_metadata[IMAGE_METADATA_XMP].size();
|
||||
WebPMuxSetChunk(mux, "XMP ", &metadata, 1);
|
||||
}
|
||||
|
||||
if (m_metadata[IMAGE_METADATA_ICCP].size() > 0)
|
||||
{
|
||||
metadata.bytes = m_metadata[IMAGE_METADATA_ICCP].data();
|
||||
metadata.size = m_metadata[IMAGE_METADATA_ICCP].size();
|
||||
WebPMuxSetChunk(mux, "ICCP", &metadata, 1);
|
||||
}
|
||||
|
||||
if (WebPMuxAssemble(mux, &finalData) == WEBP_MUX_OK) {
|
||||
size = finalData.size;
|
||||
WebPMuxDelete(mux);
|
||||
}
|
||||
else {
|
||||
WebPMuxDelete(mux);
|
||||
CV_Error(Error::StsError, "Failed to assemble WebP with EXIF");
|
||||
}
|
||||
}
|
||||
|
||||
#if WEBP_DECODER_ABI_VERSION >= 0x0206
|
||||
Ptr<uint8_t> out_cleaner(out, WebPFree);
|
||||
#else
|
||||
@@ -375,7 +460,7 @@ bool WebPEncoder::write(const Mat& img, const std::vector<int>& params)
|
||||
if (m_buf)
|
||||
{
|
||||
m_buf->resize(size);
|
||||
memcpy(&(*m_buf)[0], out, size);
|
||||
memcpy(&(*m_buf)[0], finalData.bytes, size);
|
||||
bytes_written = size;
|
||||
}
|
||||
else
|
||||
@@ -383,7 +468,7 @@ bool WebPEncoder::write(const Mat& img, const std::vector<int>& params)
|
||||
FILE *fd = fopen(m_filename.c_str(), "wb");
|
||||
if (fd != NULL)
|
||||
{
|
||||
bytes_written = fwrite(out, sizeof(uint8_t), size, fd);
|
||||
bytes_written = fwrite(finalData.bytes, sizeof(uint8_t), size, fd);
|
||||
if (size != bytes_written)
|
||||
{
|
||||
CV_LOG_ERROR(NULL, cv::format("Only %zu or %zu bytes are written\n",bytes_written, size));
|
||||
|
||||
@@ -498,9 +498,6 @@ imread_( const String& filename, int flags, OutputArray mat,
|
||||
/// Search for the relevant decoder to handle the imagery
|
||||
ImageDecoder decoder;
|
||||
|
||||
if (metadata_types)
|
||||
metadata_types->clear();
|
||||
|
||||
#ifdef HAVE_GDAL
|
||||
if(flags != IMREAD_UNCHANGED && (flags & IMREAD_LOAD_GDAL) == IMREAD_LOAD_GDAL ){
|
||||
decoder = GdalDecoder().newDecoder();
|
||||
@@ -539,6 +536,12 @@ imread_( const String& filename, int flags, OutputArray mat,
|
||||
/// set the filename in the driver
|
||||
decoder->setSource( filename );
|
||||
|
||||
if (metadata_types)
|
||||
{
|
||||
metadata_types->clear();
|
||||
decoder->setReadOptions(1);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// read the header to make sure it succeeds
|
||||
@@ -1249,9 +1252,6 @@ imdecode_( const Mat& buf, int flags, Mat& mat,
|
||||
std::vector<int>* metadata_types,
|
||||
OutputArrayOfArrays metadata )
|
||||
{
|
||||
if (metadata_types)
|
||||
metadata_types->clear();
|
||||
|
||||
CV_Assert(!buf.empty());
|
||||
CV_Assert(buf.isContinuous());
|
||||
CV_Assert(buf.checkVector(1, CV_8U) > 0);
|
||||
@@ -1302,6 +1302,12 @@ imdecode_( const Mat& buf, int flags, Mat& mat,
|
||||
decoder->setSource(filename);
|
||||
}
|
||||
|
||||
if (metadata_types)
|
||||
{
|
||||
metadata_types->clear();
|
||||
decoder->setReadOptions(1);
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
try
|
||||
{
|
||||
|
||||
@@ -9,7 +9,142 @@
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
/**
|
||||
static Mat makeCirclesImage(Size size, int type, int nbits)
|
||||
{
|
||||
Mat img(size, type);
|
||||
img.setTo(Scalar::all(0));
|
||||
RNG& rng = theRNG();
|
||||
int maxval = (int)(1 << nbits);
|
||||
for (int i = 0; i < 100; i++) {
|
||||
int x = rng.uniform(0, img.cols);
|
||||
int y = rng.uniform(0, img.rows);
|
||||
int radius = rng.uniform(5, std::min(img.cols, img.rows) / 5);
|
||||
int b = rng.uniform(0, maxval);
|
||||
int g = rng.uniform(0, maxval);
|
||||
int r = rng.uniform(0, maxval);
|
||||
circle(img, Point(x, y), radius, Scalar(b, g, r), -1, LINE_AA);
|
||||
}
|
||||
return img;
|
||||
}
|
||||
|
||||
static std::vector<uchar> getSampleExifData() {
|
||||
return {
|
||||
'M', 'M', 0, '*', 0, 0, 0, 8, 0, 10, 1, 0, 0, 4, 0, 0, 0, 1, 0, 0, 5,
|
||||
0, 1, 1, 0, 4, 0, 0, 0, 1, 0, 0, 2, 208, 1, 2, 0, 3, 0, 0, 0, 1,
|
||||
0, 10, 0, 0, 1, 18, 0, 3, 0, 0, 0, 1, 0, 1, 0, 0, 1, 14, 0, 2, 0, 0,
|
||||
0, '"', 0, 0, 0, 176, 1, '1', 0, 2, 0, 0, 0, 7, 0, 0, 0, 210, 1, 26,
|
||||
0, 5, 0, 0, 0, 1, 0, 0, 0, 218, 1, 27, 0, 5, 0, 0, 0, 1, 0, 0, 0,
|
||||
226, 1, '(', 0, 3, 0, 0, 0, 1, 0, 2, 0, 0, 135, 'i', 0, 4, 0, 0, 0,
|
||||
1, 0, 0, 0, 134, 0, 0, 0, 0, 0, 3, 144, 0, 0, 7, 0, 0, 0, 4, '0', '2',
|
||||
'2', '1', 160, 2, 0, 4, 0, 0, 0, 1, 0, 0, 5, 0, 160, 3, 0, 4, 0, 0,
|
||||
0, 1, 0, 0, 2, 208, 0, 0, 0, 0, 'S', 'a', 'm', 'p', 'l', 'e', ' ', '1', '0',
|
||||
'-', 'b', 'i', 't', ' ', 'i', 'm', 'a', 'g', 'e', ' ', 'w', 'i', 't', 'h', ' ',
|
||||
'm', 'e', 't', 'a', 'd', 'a', 't', 'a', 0, 'O', 'p', 'e', 'n', 'C', 'V', 0, 0,
|
||||
0, 0, 0, 'H', 0, 0, 0, 1, 0, 0, 0, 'H', 0, 0, 0, 1
|
||||
};
|
||||
}
|
||||
|
||||
static std::vector<uchar> getSampleXmpData() {
|
||||
return {
|
||||
'<','x',':','x','m','p','m','e','t','a',' ','x','m','l','n','s',':','x','=',
|
||||
'"','a','d','o','b','e',':','x','m','p','"','>',
|
||||
'<','x','m','p',':','C','r','e','a','t','o','r','T','o','o','l','>',
|
||||
'O','p','e','n','C','V',
|
||||
'<','/','x','m','p',':','C','r','e','a','t','o','r','T','o','o','l','>',
|
||||
'<','/','x',':','x','m','p','m','e','t','a','>',0
|
||||
};
|
||||
}
|
||||
|
||||
// Returns a Minimal ICC profile data (Generated with help from ChatGPT)
|
||||
static std::vector<uchar> getSampleIccpData() {
|
||||
std::vector<uchar> iccp_data(192, 0);
|
||||
|
||||
iccp_data[3] = 192; // Profile size: 192 bytes
|
||||
|
||||
iccp_data[12] = 'm';
|
||||
iccp_data[13] = 'n';
|
||||
iccp_data[14] = 't';
|
||||
iccp_data[15] = 'r';
|
||||
|
||||
iccp_data[16] = 'R';
|
||||
iccp_data[17] = 'G';
|
||||
iccp_data[18] = 'B';
|
||||
iccp_data[19] = ' ';
|
||||
|
||||
iccp_data[20] = 'X';
|
||||
iccp_data[21] = 'Y';
|
||||
iccp_data[22] = 'Z';
|
||||
iccp_data[23] = ' ';
|
||||
|
||||
// File signature 'acsp' at offset 36 (0x24)
|
||||
iccp_data[36] = 'a';
|
||||
iccp_data[37] = 'c';
|
||||
iccp_data[38] = 's';
|
||||
iccp_data[39] = 'p';
|
||||
|
||||
// Illuminant D50 at offset 68 (0x44), example values:
|
||||
iccp_data[68] = 0x00;
|
||||
iccp_data[69] = 0x00;
|
||||
iccp_data[70] = 0xF6;
|
||||
iccp_data[71] = 0xD6; // 0.9642
|
||||
iccp_data[72] = 0x00;
|
||||
iccp_data[73] = 0x01;
|
||||
iccp_data[74] = 0x00;
|
||||
iccp_data[75] = 0x00; // 1.0
|
||||
iccp_data[76] = 0x00;
|
||||
iccp_data[77] = 0x00;
|
||||
iccp_data[78] = 0xD3;
|
||||
iccp_data[79] = 0x2D; // 0.8249
|
||||
|
||||
// Tag count at offset 128 (0x80) = 1
|
||||
iccp_data[131] = 1;
|
||||
|
||||
// Tag record at offset 132 (0x84): signature 'desc', offset 128, size 64
|
||||
iccp_data[132] = 'd';
|
||||
iccp_data[133] = 'e';
|
||||
iccp_data[134] = 's';
|
||||
iccp_data[135] = 'c';
|
||||
|
||||
iccp_data[139] = 128; // offset
|
||||
|
||||
iccp_data[143] = 64; // size
|
||||
|
||||
// Tag data 'desc' at offset 128 (start of tag data)
|
||||
// Set type 'desc' etc. here, for simplicity fill zeros
|
||||
|
||||
iccp_data[144] = 'd';
|
||||
iccp_data[145] = 'e';
|
||||
iccp_data[146] = 's';
|
||||
iccp_data[147] = 'c';
|
||||
|
||||
// ASCII string length at offset 156
|
||||
iccp_data[156] = 20; // length
|
||||
|
||||
// ASCII string "Minimal ICC Profile" starting at offset 160
|
||||
iccp_data[160] = 'M';
|
||||
iccp_data[161] = 'i';
|
||||
iccp_data[162] = 'n';
|
||||
iccp_data[163] = 'i';
|
||||
iccp_data[164] = 'm';
|
||||
iccp_data[165] = 'a';
|
||||
iccp_data[166] = 'l';
|
||||
iccp_data[167] = ' ';
|
||||
iccp_data[168] = 'I';
|
||||
iccp_data[169] = 'C';
|
||||
iccp_data[170] = 'C';
|
||||
iccp_data[171] = ' ';
|
||||
iccp_data[172] = 'P';
|
||||
iccp_data[173] = 'r';
|
||||
iccp_data[174] = 'o';
|
||||
iccp_data[175] = 'f';
|
||||
iccp_data[176] = 'i';
|
||||
iccp_data[177] = 'l';
|
||||
iccp_data[178] = 'e';
|
||||
|
||||
return iccp_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to check whether the EXIF orientation tag was processed successfully or not.
|
||||
* The test uses a set of 8 images named testExifOrientation_{1 to 8}.(extension).
|
||||
* Each test image is a 10x10 square, divided into four smaller sub-squares:
|
||||
@@ -145,47 +280,24 @@ const std::vector<std::string> exif_files
|
||||
"readwrite/testExifOrientation_7.avif",
|
||||
"readwrite/testExifOrientation_8.avif",
|
||||
#endif
|
||||
#ifdef HAVE_WEBP
|
||||
"readwrite/testExifOrientation_1.webp",
|
||||
"readwrite/testExifOrientation_2.webp",
|
||||
"readwrite/testExifOrientation_3.webp",
|
||||
"readwrite/testExifOrientation_4.webp",
|
||||
"readwrite/testExifOrientation_5.webp",
|
||||
"readwrite/testExifOrientation_6.webp",
|
||||
"readwrite/testExifOrientation_7.webp",
|
||||
"readwrite/testExifOrientation_8.webp",
|
||||
#endif
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(Imgcodecs, Exif,
|
||||
testing::ValuesIn(exif_files));
|
||||
|
||||
static Mat makeCirclesImage(Size size, int type, int nbits)
|
||||
{
|
||||
Mat img(size, type);
|
||||
img.setTo(Scalar::all(0));
|
||||
RNG& rng = theRNG();
|
||||
int maxval = (int)(1 << nbits);
|
||||
for (int i = 0; i < 100; i++) {
|
||||
int x = rng.uniform(0, img.cols);
|
||||
int y = rng.uniform(0, img.rows);
|
||||
int radius = rng.uniform(5, std::min(img.cols, img.rows)/5);
|
||||
int b = rng.uniform(0, maxval);
|
||||
int g = rng.uniform(0, maxval);
|
||||
int r = rng.uniform(0, maxval);
|
||||
circle(img, Point(x, y), radius, Scalar(b, g, r), -1, LINE_AA);
|
||||
}
|
||||
return img;
|
||||
}
|
||||
|
||||
#ifdef HAVE_AVIF
|
||||
TEST(Imgcodecs_Avif, ReadWriteWithExif)
|
||||
{
|
||||
static const uchar exif_data[] = {
|
||||
'M', 'M', 0, '*', 0, 0, 0, 8, 0, 10, 1, 0, 0, 4, 0, 0, 0, 1, 0, 0, 5,
|
||||
0, 1, 1, 0, 4, 0, 0, 0, 1, 0, 0, 2, 208, 1, 2, 0, 3, 0, 0, 0, 1,
|
||||
0, 10, 0, 0, 1, 18, 0, 3, 0, 0, 0, 1, 0, 1, 0, 0, 1, 14, 0, 2, 0, 0,
|
||||
0, '"', 0, 0, 0, 176, 1, '1', 0, 2, 0, 0, 0, 7, 0, 0, 0, 210, 1, 26,
|
||||
0, 5, 0, 0, 0, 1, 0, 0, 0, 218, 1, 27, 0, 5, 0, 0, 0, 1, 0, 0, 0,
|
||||
226, 1, '(', 0, 3, 0, 0, 0, 1, 0, 2, 0, 0, 135, 'i', 0, 4, 0, 0, 0,
|
||||
1, 0, 0, 0, 134, 0, 0, 0, 0, 0, 3, 144, 0, 0, 7, 0, 0, 0, 4, '0', '2',
|
||||
'2', '1', 160, 2, 0, 4, 0, 0, 0, 1, 0, 0, 5, 0, 160, 3, 0, 4, 0, 0,
|
||||
0, 1, 0, 0, 2, 208, 0, 0, 0, 0, 'S', 'a', 'm', 'p', 'l', 'e', ' ', '1', '0',
|
||||
'-', 'b', 'i', 't', ' ', 'i', 'm', 'a', 'g', 'e', ' ', 'w', 'i', 't', 'h', ' ',
|
||||
'm', 'e', 't', 'a', 'd', 'a', 't', 'a', 0, 'O', 'p', 'e', 'n', 'C', 'V', 0, 0,
|
||||
0, 0, 0, 'H', 0, 0, 0, 1, 0, 0, 0, 'H', 0, 0, 0, 1
|
||||
};
|
||||
|
||||
int avif_nbits = 10;
|
||||
int avif_speed = 10;
|
||||
int avif_quality = 85;
|
||||
@@ -195,8 +307,8 @@ TEST(Imgcodecs_Avif, ReadWriteWithExif)
|
||||
Mat img = makeCirclesImage(Size(1280, 720), imgtype, avif_nbits);
|
||||
|
||||
std::vector<int> metadata_types = {IMAGE_METADATA_EXIF};
|
||||
std::vector<std::vector<uchar> > metadata(1);
|
||||
metadata[0].assign(exif_data, exif_data + sizeof(exif_data));
|
||||
std::vector<std::vector<uchar>> metadata = {
|
||||
getSampleExifData() };
|
||||
|
||||
std::vector<int> write_params = {
|
||||
IMWRITE_AVIF_DEPTH, avif_nbits,
|
||||
@@ -228,31 +340,63 @@ TEST(Imgcodecs_Avif, ReadWriteWithExif)
|
||||
}
|
||||
#endif // HAVE_AVIF
|
||||
|
||||
TEST(Imgcodecs_Jpeg, ReadWriteWithExif)
|
||||
#ifdef HAVE_WEBP
|
||||
TEST(Imgcodecs_WebP, Read_Write_With_Exif_Xmp_Iccp)
|
||||
{
|
||||
static const uchar exif_data[] = {
|
||||
'M', 'M', 0, '*', 0, 0, 0, 8, 0, 10, 1, 0, 0, 4, 0, 0, 0, 1, 0, 0, 5,
|
||||
0, 1, 1, 0, 4, 0, 0, 0, 1, 0, 0, 2, 208, 1, 2, 0, 3, 0, 0, 0, 1,
|
||||
0, 8, 0, 0, 1, 18, 0, 3, 0, 0, 0, 1, 0, 1, 0, 0, 1, 14, 0, 2, 0, 0,
|
||||
0, '!', 0, 0, 0, 176, 1, '1', 0, 2, 0, 0, 0, 7, 0, 0, 0, 210, 1, 26,
|
||||
0, 5, 0, 0, 0, 1, 0, 0, 0, 218, 1, 27, 0, 5, 0, 0, 0, 1, 0, 0, 0,
|
||||
226, 1, '(', 0, 3, 0, 0, 0, 1, 0, 2, 0, 0, 135, 'i', 0, 4, 0, 0, 0,
|
||||
1, 0, 0, 0, 134, 0, 0, 0, 0, 0, 3, 144, 0, 0, 7, 0, 0, 0, 4, '0', '2',
|
||||
'2', '1', 160, 2, 0, 4, 0, 0, 0, 1, 0, 0, 5, 0, 160, 3, 0, 4, 0, 0,
|
||||
0, 1, 0, 0, 2, 208, 0, 0, 0, 0, 'S', 'a', 'm', 'p', 'l', 'e', ' ', '8', '-',
|
||||
'b', 'i', 't', ' ', 'i', 'm', 'a', 'g', 'e', ' ', 'w', 'i', 't', 'h', ' ', 'm',
|
||||
'e', 't', 'a', 'd', 'a', 't', 'a', 0, 0, 'O', 'p', 'e', 'n', 'C', 'V', 0, 0,
|
||||
0, 0, 0, 'H', 0, 0, 0, 1, 0, 0, 0, 'H', 0, 0, 0, 1
|
||||
int imgtype = CV_MAKETYPE(CV_8U, 3);
|
||||
const std::string outputname = cv::tempfile(".webp");
|
||||
cv::Mat img = makeCirclesImage(cv::Size(160, 120), imgtype, 8);
|
||||
|
||||
std::vector<int> metadata_types = {IMAGE_METADATA_EXIF, IMAGE_METADATA_XMP, IMAGE_METADATA_ICCP};
|
||||
std::vector<std::vector<uchar>> metadata = {
|
||||
getSampleExifData(),
|
||||
getSampleXmpData(),
|
||||
getSampleIccpData()
|
||||
};
|
||||
|
||||
int webp_quality = 101; // 101 is lossless compression
|
||||
std::vector<int> write_params = {IMWRITE_WEBP_QUALITY, webp_quality};
|
||||
|
||||
imwriteWithMetadata(outputname, img, metadata_types, metadata, write_params);
|
||||
|
||||
std::vector<uchar> compressed;
|
||||
imencodeWithMetadata(outputname, img, metadata_types, metadata, compressed, write_params);
|
||||
|
||||
std::vector<int> read_metadata_types, read_metadata_types2;
|
||||
std::vector<std::vector<uchar>> read_metadata, read_metadata2;
|
||||
|
||||
cv::Mat img2 = imreadWithMetadata(outputname, read_metadata_types, read_metadata, cv::IMREAD_UNCHANGED);
|
||||
cv::Mat img3 = imdecodeWithMetadata(compressed, read_metadata_types2, read_metadata2, cv::IMREAD_UNCHANGED);
|
||||
|
||||
EXPECT_EQ(img2.cols, img.cols);
|
||||
EXPECT_EQ(img2.rows, img.rows);
|
||||
EXPECT_EQ(img2.type(), imgtype);
|
||||
|
||||
EXPECT_EQ(read_metadata_types, read_metadata_types2);
|
||||
EXPECT_EQ(read_metadata_types.size(), 3u);
|
||||
|
||||
EXPECT_EQ(read_metadata, read_metadata2);
|
||||
EXPECT_EQ(read_metadata, metadata);
|
||||
|
||||
EXPECT_EQ(cv::norm(img2, img3, cv::NORM_INF), 0.0);
|
||||
|
||||
double mse = cv::norm(img, img2, cv::NORM_L2SQR) / (img.rows * img.cols);
|
||||
EXPECT_EQ(mse, 0);
|
||||
|
||||
remove(outputname.c_str());
|
||||
}
|
||||
#endif // HAVE_WEBP
|
||||
|
||||
TEST(Imgcodecs_Jpeg, Read_Write_With_Exif)
|
||||
{
|
||||
int jpeg_quality = 95;
|
||||
int imgtype = CV_MAKETYPE(CV_8U, 3);
|
||||
const string outputname = cv::tempfile(".jpeg");
|
||||
Mat img = makeCirclesImage(Size(1280, 720), imgtype, 8);
|
||||
|
||||
std::vector<int> metadata_types = {IMAGE_METADATA_EXIF};
|
||||
std::vector<std::vector<uchar> > metadata(1);
|
||||
metadata[0].assign(exif_data, exif_data + sizeof(exif_data));
|
||||
std::vector<std::vector<uchar>> metadata = {
|
||||
getSampleExifData() };
|
||||
|
||||
std::vector<int> write_params = {
|
||||
IMWRITE_JPEG_QUALITY, jpeg_quality
|
||||
@@ -281,31 +425,16 @@ TEST(Imgcodecs_Jpeg, ReadWriteWithExif)
|
||||
remove(outputname.c_str());
|
||||
}
|
||||
|
||||
TEST(Imgcodecs_Png, ReadWriteWithExif)
|
||||
TEST(Imgcodecs_Png, Read_Write_With_Exif)
|
||||
{
|
||||
static const uchar exif_data[] = {
|
||||
'M', 'M', 0, '*', 0, 0, 0, 8, 0, 10, 1, 0, 0, 4, 0, 0, 0, 1, 0, 0, 5,
|
||||
0, 1, 1, 0, 4, 0, 0, 0, 1, 0, 0, 2, 208, 1, 2, 0, 3, 0, 0, 0, 1,
|
||||
0, 8, 0, 0, 1, 18, 0, 3, 0, 0, 0, 1, 0, 1, 0, 0, 1, 14, 0, 2, 0, 0,
|
||||
0, '!', 0, 0, 0, 176, 1, '1', 0, 2, 0, 0, 0, 7, 0, 0, 0, 210, 1, 26,
|
||||
0, 5, 0, 0, 0, 1, 0, 0, 0, 218, 1, 27, 0, 5, 0, 0, 0, 1, 0, 0, 0,
|
||||
226, 1, '(', 0, 3, 0, 0, 0, 1, 0, 2, 0, 0, 135, 'i', 0, 4, 0, 0, 0,
|
||||
1, 0, 0, 0, 134, 0, 0, 0, 0, 0, 3, 144, 0, 0, 7, 0, 0, 0, 4, '0', '2',
|
||||
'2', '1', 160, 2, 0, 4, 0, 0, 0, 1, 0, 0, 5, 0, 160, 3, 0, 4, 0, 0,
|
||||
0, 1, 0, 0, 2, 208, 0, 0, 0, 0, 'S', 'a', 'm', 'p', 'l', 'e', ' ', '8', '-',
|
||||
'b', 'i', 't', ' ', 'i', 'm', 'a', 'g', 'e', ' ', 'w', 'i', 't', 'h', ' ', 'm',
|
||||
'e', 't', 'a', 'd', 'a', 't', 'a', 0, 0, 'O', 'p', 'e', 'n', 'C', 'V', 0, 0,
|
||||
0, 0, 0, 'H', 0, 0, 0, 1, 0, 0, 0, 'H', 0, 0, 0, 1
|
||||
};
|
||||
|
||||
int png_compression = 3;
|
||||
int imgtype = CV_MAKETYPE(CV_8U, 3);
|
||||
const string outputname = cv::tempfile(".png");
|
||||
Mat img = makeCirclesImage(Size(1280, 720), imgtype, 8);
|
||||
Mat img = makeCirclesImage(Size(160, 120), imgtype, 8);
|
||||
|
||||
std::vector<int> metadata_types = {IMAGE_METADATA_EXIF};
|
||||
std::vector<std::vector<uchar> > metadata(1);
|
||||
metadata[0].assign(exif_data, exif_data + sizeof(exif_data));
|
||||
std::vector<std::vector<uchar>> metadata = {
|
||||
getSampleExifData() };
|
||||
|
||||
std::vector<int> write_params = {
|
||||
IMWRITE_PNG_COMPRESSION, png_compression
|
||||
@@ -334,6 +463,65 @@ TEST(Imgcodecs_Png, ReadWriteWithExif)
|
||||
remove(outputname.c_str());
|
||||
}
|
||||
|
||||
TEST(Imgcodecs_Png, Read_Write_With_Exif_Xmp_Iccp)
|
||||
{
|
||||
int png_compression = 3;
|
||||
int imgtype = CV_MAKETYPE(CV_8U, 3);
|
||||
const string outputname = cv::tempfile(".png");
|
||||
Mat img = makeCirclesImage(Size(160, 120), imgtype, 8);
|
||||
|
||||
std::vector<int> metadata_types = { IMAGE_METADATA_EXIF, IMAGE_METADATA_XMP, IMAGE_METADATA_ICCP };
|
||||
std::vector<std::vector<uchar>> metadata = {
|
||||
getSampleExifData(),
|
||||
getSampleXmpData(),
|
||||
getSampleIccpData(),
|
||||
};
|
||||
|
||||
std::vector<int> write_params = {
|
||||
IMWRITE_PNG_COMPRESSION, png_compression
|
||||
};
|
||||
|
||||
imwriteWithMetadata(outputname, img, metadata_types, metadata, write_params);
|
||||
std::vector<uchar> compressed;
|
||||
imencodeWithMetadata(outputname, img, metadata_types, metadata, compressed, write_params);
|
||||
|
||||
std::vector<int> read_metadata_types, read_metadata_types2;
|
||||
std::vector<std::vector<uchar> > read_metadata, read_metadata2;
|
||||
Mat img2 = imreadWithMetadata(outputname, read_metadata_types, read_metadata, IMREAD_UNCHANGED);
|
||||
Mat img3 = imdecodeWithMetadata(compressed, read_metadata_types2, read_metadata2, IMREAD_UNCHANGED);
|
||||
EXPECT_EQ(img2.cols, img.cols);
|
||||
EXPECT_EQ(img2.rows, img.rows);
|
||||
EXPECT_EQ(img2.type(), imgtype);
|
||||
|
||||
EXPECT_EQ(metadata_types, read_metadata_types);
|
||||
EXPECT_EQ(read_metadata_types, read_metadata_types2);
|
||||
EXPECT_EQ(metadata, read_metadata);
|
||||
remove(outputname.c_str());
|
||||
}
|
||||
|
||||
TEST(Imgcodecs_Png, Read_Exif_From_Text)
|
||||
{
|
||||
const string root = cvtest::TS::ptr()->get_data_path();
|
||||
const string filename = root + "../perf/320x260.png";
|
||||
const string dst_file = cv::tempfile(".png");
|
||||
|
||||
std::vector<uchar> exif_data =
|
||||
{ 'M' , 'M' , 0, '*' , 0, 0, 0, 8, 0, 4, 1,
|
||||
26, 0, 5, 0, 0, 0, 1, 0, 0, 0, 62, 1, 27, 0, 5, 0, 0, 0, 1, 0, 0, 0,
|
||||
70, 1, 40, 0, 3, 0, 0, 0, 1, 0, 2, 0, 0, 1, 49, 0, 2, 0, 0, 0, 18, 0,
|
||||
0, 0, 78, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, 1, 0, 0, 0, 96, 0, 0, 0,
|
||||
1, 80, 97, 105, 110, 116, 46, 78, 69, 84, 32, 118, 51, 46, 53, 46, 49, 48, 0
|
||||
};
|
||||
|
||||
std::vector<int> read_metadata_types;
|
||||
std::vector<std::vector<uchar> > read_metadata;
|
||||
Mat img = imreadWithMetadata(filename, read_metadata_types, read_metadata, IMREAD_GRAYSCALE);
|
||||
|
||||
std::vector<int> metadata_types = { IMAGE_METADATA_EXIF };
|
||||
EXPECT_EQ(read_metadata_types, metadata_types);
|
||||
EXPECT_EQ(read_metadata[0], exif_data);
|
||||
}
|
||||
|
||||
static size_t locateString(const uchar* exif, size_t exif_size, const std::string& pattern)
|
||||
{
|
||||
size_t plen = pattern.size();
|
||||
@@ -377,13 +565,13 @@ TEST_P(ReadExif_Sanity, Check)
|
||||
static const std::vector<ReadExif_Sanity_Params> exif_sanity_params
|
||||
{
|
||||
#ifdef HAVE_JPEG
|
||||
{"readwrite/testExifOrientation_3.jpg", 916, "Photoshop", 120},
|
||||
ReadExif_Sanity_Params("readwrite/testExifOrientation_3.jpg", 916, "Photoshop", 120),
|
||||
#endif
|
||||
#ifdef OPENCV_IMGCODECS_PNG_WITH_EXIF
|
||||
{"readwrite/testExifOrientation_5.png", 112, "ExifTool", 102},
|
||||
ReadExif_Sanity_Params("readwrite/testExifOrientation_5.png", 112, "ExifTool", 102),
|
||||
#endif
|
||||
#ifdef HAVE_AVIF
|
||||
{"readwrite/testExifOrientation_7.avif", 913, "Photoshop", 120},
|
||||
ReadExif_Sanity_Params("readwrite/testExifOrientation_7.avif", 913, "Photoshop", 120),
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@@ -12,14 +12,15 @@ TEST(Imgcodecs_Png, write_big)
|
||||
{
|
||||
const string root = cvtest::TS::ptr()->get_data_path();
|
||||
const string filename = root + "readwrite/read.png";
|
||||
const string dst_file = cv::tempfile(".png");
|
||||
Mat img;
|
||||
ASSERT_NO_THROW(img = imread(filename));
|
||||
ASSERT_FALSE(img.empty());
|
||||
EXPECT_EQ(13043, img.cols);
|
||||
EXPECT_EQ(13917, img.rows);
|
||||
ASSERT_NO_THROW(imwrite(dst_file, img));
|
||||
EXPECT_EQ(0, remove(dst_file.c_str()));
|
||||
|
||||
vector<uchar> buff;
|
||||
ASSERT_NO_THROW(imencode(".png", img, buff, { IMWRITE_PNG_ZLIBBUFFER_SIZE, INT_MAX }));
|
||||
EXPECT_EQ((size_t)816219, buff.size());
|
||||
}
|
||||
|
||||
TEST(Imgcodecs_Png, encode)
|
||||
|
||||
@@ -26,14 +26,14 @@ class BlockStorage {
|
||||
dynamicBlocks.push_back(new block_type);
|
||||
}
|
||||
BlockStorage(const BlockStorage&) = delete;
|
||||
BlockStorage(BlockStorage&&) noexcept = default;
|
||||
BlockStorage(BlockStorage&&) = default;
|
||||
~BlockStorage() {
|
||||
for(const auto & block : dynamicBlocks) {
|
||||
delete block;
|
||||
}
|
||||
}
|
||||
BlockStorage& operator=(const BlockStorage&) = delete;
|
||||
BlockStorage& operator=(BlockStorage&&) noexcept = default;
|
||||
BlockStorage& operator=(BlockStorage&&) = default;
|
||||
|
||||
void clear(void) {
|
||||
const size_t minDynamicBlocks = !staticBlocksCount ? 1 : 0;
|
||||
|
||||
@@ -190,7 +190,7 @@ public:
|
||||
ContourDataStorage(ContourDataStorage&&) noexcept = default;
|
||||
~ContourDataStorage() = default;
|
||||
ContourDataStorage& operator=(const ContourDataStorage&) = delete;
|
||||
ContourDataStorage& operator=(ContourDataStorage&&) noexcept = default;
|
||||
ContourDataStorage& operator=(ContourDataStorage&&) = default;
|
||||
public:
|
||||
typename storage_t::RangeIterator getRangeIterator(void) const {return storage->getRangeIterator(first, last);}
|
||||
public:
|
||||
@@ -243,7 +243,7 @@ public:
|
||||
Contour(const Contour&) = delete;
|
||||
Contour(Contour&& other) noexcept = default;
|
||||
Contour& operator=(const Contour&) = delete;
|
||||
Contour& operator=(Contour&& other) noexcept = default;
|
||||
Contour& operator=(Contour&& other) = default;
|
||||
~Contour() = default;
|
||||
void updateBoundingRect() {}
|
||||
bool isEmpty() const
|
||||
|
||||
@@ -261,6 +261,7 @@ enum VideoAccelerationType
|
||||
VIDEO_ACCELERATION_D3D11 = 2, //!< DirectX 11
|
||||
VIDEO_ACCELERATION_VAAPI = 3, //!< VAAPI
|
||||
VIDEO_ACCELERATION_MFX = 4, //!< libmfx (Intel MediaSDK/oneVPL)
|
||||
VIDEO_ACCELERATION_DRM = 5, //!< Raspberry Pi V4
|
||||
};
|
||||
|
||||
//! @} Hardware acceleration support
|
||||
|
||||
@@ -57,6 +57,9 @@ extern "C" {
|
||||
#ifdef HAVE_MFX // dependency only on MFX header files, no linkage dependency
|
||||
#include <libavutil/hwcontext_qsv.h>
|
||||
#endif
|
||||
#ifdef HAVE_DRM
|
||||
#include <libavutil/hwcontext_drm.h>
|
||||
#endif
|
||||
}
|
||||
|
||||
#define HW_DEFAULT_POOL_SIZE 32
|
||||
@@ -81,6 +84,7 @@ const char* getVideoAccelerationName(VideoAccelerationType va_type)
|
||||
case VIDEO_ACCELERATION_D3D11: return "d3d11";
|
||||
case VIDEO_ACCELERATION_VAAPI: return "vaapi";
|
||||
case VIDEO_ACCELERATION_MFX: return "mfx";
|
||||
case VIDEO_ACCELERATION_DRM : return "drm";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
@@ -119,6 +123,7 @@ std::string getDecoderConfiguration(VideoAccelerationType va_type, AVDictionary
|
||||
case VIDEO_ACCELERATION_D3D11: return "";
|
||||
case VIDEO_ACCELERATION_VAAPI: return "vaapi.iHD";
|
||||
case VIDEO_ACCELERATION_MFX: return "qsv.iHD";
|
||||
case VIDEO_ACCELERATION_DRM: return "drm";
|
||||
}
|
||||
return "";
|
||||
#endif
|
||||
@@ -158,6 +163,8 @@ std::string getEncoderConfiguration(VideoAccelerationType va_type, AVDictionary
|
||||
case VIDEO_ACCELERATION_D3D11: return "";
|
||||
case VIDEO_ACCELERATION_VAAPI: return "vaapi.iHD";
|
||||
case VIDEO_ACCELERATION_MFX: return "qsv.iHD";
|
||||
case VIDEO_ACCELERATION_DRM: return "";
|
||||
// Raspberry Pi 5 has no encoders, so we don't support it
|
||||
}
|
||||
return "unknown";
|
||||
#endif
|
||||
@@ -236,6 +243,18 @@ bool hw_check_device(AVBufferRef* ctx, AVHWDeviceType hw_type, const std::string
|
||||
dxgiDevice->Release();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if defined(HAVE_DRM)
|
||||
if (hw_device_ctx->type == AV_HWDEVICE_TYPE_DRM) {
|
||||
AVDRMDeviceContext *drm_ctx = (AVDRMDeviceContext *)hw_device_ctx->hwctx;
|
||||
if (drm_ctx->fd >= 0) {
|
||||
drmVersionPtr drm_version = drmGetVersion(drm_ctx->fd);
|
||||
if (drm_version) {
|
||||
device_name = drm_version->name;
|
||||
drmFreeVersion(drm_version);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (hw_device_ctx->type == AV_HWDEVICE_TYPE_VAAPI) {
|
||||
#if defined(HAVE_VA) && (VA_MAJOR_VERSION >= 1)
|
||||
@@ -908,6 +927,7 @@ VideoAccelerationType hw_type_to_va_type(AVHWDeviceType hw_type) {
|
||||
{ AV_HWDEVICE_TYPE_VAAPI, VIDEO_ACCELERATION_VAAPI },
|
||||
{ AV_HWDEVICE_TYPE_QSV, VIDEO_ACCELERATION_MFX },
|
||||
{ AV_HWDEVICE_TYPE_CUDA, (VideoAccelerationType)(1 << 11) },
|
||||
{ AV_HWDEVICE_TYPE_DRM, VIDEO_ACCELERATION_DRM }
|
||||
};
|
||||
for (const HWTypeFFMPEG& hw : known_hw_types) {
|
||||
if (hw_type == hw.hw_type)
|
||||
|
||||
@@ -393,6 +393,7 @@ std::ostream& operator<<(std::ostream& out, const VideoAccelerationType& va_type
|
||||
case VIDEO_ACCELERATION_D3D11: out << "D3D11"; return out;
|
||||
case VIDEO_ACCELERATION_VAAPI: out << "VAAPI"; return out;
|
||||
case VIDEO_ACCELERATION_MFX: out << "MFX"; return out;
|
||||
case VIDEO_ACCELERATION_DRM: out << "DRM"; return out;
|
||||
}
|
||||
out << cv::format("UNKNOWN(0x%ux)", static_cast<unsigned int>(va_type));
|
||||
return out;
|
||||
|
||||
@@ -35,6 +35,7 @@ std::ostream& operator<<(std::ostream& out, const VideoAccelerationType& va_type
|
||||
{VIDEO_ACCELERATION_D3D11, "D3D11"},
|
||||
{VIDEO_ACCELERATION_VAAPI, "VAAPI"},
|
||||
{VIDEO_ACCELERATION_MFX, "MFX"},
|
||||
{VIDEO_ACCELERATION_DRM, "DRM"},
|
||||
};
|
||||
for (const auto& va : va_types) {
|
||||
if (va_type == va.va_type) {
|
||||
|
||||
@@ -869,6 +869,7 @@ static const VideoAccelerationType hw_types[] = {
|
||||
VIDEO_ACCELERATION_D3D11,
|
||||
#else
|
||||
VIDEO_ACCELERATION_VAAPI,
|
||||
VIDEO_ACCELERATION_DRM,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@@ -179,8 +179,19 @@ static bool runCalibration( vector<vector<Point2f> > imagePoints,
|
||||
|
||||
vector<vector<Point3f> > objectPoints(1);
|
||||
calcChessboardCorners(boardSize, squareSize, objectPoints[0], patternType);
|
||||
int offset = patternType != CHARUCOBOARD ? boardSize.width - 1: boardSize.width - 2;
|
||||
objectPoints[0][offset].x = objectPoints[0][0].x + grid_width;
|
||||
// Board imperfectness correction introduced in PR #12772
|
||||
// The correction does not make sense for asymmetric and assymetric circles grids
|
||||
if (patternType == CHESSBOARD)
|
||||
{
|
||||
int offset = boardSize.width - 1;
|
||||
objectPoints[0][offset].x = objectPoints[0][0].x + grid_width;
|
||||
}
|
||||
else if (patternType == CHARUCOBOARD)
|
||||
{
|
||||
int offset = boardSize.width - 2;
|
||||
objectPoints[0][offset].x = objectPoints[0][0].x + grid_width;
|
||||
}
|
||||
|
||||
newObjPoints = objectPoints[0];
|
||||
|
||||
objectPoints.resize(imagePoints.size(),objectPoints[0]);
|
||||
|
||||
@@ -641,10 +641,15 @@ static bool runCalibration( Settings& s, Size& imageSize, Mat& cameraMatrix, Mat
|
||||
|
||||
vector<vector<Point3f> > objectPoints(1);
|
||||
calcBoardCornerPositions(s.boardSize, s.squareSize, objectPoints[0], s.calibrationPattern);
|
||||
if (s.calibrationPattern == Settings::Pattern::CHARUCOBOARD) {
|
||||
|
||||
// Board imperfectness correction introduced in PR #12772
|
||||
// The correction does not make sense for asymmetric and assymetric circles grids
|
||||
if (s.calibrationPattern == Settings::Pattern::CHARUCOBOARD)
|
||||
{
|
||||
objectPoints[0][s.boardSize.width - 2].x = objectPoints[0][0].x + grid_width;
|
||||
}
|
||||
else {
|
||||
else if (s.calibrationPattern != Settings::Pattern::CHESSBOARD)
|
||||
{
|
||||
objectPoints[0][s.boardSize.width - 1].x = objectPoints[0][0].x + grid_width;
|
||||
}
|
||||
newObjPoints = objectPoints[0];
|
||||
|
||||
@@ -42,6 +42,7 @@ struct {
|
||||
{ VIDEO_ACCELERATION_D3D11, "d3d11" },
|
||||
{ VIDEO_ACCELERATION_VAAPI, "vaapi" },
|
||||
{ VIDEO_ACCELERATION_MFX, "mfx" },
|
||||
{ VIDEO_ACCELERATION_DRM, "drm" },
|
||||
};
|
||||
|
||||
class FPSCounter {
|
||||
|
||||
Reference in New Issue
Block a user