【问题标题】:Write metadata to both jpg and png将元数据写入 jpg 和 png
【发布时间】:2021-07-03 18:24:41
【问题描述】:

我需要为上传的图片添加元数据标签(描述)。

我找到了这个答案:https://stackoverflow.com/a/1764913/6776,它适用于 JPG 文件,但不适用于 PNG。

private string Tag = "test meta data";

private static Stream TagImage(Stream input, string type)
{
    bool isJpg = type.EndsWith("jpg", StringComparison.InvariantCultureIgnoreCase) || type.EndsWith("jpeg", StringComparison.InvariantCultureIgnoreCase);
    bool isPng = type.EndsWith("png", StringComparison.InvariantCultureIgnoreCase);

    BitmapDecoder decoder = null;

    if (isJpg)
    {
        decoder = new JpegBitmapDecoder(input, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
    }
    else if (isPng)
    {
        decoder = new PngBitmapDecoder(input, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
    }
    else
    {
        return input;
    }

    // modify the metadata
    BitmapFrame bitmapFrame = decoder.Frames[0];
    BitmapMetadata metaData = (BitmapMetadata)bitmapFrame.Metadata.Clone();
    metaData.Subject = Tag;
    metaData.Comment = Tag;
    metaData.Title = Tag;

    // get an encoder to create a new jpg file with the new metadata.      
    BitmapEncoder encoder = null;
    if (isJpg)
    {
        encoder = new JpegBitmapEncoder();
    }
    else if (isPng)
    {
        encoder = new PngBitmapEncoder();
    }

    encoder.Frames.Add(BitmapFrame.Create(bitmapFrame, bitmapFrame.Thumbnail, metaData, bitmapFrame.ColorContexts));

    // Save the new image 
    Stream output = new MemoryStream();
    encoder.Save(output);

    output.Seek(0, SeekOrigin.Begin);

    return output;
}

当我上传 jpg 时效果很好,但使用 png,在 metaData.Subject = Tag 行,它会抛出 System.NotSupportedException(此编解码器不支持指定的属性)。

更新

看来我必须根据图像格式使用不同的方法:

if (isJpg)
{
    metaData.SetQuery("/app1/ifd/exif:{uint=270}", Tag);
}
else
{
    metaData.SetQuery("/tEXt/{str=Description}", Tag);
}

基于the available formats' queries,第一个应该适用于两种格式。第二个也不起作用(它在图像中创建元数据但不保存其值)。

如果我尝试对 PNG 使用第一种方法 (/app1/ifd/exif),在 encoder.Save 行我得到一个不受支持的异常,“没有适合的成像组件”。

【问题讨论】:

  • 与您的问题无关,但我认为您的 isJpg = 语句有误。我假设您要测试“.jpg”或“.jpeg”,但您要测试“.jpg”两次。
  • 是的,从那时起它已在代码中修复,但问题中没有。谢谢!

标签: c# image


【解决方案1】:

我使用pngcs库解决了(你需要将下载的dll重命名为“pngcs.dll”)

我是这样实现的:

    using Hjg.Pngcs;  // https://code.google.com/p/pngcs/
using Hjg.Pngcs.Chunks;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MarkerGenerator.Utils
{
    class PngUtils
    {

        public string getMetadata(string file, string key)
        {

            PngReader pngr = FileHelper.CreatePngReader(file);
            //pngr.MaxTotalBytesRead = 1024 * 1024 * 1024L * 3; // 3Gb!
            //pngr.ReadSkippingAllRows();
            string data = pngr.GetMetadata().GetTxtForKey(key);
            pngr.End();
            return data; ;
        }


        public static void addMetadata(String origFilename, Dictionary<string, string> data)
        {
            String destFilename = "tmp.png";
            PngReader pngr = FileHelper.CreatePngReader(origFilename); // or you can use the constructor
            PngWriter pngw = FileHelper.CreatePngWriter(destFilename, pngr.ImgInfo, true); // idem
            //Console.WriteLine(pngr.ToString()); // just information
            int chunkBehav = ChunkCopyBehaviour.COPY_ALL_SAFE; // tell to copy all 'safe' chunks
            pngw.CopyChunksFirst(pngr, chunkBehav);          // copy some metadata from reader 
            foreach (string key in data.Keys)
            {
                PngChunk chunk = pngw.GetMetadata().SetText(key, data[key]);
                chunk.Priority = true;
            }

            int channels = pngr.ImgInfo.Channels;
            if (channels < 3)
                throw new Exception("This example works only with RGB/RGBA images");
            for (int row = 0; row < pngr.ImgInfo.Rows; row++)
            {
                ImageLine l1 = pngr.ReadRowInt(row); // format: RGBRGB... or RGBARGBA...
                pngw.WriteRow(l1, row);
            }
            pngw.CopyChunksLast(pngr, chunkBehav); // metadata after the image pixels? can happen
            pngw.End(); // dont forget this
            pngr.End();
            File.Delete(origFilename);
            File.Move(destFilename, origFilename);

        }

        public static void addMetadata(String origFilename,string key,string value)
        {
            Dictionary<string, string> data = new Dictionary<string, string>();
            data.Add(key, value);
            addMetadata(origFilename, data);
        }


    }
}

【讨论】:

  • 我们只有一个渠道的情况下如何处理? pngr.ImgInfo.Channels == 1 ?
【解决方案2】:

CompactExifLib 库可以在 JPEG、TIFF 和 PNG 文件中编写 EXIF 标签:

https://www.codeproject.com/Articles/5251929/CompactExifLib-Access-to-EXIF-Tags-in-JPEG-TIFF-an

纯C#编写,可以免费使用。

注意:我是这个库的作者。

【讨论】:

    【解决方案3】:

    PNG 格式不支持元数据:(

    但是XMP 可以,这在 JPEG、EXIF 元数据和 PNG 之间转换时可能会有所帮助。

    【讨论】:

    • 根据维基百科上的Portable Network Graphics 主题,PNG 可以存储元数据。所以我不确定你为什么认为它不能。
    • 我怀疑这是因为PngBitmapEncoder 的元数据成员不可设置。特别是encoder.Metadata = new BitmapMetadata("png"); 生成异常“指定的 BitmapEncoder 不支持全局元数据。”
    猜你喜欢
    • 2011-07-16
    • 2013-10-15
    • 1970-01-01
    • 2014-03-17
    • 1970-01-01
    • 1970-01-01
    • 2014-08-27
    • 2017-05-07
    • 1970-01-01
    相关资源
    最近更新 更多