调整并保存图像时保留元数据

8
我正在尝试调整并保存一张图片,这很容易实现(例如,参见此示例)。但是,使用此代码会从图像中剥离元数据信息。我似乎无法弄清楚如何保留jpeg图像的元数据。
编辑:示例代码
public static void ResizeMethodThree(string sourceFile, string targetFile)
{
    byte[] baSource = File.ReadAllBytes(sourceFile);

    PropertyItem[] propertyItems = new Bitmap(sourceFile).PropertyItems;

    using (Stream streamPhoto = new MemoryStream(baSource))
    {
        BitmapFrame bfPhoto = ReadBitmapFrame(streamPhoto);
        BitmapMetadata metaData = (BitmapMetadata)bfPhoto.Metadata;
        int nNewPictureSize = 200;
        int nWidth = 0;
        int nHeight = 0;

        if (bfPhoto.Width > bfPhoto.Height)
        {
            nWidth = nNewPictureSize;
            nHeight = (int)(bfPhoto.Height * nNewPictureSize / bfPhoto.Width);
        }
        else
        {
            nHeight = nNewPictureSize;
            nWidth = (int)(bfPhoto.Width * nNewPictureSize / bfPhoto.Height);
        }        

        BitmapFrame bfResize = ResizeHelper(bfPhoto, nWidth, nHeight, BitmapScalingMode.HighQuality);

        byte[] baResize = ToByteArray(bfResize);

        File.WriteAllBytes(targetFile, baResize);

        Image targetImage = new Bitmap(targetFile);
        foreach (var propertyItem in propertyItems)
        {
            targetImage.SetPropertyItem(propertyItem);
        }

        targetImage.Save(targetFile);
    }
}

public static BitmapFrame ResizeHelper(BitmapFrame photo, int width, 
                                       int height, BitmapScalingMode scalingMode)
{

    var group = new DrawingGroup();
    RenderOptions.SetBitmapScalingMode(
        group, scalingMode);
    group.Children.Add(
        new ImageDrawing(photo,
            new Rect(0, 0, width, height)));
    var targetVisual = new DrawingVisual();
    var targetContext = targetVisual.RenderOpen();
    targetContext.DrawDrawing(group);
    var target = new RenderTargetBitmap(
        width, height, 96, 96, PixelFormats.Default);
    targetContext.Close();
    target.Render(targetVisual);
    var targetFrame = BitmapFrame.Create(target);
    return targetFrame;
}

private static byte[] ToByteArray(BitmapFrame bfResize)
{
    using (MemoryStream msStream = new MemoryStream())
    {
        JpegBitmapEncoder jpgEncoder = new JpegBitmapEncoder();
        jpgEncoder.Frames.Add(bfResize);
        jpgEncoder.Save(msStream);
        return msStream.ToArray();
    }
}

private static BitmapFrame ReadBitmapFrame(Stream streamPhoto)
{
    BitmapDecoder bdDecoder = 
        BitmapDecoder.Create(streamPhoto, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None);
    return bdDecoder.Frames[0];
}

帖子中引用的链接已失效,并导向可疑网站。 - Alex Jorgenson
@AlexJorgenson - 已删除7年前的链接 - 谢谢。 - Wonko the Sane
1个回答

4

使用Image.PropertyItems属性获取源图像的元数据项目列表。循环遍历该列表,对目标图像调用Image.SetPropertyItem方法。通常应避免调整大小并重新压缩jpeg图像,最好从未压缩的原始图像开始工作以保持质量并避免伪影。


@Hans - 感谢您的回答。FYI- 调整大小只是我们在一个质量不是问题的应用程序中提高效率的一种方式(尽管这将被测试)- 我们也保留了原始文件。 - Wonko the Sane
将调整大小并保存回来的JPEG图像通常会占用更多的空间。 - Hans Passant
也许你应该使用targetImage.SetPropertyItem()而不是targetFile。期望我从中诊断出你的问题是不现实的。 - Hans Passant
之前的评论因为打错字被删除了。我正在编辑我的问题以展示我所尝试的内容。 - Wonko the Sane
1
这是一段丑陋的WPF和GDI+代码混合。它无法工作,因为您重新将图像保存为PNG格式。您必须传递ImageFormat参数。 - Hans Passant
+1 的丑陋组合 - 我自己对此并不太满意。我很想找到一个真正的 WPF 示例,但迄今为止一直没有成功。 - Wonko the Sane

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接