【问题标题】:How to resize Image in C# WinRT/winmd?如何在 C# WinRT/winmd 中调整图像大小?
【发布时间】:2017-02-01 18:09:54
【问题描述】:

我有一个简单的问题,但到目前为止我还没有找到答案:如何在 C# WinRT/WinMD 项目中调整 jpeg 图像的大小并将其另存为新 jpeg?

我正在开发 Windows 8 Metro 应用程序,用于从某个站点下载每日图像并将其显示在动态磁贴上。问题是图像必须小于 1024x1024 且小于 200kB,否则它不会显示在图块上: http://msdn.microsoft.com/en-us/library/windows/apps/hh465403.aspx

如果我有更大的图像,如何调整它的大小以适应动态磁贴?我正在考虑简单的调整大小,如宽度/2 和高度/2,同时保持纵横比。

这里的具体要求是代码必须作为 Windows 运行时组件运行,因此 WriteableBitmapEx 库在这里不起作用 - 它仅适用于常规 WinRT 项目。甚至还有一个WriteableBitmapEx 的分支作为winmd 项目,但还远远没有准备好。

【问题讨论】:

    标签: c# windows-8 windows-runtime


    【解决方案1】:

    如何缩放和裁剪的示例取自here

    async private void BitmapTransformTest()
    {
        // hard coded image location
        string filePath = "C:\\Users\\Public\\Pictures\\Sample Pictures\\fantasy-dragons-wallpaper.jpg";
    
        StorageFile file = await StorageFile.GetFileFromPathAsync(filePath);
        if (file == null)
            return;
    
        // create a stream from the file and decode the image
        var fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
        BitmapDecoder decoder = await BitmapDecoder.CreateAsync(fileStream);
    
    
        // create a new stream and encoder for the new image
        InMemoryRandomAccessStream ras = new InMemoryRandomAccessStream();
        BitmapEncoder enc = await BitmapEncoder.CreateForTranscodingAsync(ras, decoder);
    
        // convert the entire bitmap to a 100px by 100px bitmap
        enc.BitmapTransform.ScaledHeight = 100;
        enc.BitmapTransform.ScaledWidth = 100;
    
    
        BitmapBounds bounds = new BitmapBounds();
        bounds.Height = 50;
        bounds.Width = 50;
        bounds.X = 50;
        bounds.Y = 50;
        enc.BitmapTransform.Bounds = bounds;
    
        // write out to the stream
        try
        {
            await enc.FlushAsync();
        }
        catch (Exception ex)
        {
            string s = ex.ToString();
        }
    
        // render the stream to the screen
        BitmapImage bImg = new BitmapImage();
        bImg.SetSource(ras);
        img.Source = bImg; // image element in xaml
    
    }
    

    【讨论】:

    • 你认为当代码在鼠标移动时它会是一个非常快速的性能解决方案?你怎么看 ?它的性能是否比 WriteableBitmapEX 更好?
    • @Apoorv 我不知道
    【解决方案2】:

    更简单的代码来调整图像大小,而不是裁剪。下面的代码将图像重新调整为 80x80

    using (var sourceStream = await sourceFile.OpenAsync(FileAccessMode.Read))
    {
        BitmapDecoder decoder = await BitmapDecoder.CreateAsync(sourceStream);
        BitmapTransform transform = new BitmapTransform() { ScaledHeight = 80, ScaledWidth = 80 };
        PixelDataProvider pixelData = await decoder.GetPixelDataAsync(
            BitmapPixelFormat.Rgba8,
            BitmapAlphaMode.Straight,
            transform,
            ExifOrientationMode.RespectExifOrientation,
            ColorManagementMode.DoNotColorManage);
    
        using (var destinationStream = await destinationFile.OpenAsync(FileAccessMode.ReadWrite))
        {
            BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, destinationStream);
            encoder.SetPixelData(BitmapPixelFormat.Rgba8, BitmapAlphaMode.Premultiplied, 80, 80, 96, 96, pixelData.DetachPixelData());                        
            await encoder.FlushAsync();
        }
    }
    

    Source

    【讨论】:

    • 使用这个我得到非常的图像质量很差的瓷砖...有什么建议吗?
    • 如果您从这段代码中得到的图像质量很差,请尝试transform.InterpolationMode = BitmapInterpolationMode.Fant
    【解决方案3】:

    这是我经过大量谷歌搜索和试错编码后得到的解决方案:

    这里的目标是找出如何在 WinRT 中操作图像,特别是在 Background Tasks 中。后台任务比普通的 WinRT 项目更受限制,因为它们必须是 Windows Runtime Component 类型。面向 WinRT 的 NuGet 上 99% 的可用库仅面向默认 WinRT 项目,因此它们不能在 Windows 运行时组件项目中使用。

    起初我尝试使用well-known WriteableBitmapEx library - 将必要的代码移植到我的winmd 项目中。甚至还有branch of the WBE project targeting winmd,但还没有完成。我在将 [ReadOnlyArray]、[WriteOnlyArray] 属性添加到数组类型的方法参数以及将项目命名空间更改为不以“Windows”开头的东西之后进行编译 - winmd 项目限制。

    即使我能够在我的后台任务项目中使用此库,它也无法正常工作,因为正如我所发现的,WriteableBitmap 必须在 UI 线程中实例化,而据我在后台任务中所知,这是不可能的。

    同时我也发现了这个MSDN article about Image manipulation in WinRT。大多数示例仅在 JavaScript 部分中,因此我必须先将其转换为 C#。我还发现这很有帮助article on StackOverflow about image manipulation in WinRT

    internal static async Task LoadTileImageInternalAsync(string imagePath)
    {
        string tileName = imagePath.GetHashedTileName();
        StorageFile origFile = await ApplicationData.Current.LocalFolder.GetFileAsync(imagePath);
    
        // open file for the new tile image file
        StorageFile tileFile = await ApplicationData.Current.LocalFolder.CreateFileAsync(tileName, CreationCollisionOption.ReplaceExisting);
        using (IRandomAccessStream tileStream = await tileFile.OpenAsync(FileAccessMode.ReadWrite))
        {
            // get width and height from the original image
            IRandomAccessStreamWithContentType stream = await origFile.OpenReadAsync();
            ImageProperties properties = await origFile.Properties.GetImagePropertiesAsync();
            uint width = properties.Width;
            uint height = properties.Height;
    
            // get proper decoder for the input file - jpg/png/gif
            BitmapDecoder decoder = await GetProperDecoder(stream, imagePath);
            if (decoder == null) return; // should not happen
            // get byte array of actual decoded image
            PixelDataProvider data = await decoder.GetPixelDataAsync();
            byte[] bytes = data.DetachPixelData();
    
            // create encoder for saving the tile image
            BitmapPropertySet propertySet = new BitmapPropertySet();
            // create class representing target jpeg quality - a bit obscure, but it works
            BitmapTypedValue qualityValue = new BitmapTypedValue(TargetJpegQuality, PropertyType.Single);
            propertySet.Add("ImageQuality", qualityValue);
            // create the target jpeg decoder
            BitmapEncoder be = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, tileStream, propertySet);
            be.SetPixelData(BitmapPixelFormat.Rgba8, BitmapAlphaMode.Straight, width, height, 96.0, 96.0, bytes);
    
            // crop the image, if it's too big
            if (width > MaxImageWidth || height > MaxImageHeight)
            {
                BitmapBounds bounds = new BitmapBounds();
                if (width > MaxImageWidth)
                {
                    bounds.Width = MaxImageWidth;
                    bounds.X = (width - MaxImageWidth) / 2;
                }
                else bounds.Width = width;
                if (height > MaxImageHeight)
                {
                    bounds.Height = MaxImageHeight;
                    bounds.Y = (height - MaxImageHeight) / 2;
                }
                else bounds.Height = height;
                be.BitmapTransform.Bounds = bounds;
            }
    
            // save the target jpg to the file
            await be.FlushAsync();
        }
    }
    
    private static async Task<BitmapDecoder> GetProperDecoder(IRandomAccessStreamWithContentType stream, string imagePath)
    {
        string ext = Path.GetExtension(imagePath);
        switch (ext)
        {
            case ".jpg":
            case ".jpeg":
                return await BitmapDecoder.CreateAsync(BitmapDecoder.JpegDecoderId, stream);
            case ".png":
                return await BitmapDecoder.CreateAsync(BitmapDecoder.PngDecoderId, stream);
            case ".gif":
                return await BitmapDecoder.CreateAsync(BitmapDecoder.GifDecoderId, stream);
        }
        return null;
    }
    

    在此示例中,我们打开一个文件,将其解码为字节数组,然后将其编码回具有不同大小/格式/质量的新文件。

    结果是即使在 Windows 运行时组件类中并且没有 WriteableBitmapEx 库,图像操作也能完全正常工作。

    【讨论】:

    • 这有多快..我需要一个可以在 mousemove 事件中实现的解决方案..所以它必须裁剪我的指针在哪里..在旅途中...它适合 2K 图像吗?我正在使用 writeablebitmapex ..它很慢
    • 你有这个示例代码吗?它要求 TargetJpegQuality 的课程
    【解决方案4】:

    这是更短的版本,没有访问像素数据的开销。

    using (var sourceFileStream = await sourceFile.OpenAsync(Windows.Storage.FileAccessMode.Read))
    using (var destFileStream = await destinationFile.OpenAsync(FileAccessMode.ReadWrite))
    {
        BitmapDecoder decoder = await BitmapDecoder.CreateAsync(sourceFileStream);
        BitmapEncoder enc = await BitmapEncoder.CreateForTranscodingAsync(destFileStream, decoder);
        enc.BitmapTransform.ScaledWidth = newWidth;
        enc.BitmapTransform.ScaledHeight = newHeight;
        await enc.FlushAsync();
        await destFileStream.FlushAsync();
    }
    

    【讨论】:

      【解决方案5】:

      我刚刚花了最后一个半小时试图弄清楚这个问题,我有一个 JPG 字节数组并尝试了给出的答案......我无法让它工作,所以我提出了一个新的答案...希望这对其他人有所帮助...我正在将 JPG 转换为 250/250 像素

      private async Task<BitmapImage> ByteArrayToBitmapImage(byte[] byteArray)
          {
              BitmapImage image = new BitmapImage();
              using (InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream())
              {
                  using (DataWriter writer = new DataWriter(stream.GetOutputStreamAt(0)))
                  {
                      writer.WriteBytes((byte[])byteArray);
                      writer.StoreAsync().GetResults();
                  }
                  image.SetSource(stream);
              }
              image.DecodePixelHeight = 250;
              image.DecodePixelWidth = 250;
      
              return image;            
          }
      

      【讨论】:

        【解决方案6】:

        如果您想要高质量的图像,请添加 InterpolationMode = BitmapInterpolationMode.Fant in BitmapTransform , 这里是例子

        ` 公共静态异步任务 ResizeImage(Windows.Storage.StorageFile imgeTOBytes,int maxWidth,int maxHeight) {

                using (var sourceStream = await imgeTOBytes.OpenAsync(FileAccessMode.Read))
                {
                    BitmapDecoder decoder = await BitmapDecoder.CreateAsync(sourceStream);
        
                    double widthRatio = (double)maxWidth / decoder.OrientedPixelWidth;
                    double heightRatio = (double)maxHeight / decoder.OrientedPixelHeight;
        
                    double scaleRatio = Math.Min(widthRatio, heightRatio);
                    uint aspectHeight = (uint)Math.Floor((double)decoder.OrientedPixelHeight * scaleRatio);
                    uint aspectWidth = (uint)Math.Floor((double)decoder.OrientedPixelWidth * scaleRatio);
        
                    BitmapTransform transform = new BitmapTransform() { InterpolationMode = BitmapInterpolationMode.Fant, ScaledHeight = aspectHeight, ScaledWidth = aspectWidth };
                    PixelDataProvider pixelData = await decoder.GetPixelDataAsync(
                        BitmapPixelFormat.Rgba8,
                        BitmapAlphaMode.Premultiplied,
                        transform,
                        ExifOrientationMode.RespectExifOrientation,
                        ColorManagementMode.DoNotColorManage);
        
                    using (var destinationStream = await imgeTOBytes.OpenAsync(FileAccessMode.ReadWrite))
                    {
                        BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, destinationStream);
                        encoder.SetPixelData(BitmapPixelFormat.Rgba8, BitmapAlphaMode.Straight, aspectWidth, aspectHeight, 96, 96, pixelData.DetachPixelData());
                        await encoder.FlushAsync();
                    }
                }`
        

        【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-12-27
        • 1970-01-01
        • 1970-01-01
        • 2014-05-24
        相关资源
        最近更新 更多