WinRT图像处理

8
我和我的朋友昨晚花了大部分时间在尝试处理metro应用中的一些图片上,几乎要把我们的头发都掉光了。我们使用共享Charm将图片导入应用程序,然后我想对它们进行一些其他操作,如裁剪图像并将其保存回appdata文件夹中。这证明非常令人沮丧。
我的问题是,在所有这些之后,"有没有正确的方法来完成这个任务,而不感觉像是在拼凑不匹配的拼图碎片?"
当使用共享多个图像到应用程序时,它们以Windows.Storage.StorageFile列表的形式出现。以下是用于处理此操作的一些代码。
var storageItems = await _shareOperation.Data.GetStorageItemsAsync();

foreach (StorageFile item in storageItems)
{
    var stream = await item.OpenReadAsync();
    var properties = await item.Properties.GetImagePropertiesAsync();

    var image = new WriteableBitmap((Int32)properties.Width, (Int32)properties.Height);
    image.SetSource(stream);

    images.Add(image);
}

一些在线搜索表明,目前只有 Windows.UI.Xaml.Media.Imaging.WriteableBitmap 能够让您访问图像中的像素数据。这个问题 包含了一个充满扩展方法的有用答案,可以将图像保存到文件中,因此我们使用了这些方法。
当我稍后尝试重新打开文件时,我们遇到了最严重的问题。我做了类似之前的事情:
var files = await ApplicationData.Current.LocalFolder.GetFilesAsync();

foreach (var file in files)
{
    var fileStream = await file.OpenReadAsync();
    var properties = await file.Properties.GetImagePropertiesAsync();
    var bitmap = new WriteableBitmap((Int32)properties.Width, (Int32)properties.Height);
    bitmap.SetSource(fileStream);

    System.IO.Stream stream = bitmap.PixelBuffer.AsStream();

这里有一个问题。如果我想从字节流中获取字节数,这个流有多长?

    // CRASH! Length isn't supported on an IRandomAccessStream.
    var pixels = new byte[fileStream.Length];

好的,请再试一次。

    var pixels = new byte[stream.Length];

这段代码可以正常工作,但是如果图片被压缩,数据流会比预期的短,因此最终会出现越界异常。目前先假设它是未压缩的位图。
    await _stream.ReadAsync(pixels, 0, pixels.Length);

好吧,你猜怎么着。尽管我使用bitmap.SetSource(fileStream);来读取数据,但我的字节数组仍然全是零。我不知道为什么。如果我通过示例数据组将同样的bitmap传递到我的UI中,图像就会正常显示。所以它显然在那个位图中有像素数据,但我为什么不能从bitmap.PixelBuffer中读取它呢?为什么?

最后,这是实际起作用的方法。

    var decoder = await BitmapDecoder.CreateAsync(BitmapDecoder.PngDecoderId, fileStream);
    var data = await decoder.GetPixelDataAsync();
    var bytes = data.DetachPixelData();

    /* process my data, finally */

} // end of that foreach I started a while ago

现在我已经有了图像数据,但我仍然有一个大问题。为了对其进行任何处理,我必须对其格式做出假设。我不知道它是rgba、rgb、abgr、bgra或其他格式。如果我猜错了,我的处理就会失败。我已经进行了几十次测试,结果得到的都是零字节和损坏的图像、颜色错误等问题。我希望从调用await file.Properties.GetImagePropertiesAsync();获取的properties中能够找到一些信息,但没有运气。它只包含图像的宽度和高度以及其他一些无用的东西。最小的文档here

那么,为什么这个过程如此痛苦?这只是反映了库目前的不成熟,我可以期待它变得更好吗?还是已经有某种标准方法来完成这个任务?我希望它像System.Drawing那样容易。它提供了您所需的所有数据,并愉快地正确加载任何图像类型,而不需要让您自己处理流。


“我不知道它是rgba、rgb、abgr、bgra或者其他格式。” 其实,通过选择正确的格式,你完全可以知道它是什么。只需要处理一次PNG文件,就可以将这种逻辑应用到所有PNG文件中。 - jheriko
此外,每个人都实现图像库得很差,尽管BitmapEncoder/BitmapDecoder非常好用。在大多数情况下,在大多数平台上,stb_image.c比可用的任何本机库做得更好,因为它提供了一个简单而正确的接口(字节、尺寸、格式、无Chrome和样板代码)。 - jheriko
1个回答

2
  1. From what I have seen - when you are planning on loading the WriteableBitmap with a stream - you don't need to check the image dimensions - just do new WriteableBitmap(1,1), then call SetSource().
  2. Not sure why you were thinking var pixels = new byte[fileStream.Length]; would work, since the fileStream has the compressed image bytes and not a pixel array.
  3. You might need to seek to the beginning of the stream to get the pixels array:

    var pixelStream = pixelBuffer.AsStream();
    var bytes = new byte[this.pixelStream.Length];
    this.pixelStream.Seek(0, SeekOrigin.Begin);
    this.pixelStream.Read(bytes, 0, Bytes.Length);
    
  4. I had started working on a WinRT port of WriteableBitmapEx - maybe it could help you: http://bit.ly/WriteableBitmapExWinRT. I have not tested it well and it is based on an older version of WBX, but it is fairly complete in terms of feature support. Might be a tad slower than it is possible too.


啊,所以我不需要解码器,只需要定位到开头。谢谢!我会试一下的。还会去看看你的库! - Tesserex
寻找流失败了,仍然有所有的零字节。抱歉。 - Tesserex
也许你需要等待图像加载完成才能访问像素缓冲区?我建议在访问PixelBuffer之前使用await Task.Delay(1000)。如果有帮助的话-也许有一个等待事件,如bitmap.Loaded。 - Filip Skakun

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