mirror of
https://github.com/opencv/opencv.git
synced 2026-07-30 15:53:03 +04:00
Merge branch 4.x
This commit is contained in:
@@ -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
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user