From c932fc3a4737d7508b7f77d3f5840dec88a95fe7 Mon Sep 17 00:00:00 2001 From: Canming Huang Date: Fri, 25 Jul 2025 11:54:18 -0400 Subject: [PATCH] Added CvInvoke.ImwriteWithMetadata, ImreadWithMetadata, ImencodeWithMetadata & ImdecodeWithMetadata. --- .../imgcodecs/imgcodecs_c_extra.cpp | 126 +++++++++--- Emgu.CV.Extern/imgcodecs/imgcodecs_c_extra.h | 39 +++- Emgu.CV/PInvoke/CvInvokeImgcodecs.cs | 180 +++++++++++++++++- 3 files changed, 311 insertions(+), 34 deletions(-) diff --git a/Emgu.CV.Extern/imgcodecs/imgcodecs_c_extra.cpp b/Emgu.CV.Extern/imgcodecs/imgcodecs_c_extra.cpp index 4ede74ae7..da2199d6b 100644 --- a/Emgu.CV.Extern/imgcodecs/imgcodecs_c_extra.cpp +++ b/Emgu.CV.Extern/imgcodecs/imgcodecs_c_extra.cpp @@ -41,6 +41,25 @@ bool cveImwritemulti(cv::String* filename, cv::_InputArray* img, std::vector* metadataTypes, + cv::_InputArray* metadata, + std::vector* params) +{ +#ifdef HAVE_OPENCV_IMGCODECS + return cv::imwriteWithMetadata( + *filename, + *img, + *metadataTypes, + *metadata, + params ? *params : std::vector()); +#else + throw_no_imgcodecs(); +#endif +} + void cveImread(cv::String* fileName, int flags, cv::Mat* result) { #ifdef HAVE_OPENCV_IMGCODECS @@ -60,6 +79,25 @@ bool cveImreadmulti(const cv::String* filename, std::vector* mats, int #endif } +void cveImreadWithMetadata( + const cv::String* filename, + std::vector* metadataTypes, + cv::_OutputArray* metadata, + int flags, + cv::Mat* result) +{ +#ifdef HAVE_OPENCV_IMGCODECS + cv::Mat m = cv::imreadWithMetadata( + *filename, + *metadataTypes, + *metadata, + flags); + cv::swap(*result, m); +#else + throw_no_imgcodecs(); +#endif +} + void cveImdecode(cv::_InputArray* buf, int flags, cv::Mat* dst) { #ifdef HAVE_OPENCV_IMGCODECS @@ -68,6 +106,39 @@ void cveImdecode(cv::_InputArray* buf, int flags, cv::Mat* dst) throw_no_imgcodecs(); #endif } + +bool cveImdecodemulti(cv::_InputArray* buf, int flags, std::vector* mats, cv::Range* range) +{ +#ifdef HAVE_OPENCV_IMGCODECS + if ((range->start == 0) && (range->end == 0)) + return cv::imdecodemulti(*buf, flags, *mats); + else + return cv::imdecodemulti(*buf, flags, *mats, *range); +#else + throw_no_imgcodecs(); +#endif +} + +void cveImdecodeWithMetadata( + cv::_InputArray* buf, + std::vector* metadataTypes, + cv::_OutputArray* metadata, + int flags, + cv::Mat* dst) +{ +#ifdef HAVE_OPENCV_IMGCODECS + cv::Mat result = cv::imdecodeWithMetadata( + *buf, + *metadataTypes, + *metadata, + flags + ); + cv::swap(result, *dst); +#else + throw_no_imgcodecs(); +#endif +} + bool cveImencode(cv::String* ext, cv::_InputArray* img, std::vector< unsigned char >* buf, std::vector< int >* params) { #ifdef HAVE_OPENCV_IMGCODECS @@ -77,6 +148,39 @@ bool cveImencode(cv::String* ext, cv::_InputArray* img, std::vector< unsigned ch #endif } +bool cveImencodemulti(cv::String* ext, cv::_InputArray* imgs, std::vector* buf, std::vector* params) +{ +#ifdef HAVE_OPENCV_IMGCODECS + if (params) + return cv::imencodemulti(*ext, *imgs, *buf, *params); + else + return cv::imencodemulti(*ext, *imgs, *buf); +#else + throw_no_imgcodecs(); +#endif +} + +bool cveImencodeWithMetadata( + cv::String* ext, + cv::_InputArray* img, + std::vector< int >* metadataTypes, + cv::_InputArray* metadata, + std::vector< uchar >* buf, + std::vector< int >* params) +{ +#ifdef HAVE_OPENCV_IMGCODECS + return cv::imencodeWithMetadata( + *ext, + *img, + *metadataTypes, + *metadata, + *buf, + params ? *params : std::vector()); +#else + throw_no_imgcodecs(); +#endif +} + cv::Animation* cveAnimationCreate(int loopCount, CvScalar* bgColor) { #ifdef HAVE_OPENCV_IMGCODECS @@ -131,25 +235,3 @@ bool cveImwriteAnimation(cv::String* filename, cv::Animation* animation, std::ve #endif } -bool cveImencodemulti(cv::String* ext, cv::_InputArray* imgs, std::vector* buf, std::vector* params) -{ -#ifdef HAVE_OPENCV_IMGCODECS - if (params) - return cv::imencodemulti(*ext, *imgs, *buf, *params); - else - return cv::imencodemulti(*ext, *imgs, *buf); -#else - throw_no_imgcodecs(); -#endif -} -bool cveImdecodemulti(cv::_InputArray* buf, int flags, std::vector* mats, cv::Range* range) -{ -#ifdef HAVE_OPENCV_IMGCODECS - if ((range->start == 0) && (range->end == 0)) - return cv::imdecodemulti(*buf, flags, *mats); - else - return cv::imdecodemulti(*buf, flags, *mats, *range); -#else - throw_no_imgcodecs(); -#endif -} \ No newline at end of file diff --git a/Emgu.CV.Extern/imgcodecs/imgcodecs_c_extra.h b/Emgu.CV.Extern/imgcodecs/imgcodecs_c_extra.h index 96ed7fb4d..bf378ca2f 100644 --- a/Emgu.CV.Extern/imgcodecs/imgcodecs_c_extra.h +++ b/Emgu.CV.Extern/imgcodecs/imgcodecs_c_extra.h @@ -22,17 +22,50 @@ CVAPI(bool) cveHaveImageWriter(cv::String* filename); CVAPI(bool) cveImwrite(cv::String* filename, cv::_InputArray* img, std::vector* params); CVAPI(bool) cveImwritemulti(cv::String* filename, cv::_InputArray* img, std::vector* params); +CVAPI(bool) cveImwriteWithMetadata( + cv::String* filename, + cv::_InputArray* img, + std::vector< int >* metadataTypes, + cv::_InputArray* metadata, + std::vector< int >* params); CVAPI(void) cveImread(cv::String* fileName, int flags, cv::Mat* result); CVAPI(bool) cveImreadmulti(const cv::String* filename, std::vector* mats, int flags); +CVAPI(void) cveImreadWithMetadata( + const cv::String* filename, + std::vector* metadataTypes, + cv::_OutputArray* metadata, + int flags, + cv::Mat* result); CVAPI(void) cveImdecode(cv::_InputArray* buf, int flags, cv::Mat* dst); CVAPI(bool) cveImdecodemulti(cv::_InputArray* buf, int flags, std::vector* mats, cv::Range* range); +CVAPI(void) cveImdecodeWithMetadata( + cv::_InputArray* buf, + std::vector< int >* metadataTypes, + cv::_OutputArray* metadata, + int flags, + cv::Mat* dst); + +CVAPI(bool) cveImencode( + cv::String* ext, + cv::_InputArray* img, + std::vector< unsigned char >* buf, + std::vector< int >* params); +CVAPI(bool) cveImencodemulti( + cv::String* ext, + cv::_InputArray* imgs, + std::vector* buf, + std::vector* params); +CVAPI(bool) cveImencodeWithMetadata( + cv::String* ext, + cv::_InputArray* img, + std::vector< int >* metadataTypes, + cv::_InputArray* metadata, + std::vector< uchar >* buf, + std::vector< int >* params); -CVAPI(bool) cveImencode(cv::String* ext, cv::_InputArray* img, std::vector< unsigned char >* buf, std::vector< int >* params); -CVAPI(bool) cveImencodemulti(cv::String* ext, cv::_InputArray* imgs, std::vector* buf, std::vector* params); - CVAPI(cv::Animation*) cveAnimationCreate(int loopCount, CvScalar* bgColor); CVAPI(void) cveAnimationRelease(cv::Animation** animation); CVAPI(std::vector*) cveAnimationGetDurations(cv::Animation* animation); diff --git a/Emgu.CV/PInvoke/CvInvokeImgcodecs.cs b/Emgu.CV/PInvoke/CvInvokeImgcodecs.cs index 920063e12..2de28fb16 100644 --- a/Emgu.CV/PInvoke/CvInvokeImgcodecs.cs +++ b/Emgu.CV/PInvoke/CvInvokeImgcodecs.cs @@ -2,15 +2,16 @@ // Copyright (C) 2004-2025 by EMGU Corporation. All rights reserved. //---------------------------------------------------------------------------- -using System; -using System.IO; -using System.Collections.Generic; -using System.Runtime.InteropServices; using Emgu.CV.CvEnum; using Emgu.CV.Structure; using Emgu.CV.Util; using Emgu.Util; using Emgu.Util.TypeEnum; +using System; +using System.Collections.Generic; +using System.IO; +using System.Numerics; +using System.Runtime.InteropServices; namespace Emgu.CV { @@ -123,6 +124,50 @@ namespace Emgu.CV [return: MarshalAs(CvInvoke.BoolMarshalType)] internal static extern bool cveImreadmulti(IntPtr filename, IntPtr mats, CvEnum.ImreadModes flags); + /// + /// Loads an image from the specified file and returns the pointer to the loaded image. Currently the following file formats are supported: + /// Windows bitmaps - BMP, DIB; + /// JPEG files - JPEG, JPG, JPE; + /// Portable Network Graphics - PNG; + /// Portable image format - PBM, PGM, PPM; + /// Sun rasters - SR, RAS; + /// TIFF files - TIFF, TIF; + /// OpenEXR HDR images - EXR; + /// JPEG 2000 images - jp2. + /// + /// The name of the file to be loaded + /// The image reading mode + /// The loaded image + public static Mat ImreadWithMetadata( + String filename, + VectorOfInt metadataTypes, + IOutputArrayOfArrays metaData, + ImreadModes readMode = ImreadModes.ColorBgr) + { + Mat result = new Mat(); + using (CvString csFilename = new CvString(filename)) + using(OutputArray oaMetaData = metaData.GetOutputArray()) + { + cveImreadWithMetadata( + csFilename, + metadataTypes, + oaMetaData, + readMode, + result + ); + } + return result; + } + + [DllImport(ExternLibrary, CallingConvention = CvInvoke.CvCallingConvention)] + [return: MarshalAs(CvInvoke.BoolMarshalType)] + private static extern void cveImreadWithMetadata( + IntPtr filename, + IntPtr metadataTypes, + IntPtr metadata, + ImreadModes flags, + IntPtr result); + private static void PushParameters(VectorOfInt vec, KeyValuePair[] parameters) { if (parameters == null || parameters.Length == 0) @@ -184,6 +229,82 @@ namespace Emgu.CV [return: MarshalAs(CvInvoke.BoolMarshalType)] private static extern bool cveImwrite(IntPtr filename, IntPtr image, IntPtr parameters); + + /// + /// Saves the image to the specified file. The function imwrite saves the image to the specified file. The image format is chosen based on the filename extension (see cv::imread for the list of extensions). + /// + /// The name of the file to be saved to + /// The image to be saved + /// The parameters + /// In general, only 8-bit single-channel or 3-channel (with 'BGR' channel order) images can be saved using this function, with these exceptions: + /// 16-bit unsigned(CV_16U) images can be saved in the case of PNG, JPEG 2000, and TIFF formats + /// 32-bit float (CV_32F) images can be saved in PFM, TIFF, OpenEXR, and Radiance HDR formats; 3-channel(CV_32FC3) TIFF images will be saved using the LogLuv high dynamic range encoding(4 bytes per pixel) + /// PNG images with an alpha channel can be saved using this function.To do this, create 8-bit (or 16-bit) 4-channel image BGRA, where the alpha channel goes last. Fully transparent pixels should have alpha set to 0, fully opaque pixels should have alpha set to 255 / 65535(see the code sample below). + /// Multiple images(vector of Mat) can be saved in TIFF format(see the code sample below). + /// If the image format is not supported, the image will be converted to 8 - bit unsigned(CV_8U) and saved that way. + /// If the format, depth or channel order is different, use Mat::convertTo and cv::cvtColor to convert it before saving. Or, use the universal FileStorage I / O functions to save the image to XML or YAML format. + /// true if success + public static bool ImwriteWithMetadata( + String filename, + IInputArray image, + VectorOfInt metadataTypes, + IInputArrayOfArrays metaData, + params KeyValuePair[] parameters) + { + using (CvString s = new CvString(filename)) + { + bool containsUnicode = (s.Length != filename.Length); + if (containsUnicode && + (Emgu.Util.Platform.OperationSystem != Emgu.Util.Platform.OS.MacOS) && + (Emgu.Util.Platform.OperationSystem != Emgu.Util.Platform.OS.Linux)) + { + //Handle unicode in Windows platform + //Work around for Open CV ticket: + //https://github.com/Itseez/opencv/issues/4292 + //https://github.com/Itseez/opencv/issues/4866 + System.IO.FileInfo fi = new System.IO.FileInfo(filename); + + using (VectorOfByte vb = new VectorOfByte()) + { + CvInvoke.ImencodeWithMetadata( + fi.Extension, + image, + metadataTypes, + metaData, + vb, + parameters); + byte[] arr = vb.ToArray(); + System.IO.File.WriteAllBytes(filename, arr); + return true; + } + } + else + { + using (VectorOfInt vec = new VectorOfInt()) + using (InputArray iaMetaData = metaData.GetInputArray()) + using (InputArray iaImage = image.GetInputArray()) + { + PushParameters(vec, parameters); + return cveImwriteWithMetadata( + s, + iaImage, + metadataTypes, + iaMetaData, + vec); + } + } + } + } + + [DllImport(ExternLibrary, CallingConvention = CvInvoke.CvCallingConvention)] + [return: MarshalAs(CvInvoke.BoolMarshalType)] + private static extern bool cveImwriteWithMetadata( + IntPtr filename, + IntPtr img, + IntPtr metadataTypes, + IntPtr metadata, + IntPtr parameters); + /// /// Decode image stored in the buffer /// @@ -310,6 +431,51 @@ namespace Emgu.CV [return: MarshalAs(CvInvoke.BoolMarshalType)] private static extern bool cveImencode(IntPtr ext, IntPtr image, IntPtr buffer, IntPtr parameters); + /// + /// Encodes an image into a memory buffer. + /// The function imencode compresses the image and stores it in the memory buffer that is resized to fit the result. + /// + /// File extension that defines the output format. Must include a leading period. + /// Image to be compressed. + /// Vector with types of metadata chucks stored in metadata to write + /// Vector of vectors or vector of matrices with chunks of metadata to store into the file + /// Output buffer resized to fit the compressed image. + /// Format-specific parameters. + /// True if successfully encoded the image into the buffer. + public static bool ImencodeWithMetadata( + String ext, + IInputArray image, + VectorOfInt metadataTypes, + IInputArrayOfArrays metaData, + VectorOfByte buf, + params KeyValuePair[] parameters) + { + using (CvString extStr = new CvString(ext)) + using (VectorOfInt p = new VectorOfInt()) + using (InputArray iaImage = image.GetInputArray()) + using (InputArray iaMetaData = metaData.GetInputArray()) + { + PushParameters(p, parameters); + return cveImencodeWithMetadata( + extStr, + iaImage, + metadataTypes, + iaMetaData, + buf, + p); + } + } + + [DllImport(ExternLibrary, CallingConvention = CvInvoke.CvCallingConvention)] + [return: MarshalAs(CvInvoke.BoolMarshalType)] + private static extern bool cveImencodeWithMetadata( + IntPtr ext, + IntPtr img, + IntPtr metadataTypes, + IntPtr metadata, + IntPtr buf, + IntPtr parameters); + /// /// Encodes array of images into a memory buffer. /// @@ -384,13 +550,9 @@ namespace Emgu.CV /// Returns true if the animation was successfully saved; returns false otherwise. public static bool ImwriteAnimation(String fileName, Animation animation, VectorOfInt parameters = null) { - using (CvString csFileName = new CvString(fileName)) { - if (parameters == null) - return cveImwriteAnimation(csFileName, animation, IntPtr.Zero); - else - return cveImwriteAnimation(csFileName, animation, parameters); + return cveImwriteAnimation(csFileName, animation, parameters); } }