1
0
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:
Alexander Smorkalov
2025-07-28 10:04:17 +03:00
28 changed files with 780 additions and 158 deletions
@@ -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;
}
};
+4
View File
@@ -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 =
+7 -2
View File
@@ -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))
+1 -3
View File
@@ -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) )
{
+24
View File
@@ -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 =
+34 -23
View File
@@ -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.
*/
+68
View File
@@ -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
*
+1
View File
@@ -154,6 +154,7 @@ public:
ExifReader();
~ExifReader();
bool processRawProfile(const char* profile, size_t profile_len);
/**
* @brief Parse the file with exif info
+24 -6
View File
@@ -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;
+20 -1
View File
@@ -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;
};
+97 -29
View File
@@ -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 );
-1
View File
@@ -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;
+77
View File
@@ -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> &params)
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);
+87 -2
View File
@@ -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));
+12 -6
View File
@@ -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
{
+262 -74
View File
@@ -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
};
+4 -3
View File
@@ -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;
+2 -2
View File
@@ -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
+20
View File
@@ -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)
+1
View File
@@ -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;
+1
View File
@@ -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) {
+1
View File
@@ -869,6 +869,7 @@ static const VideoAccelerationType hw_types[] = {
VIDEO_ACCELERATION_D3D11,
#else
VIDEO_ACCELERATION_VAAPI,
VIDEO_ACCELERATION_DRM,
#endif
};
+13 -2
View File
@@ -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];
+1
View File
@@ -42,6 +42,7 @@ struct {
{ VIDEO_ACCELERATION_D3D11, "d3d11" },
{ VIDEO_ACCELERATION_VAAPI, "vaapi" },
{ VIDEO_ACCELERATION_MFX, "mfx" },
{ VIDEO_ACCELERATION_DRM, "drm" },
};
class FPSCounter {