如何以原始格式保存图像?

Posted

技术标签:

【中文标题】如何以原始格式保存图像?【英文标题】:How to save an image in its original format? 【发布时间】:2011-02-17 13:15:49 【问题描述】:

如何使用原始编码保存图像?

似乎保存图像的唯一方法是使用 BitmapEncoder,但我不知道如何从图像中获取正确的格式。

例子:

Clipboard.GetImage() 返回一个 InteropBitmap,它似乎不包含有关原始格式的任何信息。

我也尝试过使用扩展方法:

public static void Save(this BitmapImage image, System.IO.Stream stream)

    var decoder = BitmapDecoder.Create(image.StreamSource, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
    var encoder = BitmapEncoder.Create(decoder.CodecInfo.ContainerFormat);
    foreach (var frame in decoder.Frames)
    
        encoder.Frames.Add(BitmapFrame.Create(frame));
    
    encoder.Save(stream);

但问题是

    例如,ImageSource 并不总是 BitmapImage (Clipboard.GetImage()),并且 image.StreamSource 在某些情况下可以为空(似乎是通过相对 Uri 加载图像时)

有什么建议吗?

【问题讨论】:

您能解释一下您遇到这种情况的背景吗?在我看来,如果您要保存图像,只需将其保存为您想要的格式即可。 BitmapImage 好像是一种格式,原图已经转换好了。 【参考方案1】:

这里的基本问题是“我如何获取任意 ImageSource 并了解它最初编码的格式?”

答案是您不能总是这样做,但对于大多数实际目的来说,有一个可用的解决方法。正如您上面的代码所示,一旦您了解了使用哪种格式,剩下的就很容易了。

您无法始终找出原始格式的原因是,可以(使用一些非常棘手的代码!)子类 BitmapSource 以创建一个新的 ImageSource,该 ImageSource 从您喜欢的任何地方获取其数据。例如,我可以实现一个返回随机像素的 PseudoRandomBitmapSource。在这种情况下,“原始格式”可能是用于随机数生成器的种子。

如果您正在处理内置的 ImageSource 类之一,找出原始编码的方法取决于您使用的确切类:

    对于 BitmapImage,您可以使用 StreamSource 或 UriSource,无论设置哪个。其中任何一个都可以传递给 BitmapDecoder.Create 重载。您的示例代码显示了如何为 StreamSource 执行此操作。它与 UriSource 完全相同,只是您需要 new Uri(BaseUri, UriSource) 并将其传递给 BitmapDecoder.Create 采用 Uri 的重载。

    对于 ColorConvertedBitmap、CroppedBitmap、FormatConvertedBitmap 和 TransformedBitmap,有一个公共的“Source”属性,您可以使用它来获取底层源,然后使用此算法递归检查其编码。

    对于 CachedBitmap,您只能通过内部字段获取源位图。如果你有足够的权限,你可以使用反射来访问,否则你就不走运了。

    对于 RenderTargetBitmap、WritableBitmap、D3DImage 和 DrawingImage,没有原始编码,因为图像是根据矢量格式或算法“动态”构建的。

    对于BitmapFrame,使用Decoder属性获取解码器,然后使用CodecInfo.ContainerFormat。

    对于 InteropBitmap 或 UnmanagedBitmapWrapper,它非常复杂。基本上,您需要使用反射来读取内部 WicSourceHandle 属性,然后调用 DangerousGetHandle() 来获取实际上是非托管 IUnkown 的 IntPtr。使用非托管代码,IWICBitmapDecoder 的 QueryInterface。如果成功,您可以调用 IWICBitmapDecoder.GetContainerFormat 来获取格式 Guid(这仍然是非托管代码)。否则,有关原始来源的所有信息都已丢失。

如您所见,在很多情况下您无法获取源(例如,完全解码位图的 InteropBitmap)或获取源需要特殊技术和权限(InteropBitmap 的非托管代码或反射) CachedBitmap 的内部字段)。

由于获取原始格式通常很难甚至不可能,因此最好在将这些信息传递到您的代码时寻找方法来保存它。如果控制图片的来源,可能就像创建一个 ImageSourceAndFormat 类一样简单:

public class BitmapSourceAndFormat

  public ImageSource Source  get; set; 
  public Guid OriginalFormat  get; set; 

如果您将图像放在剪贴板上,这将特别有用。除了正常添加图片到DataObject中,还可以添加BitmapSourceAndFormat对象。如果这样做,粘贴操作将收到包含这两种格式的 DataObject。然后,您的代码只需要首先检查 BitmapSourceAndFormat 对象。如果找到它可以简单地访问它以获得原始格式。如果不行,就必须求助于上述手段。

最后一点:对于剪贴板粘贴,您可以检查可用的数据格式字符串。一些有用的有:Bitmap、Dib、Tiff、Gif、Jpeg 和 FileName。对于“Tiff”、“Gif”和“Jpeg”,您可以硬编码所需的格式。对于“FileName”,您可以自己打开文件以获取要传递给 BitmapDecoder 的流。对于没有其他任何内容的“Dib”或“Bitmap”,您知道原始格式已经丢失。

【讨论】:

哇 - 这是一个很好的答案。太感谢了!关于复制/粘贴的重要提示! + 感谢您不仅告诉我我想知道什么,还告诉我我的基本问题是什么!我应该更好地描述它。

以上是关于如何以原始格式保存图像?的主要内容,如果未能解决你的问题,请参考以下文章

压缩原始图像缓冲区

以原始形式保存图像并且不压缩

在 OpenCV 中保存原始图像

Python:如何以通道,行,cols格式保存图像?

如何以两种不同的大小从图像选择器中保存图像

xml 如何使用URI以json格式将多个图像保存到localdb