解码多页 TIFF 容器
Decode a Multipage TIFF Container
使用 的反向词,我能够使用 Windows.Graphics.Imaging[= 编写一个函数来解码多页 TIFF 文件19=] :
private async Task TIFHandler( StorageFile file)
{
var random = new Random();
StorageFolder storage = null;
try
{
uint frameCount;
using (IRandomAccessStream randomAccessStream = await file.OpenAsync(FileAccessMode.Read, StorageOpenOptions.None))
{
Windows.Graphics.Imaging.BitmapDecoder bitmapDecoder = await Windows.Graphics.Imaging.BitmapDecoder.CreateAsync(Windows.Graphics.Imaging.BitmapDecoder.TiffDecoderId, randomAccessStream);
frameCount = bitmapDecoder.FrameCount;
if (frameCount == 16)
{
StorageFolder mainfolder = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFolderAsync((string)ApplicationData.Current.LocalSettings.Values["DynamicFolder"], CreationCollisionOption.OpenIfExists);
storage = await mainfolder.CreateFolderAsync(String.Format("{0:X6}", random.Next(0x1000000)), CreationCollisionOption.ReplaceExisting);
}
if (storage != null)
{
for (int frame = 0; frame < frameCount; frame++)
{
var bitmapFrame = await bitmapDecoder.GetFrameAsync(Convert.ToUInt32(frame));
var softImage = await bitmapFrame.GetSoftwareBitmapAsync();
byte[] array = null;
using (var ms = new InMemoryRandomAccessStream())
{
Windows.Graphics.Imaging.BitmapEncoder bitmapEncoder = await Windows.Graphics.Imaging.BitmapEncoder.CreateAsync(Windows.Graphics.Imaging.BitmapEncoder.PngEncoderId, ms);
bitmapEncoder.SetSoftwareBitmap(softImage);
try
{
await bitmapEncoder.FlushAsync();
}
catch (Exception ex) { }
array = new byte[ms.Size];
WriteableBitmap wb = new WriteableBitmap((int)bitmapDecoder.PixelWidth, (int)bitmapDecoder.PixelHeight);
using (Stream stream = wb.PixelBuffer.AsStream())
{
await stream.WriteAsync(array, 0, array.Length);
}
Guid BitmapEncoderGuid = Windows.Graphics.Imaging.BitmapEncoder.PngEncoderId;
var bmif = await storage.CreateFileAsync($"X{frame}.png", CreationCollisionOption.ReplaceExisting);
using (IRandomAccessStream stream = await bmif.OpenAsync(FileAccessMode.ReadWrite))
{
Windows.Graphics.Imaging.BitmapEncoder encoder = await Windows.Graphics.Imaging.BitmapEncoder.CreateAsync(BitmapEncoderGuid, stream);
Stream pixelStream = wb.PixelBuffer.AsStream();
byte[] pixels = new byte[pixelStream.Length];
await pixelStream.ReadAsync(pixels, 0, pixels.Length);
encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore,
(uint)wb.PixelWidth,
(uint)wb.PixelHeight,
96.0,
96.0,
pixels);
await encoder.FlushAsync();
}
}
}
}
}
}
catch (Exception)
{
this.DynamicOperation.Text = "Error Reading TIFF Container";
ProgressBar.Visibility = Visibility.Collapsed;
AddTIFButton.IsEnabled = true;
if (storage != null) { await storage.DeleteAsync(); }
}
}
所以从外观上看,我似乎能够解码 TIFF 图像(我得到了正确的帧数)但是因为我收到的是 BitmapFrame,所以我使用该函数将其转换为 SoftImage .
这次我使用 BitmapEncoder 将 SoftImage 保存为 PNG。这是我在使用正确的方法将 SoftImage 另存为 PNG 文件时遇到困难的地方!
获取SoftwareBitmap时,只需调用要另存为.png的StorageFile的OpenAsync方法即可获取随机访问流。然后使用 BitmapEncoder.CreateAsync 方法获取指定流的 BitmapEncoder class 实例,并在编码器中设置 SoftwareBitmap。之后,调用 FlushAsync 使编码器将图像数据写入指定文件。关于如何将SoftwareBitmap保存为png文件的更多细节,可以参考这个document.
private async void Button_Click(object sender, RoutedEventArgs e)
{
var random = new Random();
StorageFolder folder = KnownFolders.PicturesLibrary;
StorageFile MyOriginalfile = await folder.GetFileAsync("file_example_TIFF_1MB.tiff");
StorageFolder storage = null;
try
{
uint frameCount;
using (IRandomAccessStream randomAccessStream = await MyOriginalfile.OpenAsync(FileAccessMode.Read, StorageOpenOptions.None))
{
Windows.Graphics.Imaging.BitmapDecoder bitmapDecoder = await Windows.Graphics.Imaging.BitmapDecoder.CreateAsync(Windows.Graphics.Imaging.BitmapDecoder.TiffDecoderId, randomAccessStream);
frameCount = bitmapDecoder.FrameCount;
if (frameCount == 16)
{
StorageFolder mainfolder = await Windows.Storage.ApplicationData.Current.LocalFolder.GetFolderAsync("DynamicFolder");
storage = await mainfolder.CreateFolderAsync(String.Format("{0:X6}", random.Next(0x1000000)), CreationCollisionOption.ReplaceExisting);
}
if (storage != null)
{
for (int frame = 0; frame < frameCount; frame++)
{
var bitmapFrame = await bitmapDecoder.GetFrameAsync(Convert.ToUInt32(frame));
var softImage = await bitmapFrame.GetSoftwareBitmapAsync();
var bmif = await storage.CreateFileAsync($"X{frame}.png", CreationCollisionOption.ReplaceExisting);
SaveSoftwareBitmapToFile(softImage, bmif);
}
}
}
}
catch { }
}
private async void SaveSoftwareBitmapToFile(SoftwareBitmap softwareBitmap, StorageFile outputFile)
{
using (IRandomAccessStream stream = await outputFile.OpenAsync(FileAccessMode.ReadWrite))
{
// Create an encoder with the desired format
BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);
// Set the software bitmap
encoder.SetSoftwareBitmap(softwareBitmap);
encoder.BitmapTransform.InterpolationMode = BitmapInterpolationMode.Fant;
encoder.IsThumbnailGenerated = true;
try
{
await encoder.FlushAsync();
}
catch (Exception err)
{
const int WINCODEC_ERR_UNSUPPORTEDOPERATION = unchecked((int)0x88982F81);
switch (err.HResult)
{
case WINCODEC_ERR_UNSUPPORTEDOPERATION:
// If the encoder does not support writing a thumbnail, then try again
// but disable thumbnail generation.
encoder.IsThumbnailGenerated = false;
break;
default:
throw;
}
}
if (encoder.IsThumbnailGenerated == false)
{
await encoder.FlushAsync();
}
}
}
使用
private async Task TIFHandler( StorageFile file)
{
var random = new Random();
StorageFolder storage = null;
try
{
uint frameCount;
using (IRandomAccessStream randomAccessStream = await file.OpenAsync(FileAccessMode.Read, StorageOpenOptions.None))
{
Windows.Graphics.Imaging.BitmapDecoder bitmapDecoder = await Windows.Graphics.Imaging.BitmapDecoder.CreateAsync(Windows.Graphics.Imaging.BitmapDecoder.TiffDecoderId, randomAccessStream);
frameCount = bitmapDecoder.FrameCount;
if (frameCount == 16)
{
StorageFolder mainfolder = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFolderAsync((string)ApplicationData.Current.LocalSettings.Values["DynamicFolder"], CreationCollisionOption.OpenIfExists);
storage = await mainfolder.CreateFolderAsync(String.Format("{0:X6}", random.Next(0x1000000)), CreationCollisionOption.ReplaceExisting);
}
if (storage != null)
{
for (int frame = 0; frame < frameCount; frame++)
{
var bitmapFrame = await bitmapDecoder.GetFrameAsync(Convert.ToUInt32(frame));
var softImage = await bitmapFrame.GetSoftwareBitmapAsync();
byte[] array = null;
using (var ms = new InMemoryRandomAccessStream())
{
Windows.Graphics.Imaging.BitmapEncoder bitmapEncoder = await Windows.Graphics.Imaging.BitmapEncoder.CreateAsync(Windows.Graphics.Imaging.BitmapEncoder.PngEncoderId, ms);
bitmapEncoder.SetSoftwareBitmap(softImage);
try
{
await bitmapEncoder.FlushAsync();
}
catch (Exception ex) { }
array = new byte[ms.Size];
WriteableBitmap wb = new WriteableBitmap((int)bitmapDecoder.PixelWidth, (int)bitmapDecoder.PixelHeight);
using (Stream stream = wb.PixelBuffer.AsStream())
{
await stream.WriteAsync(array, 0, array.Length);
}
Guid BitmapEncoderGuid = Windows.Graphics.Imaging.BitmapEncoder.PngEncoderId;
var bmif = await storage.CreateFileAsync($"X{frame}.png", CreationCollisionOption.ReplaceExisting);
using (IRandomAccessStream stream = await bmif.OpenAsync(FileAccessMode.ReadWrite))
{
Windows.Graphics.Imaging.BitmapEncoder encoder = await Windows.Graphics.Imaging.BitmapEncoder.CreateAsync(BitmapEncoderGuid, stream);
Stream pixelStream = wb.PixelBuffer.AsStream();
byte[] pixels = new byte[pixelStream.Length];
await pixelStream.ReadAsync(pixels, 0, pixels.Length);
encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore,
(uint)wb.PixelWidth,
(uint)wb.PixelHeight,
96.0,
96.0,
pixels);
await encoder.FlushAsync();
}
}
}
}
}
}
catch (Exception)
{
this.DynamicOperation.Text = "Error Reading TIFF Container";
ProgressBar.Visibility = Visibility.Collapsed;
AddTIFButton.IsEnabled = true;
if (storage != null) { await storage.DeleteAsync(); }
}
}
所以从外观上看,我似乎能够解码 TIFF 图像(我得到了正确的帧数)但是因为我收到的是 BitmapFrame,所以我使用该函数将其转换为 SoftImage . 这次我使用 BitmapEncoder 将 SoftImage 保存为 PNG。这是我在使用正确的方法将 SoftImage 另存为 PNG 文件时遇到困难的地方!
获取SoftwareBitmap时,只需调用要另存为.png的StorageFile的OpenAsync方法即可获取随机访问流。然后使用 BitmapEncoder.CreateAsync 方法获取指定流的 BitmapEncoder class 实例,并在编码器中设置 SoftwareBitmap。之后,调用 FlushAsync 使编码器将图像数据写入指定文件。关于如何将SoftwareBitmap保存为png文件的更多细节,可以参考这个document.
private async void Button_Click(object sender, RoutedEventArgs e)
{
var random = new Random();
StorageFolder folder = KnownFolders.PicturesLibrary;
StorageFile MyOriginalfile = await folder.GetFileAsync("file_example_TIFF_1MB.tiff");
StorageFolder storage = null;
try
{
uint frameCount;
using (IRandomAccessStream randomAccessStream = await MyOriginalfile.OpenAsync(FileAccessMode.Read, StorageOpenOptions.None))
{
Windows.Graphics.Imaging.BitmapDecoder bitmapDecoder = await Windows.Graphics.Imaging.BitmapDecoder.CreateAsync(Windows.Graphics.Imaging.BitmapDecoder.TiffDecoderId, randomAccessStream);
frameCount = bitmapDecoder.FrameCount;
if (frameCount == 16)
{
StorageFolder mainfolder = await Windows.Storage.ApplicationData.Current.LocalFolder.GetFolderAsync("DynamicFolder");
storage = await mainfolder.CreateFolderAsync(String.Format("{0:X6}", random.Next(0x1000000)), CreationCollisionOption.ReplaceExisting);
}
if (storage != null)
{
for (int frame = 0; frame < frameCount; frame++)
{
var bitmapFrame = await bitmapDecoder.GetFrameAsync(Convert.ToUInt32(frame));
var softImage = await bitmapFrame.GetSoftwareBitmapAsync();
var bmif = await storage.CreateFileAsync($"X{frame}.png", CreationCollisionOption.ReplaceExisting);
SaveSoftwareBitmapToFile(softImage, bmif);
}
}
}
}
catch { }
}
private async void SaveSoftwareBitmapToFile(SoftwareBitmap softwareBitmap, StorageFile outputFile)
{
using (IRandomAccessStream stream = await outputFile.OpenAsync(FileAccessMode.ReadWrite))
{
// Create an encoder with the desired format
BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);
// Set the software bitmap
encoder.SetSoftwareBitmap(softwareBitmap);
encoder.BitmapTransform.InterpolationMode = BitmapInterpolationMode.Fant;
encoder.IsThumbnailGenerated = true;
try
{
await encoder.FlushAsync();
}
catch (Exception err)
{
const int WINCODEC_ERR_UNSUPPORTEDOPERATION = unchecked((int)0x88982F81);
switch (err.HResult)
{
case WINCODEC_ERR_UNSUPPORTEDOPERATION:
// If the encoder does not support writing a thumbnail, then try again
// but disable thumbnail generation.
encoder.IsThumbnailGenerated = false;
break;
default:
throw;
}
}
if (encoder.IsThumbnailGenerated == false)
{
await encoder.FlushAsync();
}
}
}