Skip to content

Extend image I/O API with metadata support #27499

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jul 1, 2025
Prev Previous commit
Next Next commit
removed trailing whitespaces
  • Loading branch information
vpisarev committed Jun 30, 2025
commit 923c5ea16f3d01ddaa95a801ca50563f69d11b33
2 changes: 1 addition & 1 deletion modules/imgcodecs/src/exif.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ class ExifReader
* @return ExifEntru_t structure. Caller has to know what tag it calls in order to extract proper field from the structure ExifEntry_t
*/
ExifEntry_t getTag( const ExifTagName tag ) const;

/**
* @brief Get the whole exif buffer
*/
Expand Down
2 changes: 1 addition & 1 deletion modules/imgcodecs/src/grfmt_avif.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ AvifImageUniquePtr ConvertToAvif(const cv::Mat &img, bool lossless, int bit_dept
result->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_BT601;
result->yuvRange = AVIF_RANGE_FULL;
}

if (!metadata.empty()) {
const std::vector<uchar>& metadata_exif = metadata[IMAGE_METADATA_EXIF];
const std::vector<uchar>& metadata_xmp = metadata[IMAGE_METADATA_XMP];
Expand Down
6 changes: 3 additions & 3 deletions modules/imgcodecs/src/grfmt_base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,13 @@ class BaseImageDecoder {
* @return The type of the image.
*/
virtual int type() const { return m_type; }

/**
* @brief Checks whether file contains metadata of the certain type.
* @param type The type of metadata to look for
*/
virtual bool haveMetadata(ImageMetadataType type) const;

/**
* @brief Retrieves metadata (if any) of the certain kind.
* If there is no such metadata, the method returns empty array.
Expand Down Expand Up @@ -218,7 +218,7 @@ class BaseImageEncoder {
* @return true if the destination was successfully set, false otherwise.
*/
virtual bool setDestination(std::vector<uchar>& buf);

/**
* @brief Sets the metadata to write together with the image data
* @param type The type of metadata to add
Expand Down
4 changes: 2 additions & 2 deletions modules/imgcodecs/src/grfmt_jpeg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -814,15 +814,15 @@ bool JpegEncoder::write( const Mat& img, const std::vector<int>& params )
}

jpeg_start_compress( &cinfo, TRUE );

if (!m_metadata.empty()) {
const std::vector<uchar>& metadata_exif = m_metadata[IMAGE_METADATA_EXIF];
size_t exif_size = metadata_exif.size();
if (exif_size > 0u) {
const char app1_exif_prefix[] = {'E', 'x', 'i', 'f', '\0', '\0'};
size_t app1_exif_prefix_size = sizeof(app1_exif_prefix);
size_t data_size = exif_size + app1_exif_prefix_size;

std::vector<uchar> metadata_app1(data_size);
uchar* data = metadata_app1.data();
memcpy(data, app1_exif_prefix, app1_exif_prefix_size);
Expand Down
8 changes: 4 additions & 4 deletions modules/imgcodecs/src/loadsave.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@ 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();

Expand Down Expand Up @@ -569,7 +569,7 @@ imread_( const String& filename, int flags, OutputArray mat,
CV_CheckTrue(original_ptr == real_mat.data, "Internal imread issue");
success = true;
}

readMetadata(decoder, metadata_types, metadata);
}
catch (const cv::Exception& e)
Expand Down Expand Up @@ -1063,7 +1063,7 @@ static bool imwrite_( const String& filename, const std::vector<Mat>& img_vec,

encoder->setDestination( filename );
addMetadata(encoder, metadata_types, metadata);

#if CV_VERSION_MAJOR < 5 && defined(HAVE_IMGCODEC_HDR)
bool fixed = false;
std::vector<int> params_pair(2);
Expand Down Expand Up @@ -1248,7 +1248,7 @@ imdecode_( const Mat& buf, int flags, Mat& mat,
{
if (metadata_types)
metadata_types->clear();

CV_Assert(!buf.empty());
CV_Assert(buf.isContinuous());
CV_Assert(buf.checkVector(1, CV_8U) > 0);
Expand Down
36 changes: 18 additions & 18 deletions modules/imgcodecs/test/test_exif.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ struct ExifTag
std::string str;
rational64_t n={0, 1};
std::vector<rational64_t> v;

bool empty() const { return id == 0; }
size_t nvalues() const;
};
Expand Down Expand Up @@ -345,19 +345,19 @@ static void packIFD(const std::vector<ExifTag>* ifds, size_t nifds, size_t idx,
const std::vector<ExifTag>& ifd = ifds[idx];
std::vector<std::pair<size_t, size_t> > subifds;
size_t ntags = ifd.size();

size_t subifd_offset0 = offset + IFD_HDR_SIZE + ntags*IFD_ENTRY_SIZE;
size_t subifd_offset = subifd_offset0;
pack2(data, offset, (uint16_t)ntags, bigendian);

// first, pack the specified (by idx) IFD without subdirectories
for (const ExifTag& tag: ifd) {
pack2(data, offset, (uint16_t)tag.id, bigendian);

ExifTagType type = tag.type == TAG_TYPE_IFD ? TAG_TYPE_LONG : tag.type;
pack2(data, offset, (uint16_t)type, bigendian);
size_t nvalues = tag.nvalues();

pack4(data, offset, (uint32_t)nvalues, bigendian);
if (tag.type == TAG_TYPE_IFD) {
int64_t sub_idx = tag.n.num;
Expand All @@ -378,7 +378,7 @@ static void packIFD(const std::vector<ExifTag>* ifds, size_t nifds, size_t idx,
} else {
pack4(data, offset, 0u, bigendian);
}

if (type == TAG_TYPE_ASCII || type == TAG_TYPE_UNDEFINED) {
size_t v_size = tag.str.size();
memcpy(&data[tag_values_offset], tag.str.c_str(), v_size);
Expand Down Expand Up @@ -426,13 +426,13 @@ static void packIFD(const std::vector<ExifTag>* ifds, size_t nifds, size_t idx,
} else {
CV_Error_(Error::StsBadArg, ("unsupported tag type %d", tag.type));
}

if (!inline_values)
values_offset = tag_values_offset;
}

pack4(data, offset, 0u, bigendian);

// now pack all sub-IFDs and the next one, if any
for (auto sub: subifds) {
size_t subofs = sub.second;
Expand All @@ -447,14 +447,14 @@ static bool packExif(const std::vector<std::vector<ExifTag> >& exif,
size_t values_size = 0;
size_t ifd_size = computeIFDSize(exif.data(), exif.size(), 0u, values_size) + EXIF_HDR_SIZE;
data.resize(ifd_size + values_size);

char signature = bigendian ? 'M' : 'I';
size_t offset = 0;
pack1(data, offset, (uint8_t)signature);
pack1(data, offset, (uint8_t)signature);
pack2(data, offset, 42u, bigendian);
pack4(data, offset, 8u, bigendian);

packIFD(exif.data(), exif.size(), 0u, data, offset, ifd_size, bigendian);
return true;
}
Expand Down Expand Up @@ -580,22 +580,22 @@ TEST(Imgcodecs_Avif, ReadWriteWithExif)
int imgtype = CV_MAKETYPE(imgdepth, 3);
const string outputname = cv::tempfile(".avif");
Mat img = makeCirclesImage(Size(1280, 720), imgtype, avif_nbits);

std::vector<int> metadata_types = {IMAGE_METADATA_EXIF};
std::vector<std::vector<uchar> > metadata(1);
std::vector<std::vector<ExifTag> > exif = makeTestExif(img.size(), avif_nbits);
packExif(exif, metadata[0], true);

std::vector<int> write_params = {
IMWRITE_AVIF_DEPTH, avif_nbits,
IMWRITE_AVIF_SPEED, avif_speed,
IMWRITE_AVIF_QUALITY, avif_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;
Mat img2 = imreadWithMetadata(outputname, read_metadata_types, read_metadata, IMREAD_UNCHANGED);
Expand All @@ -622,16 +622,16 @@ TEST(Imgcodecs_Jpeg, ReadWriteWithExif)
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);
std::vector<std::vector<ExifTag> > exif = makeTestExif(img.size(), 8);
packExif(exif, metadata[0], true);

std::vector<int> write_params = {
IMWRITE_JPEG_QUALITY, jpeg_quality
};

imwriteWithMetadata(outputname, img, metadata_types, metadata, write_params);
std::vector<uchar> compressed;
imencodeWithMetadata(outputname, img, metadata_types, metadata, compressed, write_params);
Expand Down
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy