使用ImageResizer调整大小时,System.Drawing.Image失去透明度

4
对于我当前的WPF应用程序,我必须缩小一些System.Drawing.Image(从PNG文件中加载的对象,其中一些具有透明背景)。我尝试了多种方法来调整图像大小,所有这些方法在拥有较小图像方面都表现良好。但不幸的是,它们都使图像失去了透明度。
我的最后一次尝试是使用ImageResizer外部库来完成工作,因为我希望它可以轻松处理这个问题,但我仍然遇到了同样的问题:原始图像显示为透明背景;调整大小后的图像显示为黑色背景。
以下是我使用ImageResizer库的代码:
ImageResizer.Instructions inst = new ImageResizer.Instructions("width=" + newWidth.ToString() + ";height=" + newHeight.ToString() + ";format=png;mode=max");
ImageResizer.ImageJob job = new ImageResizer.ImageJob(originalImage, typeof(System.Drawing.Bitmap), inst);
job.Build();
return job.Result as System.Drawing.Image;

这是我另外两种方法,基本上也能产生相同的结果(图片调整大小:是;透明度保留:否):
return originalImage.GetThumbnailImage(newWidth, newHeight, null, IntPtr.Zero); // Transparency gets lost

return new System.Drawing.Bitmap(originalImage, new System.Drawing.Size(newWidth, newHeight));  // Transparency gets lost

任何关于如何在调整大小时保留透明度的想法吗?
问候,
Ralf
2个回答

2

ImageResizer始终保留透明度。

在图像的编码(或显示)期间,您失去了透明度,这发生在您从ImageResizer中取回控制权之后。而不是传递typeof(System.Drawing.Bitmap),请传递输出路径或输出流。

var i = new Instructions(){ Width = newWidth,Height = newHeight, OutputFormat= OutputFormat.Png, Mode= FitMode.Max};
new ImageJob(originalImage, "output.png", i).Build();

如果您从ImageResizer中获取原始位图,则无法控制如何对图像进行编码。


您没有具体说明您如何使用结果,这非常重要。如果您不将它们写入磁盘或流中,而是显示它们,则您正在错误的地方寻找问题。很可能负责将结果合成到显示表面的代码未能将图像视为32位图像,并且忽略了Alpha通道。


我想你是对的。我删除了我的答案,它可能无法解决Ralf的问题。而且我看到你是ImageResizer的作者,所以你可能知道自己在说什么 :) - Bedford
我必须承认你是对的,透明度很可能在使用ImageResizer之前或之后的某个地方丢失了。因此,我尝试使用MemoryStream作为sourcedest参数,以避免在最终显示图像之前使用System.Drawing.Bitmap。不幸的是,这对我没有用,因为Result保持为空(Null)。我一定是错误地使用了ImageResizer。 然而,我决定尝试dbc的建议并使用BitmapImage类。这解决了我的问题,不再需要ImageResizer。 - Ralf

1

即使您正在使用WPF,您也正在使用System.Drawing.Image对象,因此您可以执行以下操作:

    public static Bitmap ResizeImage(Image imgToResize, int newHeight)
    {
        int sourceWidth = imgToResize.Width;
        int sourceHeight = imgToResize.Height;

        float nPercentH = ((float)newHeight / (float)sourceHeight);

        int destWidth = Math.Max((int)Math.Round(sourceWidth * nPercentH), 1); // Just in case;
        int destHeight = newHeight;

        Bitmap b = new Bitmap(destWidth, destHeight);
        using (Graphics g = Graphics.FromImage((Image)b))
        {
            g.SmoothingMode = SmoothingMode.HighQuality;
            g.InterpolationMode = InterpolationMode.HighQualityBicubic;
            g.PixelOffsetMode = PixelOffsetMode.HighQuality;
            g.DrawImage(imgToResize, 0, 0, destWidth, destHeight);
        }

        return b;
    }

接下来,一定要使用PNG编码器保存它:

    public static System.Drawing.Imaging.ImageCodecInfo GetEncoder(System.Drawing.Imaging.ImageFormat format)
    {
        ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
        foreach (ImageCodecInfo codec in codecs)
        {
            if (codec.FormatID == format.Guid)
            {
                return codec;
            }
        }
        return null;
    }

然后

    codec = GetEncoder(ImageFormat.Png);
    newBitmap.Save(newFile, codec, null);

(Note我使用的是标准的.Net类库,而不是第三方库;希望这没有问题。) 更新 顺便说一下,既然你正在使用WPF,为什么不使用WPF的图像处理呢?
public static class BitmapHelper
{
    public static void SaveToPng(this BitmapSource bitmap, string fileName)
    {
        var encoder = new PngBitmapEncoder();
        SaveUsingEncoder(bitmap, fileName, encoder);
    }

    public static void SaveUsingEncoder(this BitmapSource bitmap, string fileName, BitmapEncoder encoder)
    {
        BitmapFrame frame = BitmapFrame.Create(bitmap);
        encoder.Frames.Add(frame);

        using (var stream = File.Create(fileName))
        {
            encoder.Save(stream);
        }
    }

    public static void ImageLoadResizeAndSave(string inFile, string outFile, int newPixelHeight)
    {
        BitmapImage image = new BitmapImage();
        image.BeginInit();
        image.UriSource = new Uri(inFile);
        image.EndInit();

        var newImage = BitmapHelper.ResizeImageToHeight(image, newPixelHeight);

        BitmapHelper.SaveToPng(newImage, outFile);
    }

    /// <summary>
    /// Resize the image to have the selected height, keeping the width proportionate.
    /// </summary>
    /// <param name="imgToResize"></param>
    /// <param name="newHeight"></param>
    /// <returns></returns>
    public static BitmapSource ResizeImageToHeight(BitmapSource imgToResize, int newPixelHeight)
    {
        double sourceWidth = imgToResize.PixelWidth;
        double sourceHeight = imgToResize.PixelHeight;

        var nPercentH = ((double)newPixelHeight / sourceHeight);

        double destWidth = Math.Max((int)Math.Round(sourceWidth * nPercentH), 1); // Just in case;
        double destHeight = newPixelHeight;

        var bitmap = new TransformedBitmap(imgToResize, new ScaleTransform(destWidth / imgToResize.PixelWidth, destHeight / imgToResize.PixelHeight));

        return bitmap;
    }
}

也许您在将旧格式的图像转换为WPF格式时失去了透明度?

虽然问题是关于System.Drawing.Image的使用,但我认为这仍然是正确的答案:只需使用BitmapImage类即可解决我在失去透明度方面遇到的问题,因为它似乎更适合WPF。 此外,我注意到BitmapImage有两个有用的属性,称为DecodePixelWidthDecodePixelHeight,这使得调整大小对于我的情况来说更加容易。 - Ralf

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