13

我在 .NET 中遇到图像缩放问题。我使用标准的 Graphics 类型来调整图像的大小,如下例所示:

public static Image Scale(Image sourceImage, int destWidth, int destHeight)
{
        Bitmap toReturn = new Bitmap(sourceImage, destWidth, destHeight);

        toReturn.SetResolution(sourceImage.HorizontalResolution, sourceImage.VerticalResolution);

        using (Graphics graphics = Graphics.FromImage(toReturn))
        {
            graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
            graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
            graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
            graphics.DrawImage(sourceImage, 0, 0, destWidth, destHeight);
        }
        return toReturn;
    }

但是我对调整大小的图像有一个大问题:它们有灰色和黑色边框,制作没有它们的图像非常重要。

为什么它们会出现,我能做些什么让它们消失?

样本输出:

样本输出

4

9 回答 9

9

真正的解决方案是使用DrawImage允许您传递ImageAttributes对象的重载。

ImageAttributes实例上,在将其传递给之前调用以下方法DrawImage

using (var ia = new ImageAttributes())
{
    ia.SetWrapMode(WrapMode.TileFlipXY);
    aGraphic.DrawImage(..., ia);
}

另请参阅此答案

于 2013-12-02T15:40:41.697 回答
7

尝试:

graphic.CompositingMode = CompositingMode.SourceCopy;
于 2009-12-07T17:36:57.877 回答
7

问题在于您的位图toReturn默认情况下具有黑色背景。在其上复制新图像会产生黑色或灰色边框。

解决方案是通过调用删除黑色默认背景:

toReturn.MakeTransparent();

因为在这条线之后,您将在没有任何背景颜色的新图像上绘制边框将消失。

于 2011-08-07T17:16:42.420 回答
7

这可能是由于边缘周围的像素被错误地插值造成的。我称之为错误。

不过,这是解决方案:

graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.PixelOffsetMode = PixelOffsetMode.Half;
graphics.InterpolationMode = InterpolationMode.NearestNeighbor;

// Draw your image here.

graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;

// Draw it again.

这样做是首先绘制一个边缘正确填充的“背景”,然后使用插值再次绘制它。如果您不需要插值,则没有必要。

于 2013-02-15T12:07:08.763 回答
7

可以从其他一些回答中拼凑出一个正确的答案,但没有一个是完整的,有些提出了一些非常糟糕的想法(比如两次绘制图像)。

问题

您看到的工件有三个原因:

  1. 默认Graphics.PixelOffsetMode设置会导致像素值采样不正确,从而导致图像轻微失真,尤其是在边缘附近。
  2. InterpolationMode.HighQualityBicubic从图像边缘以外的像素采样,默认情况下是透明的。这些透明像素通过采样器与边缘像素混合,产生半透明边缘。
  3. 当您以不支持透明度的格式(例如 JPEG)保存半透明图像时,透明值将替换为黑色。

所有这些加起来就是半黑色(即灰色)边缘。

您发布的代码还有一些其他问题:

您使用的Bitmap构造函数是Bitmap通过调整原始图像的大小来初始化新的,因此您执行了两次调整大小操作。您应该使用仅具有所需尺寸的构造函数重载来创建空白画布。

请记住,Bitmap该类表示内存中图像的非托管副本。需要处理它,以便 GDI+ 可以在您完成后释放该内存。我假设您在接收 return 的代码中这样做Image,但我指出以防其他人借用此代码。

如果其他设置正确,代码中使用的CompositingQuality.HighQuality设置将没有视觉效果,并且与CompositingMode.SourceOver. 您可以省略CompositingQuality设置并设置CompositingMode.SourceCopy以获得相同的结果并具有更好的性能。

您的代码中使用的SmoothingMode设置对 完全没有影响DrawImage(),因此可以将其删除。

解决方案

删除这些伪影的正确方法是使用PixelOffsetMode.Half并使用一个ImageAttributes对象来指定边缘平铺,这样HighQualityBicubic采样器就可以采样透明像素以外的东西。

您可以在此处阅读有关Graphics类设置及其对图像质量和性能的影响的更多信息:http: //photosauce.net/blog/post/image-scaling-with-gdi-part-3-drawimage-and-the-settings-那影响它

修改后的代码应如下所示:

public static Image Scale(Image sourceImage, int destWidth, int destHeight)
{
    var toReturn = new Bitmap(destWidth, destHeight);

    using (var graphics = Graphics.FromImage(toReturn))
    using (var attributes = new ImageAttributes())
    {
        toReturn.SetResolution(sourceImage.HorizontalResolution, sourceImage.VerticalResolution);

        attributes.SetWrapMode(WrapMode.TileFlipXY);

        graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
        graphics.PixelOffsetMode = PixelOffsetMode.Half;
        graphics.CompositingMode = CompositingMode.SourceCopy;
        graphics.DrawImage(sourceImage, Rectangle.FromLTRB(0, 0, destWidth, destHeight), 0, 0, sourceImage.Width, sourceImage.Height, GraphicsUnit.Pixel, attributes);
    }

    return toReturn;
}
于 2017-01-18T11:49:27.557 回答
3

这是因为采样是从照片的边缘进行的。

于 2009-12-07T17:05:52.203 回答
2

以下内容对您有何帮助?这是我用来做同样事情的代码。我注意到的主要区别是我不使用 SetResolution(我假设输入和输出是方形的,因为我就是这种情况)。

/// <summary>
/// Resizes a square image
/// </summary>
/// <param name="OriginalImage">Image to resize</param>
/// <param name="Size">Width and height of new image</param>
/// <returns>A scaled version of the image</returns>
internal static Image ResizeImage( Image OriginalImage, int Size )
{
    Image finalImage = new Bitmap( Size, Size );

    Graphics graphic = Graphics.FromImage( finalImage );

    graphic.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;
    graphic.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighSpeed;
    graphic.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;

    Rectangle rectangle = new Rectangle( 0, 0, Size, Size );

    graphic.DrawImage( OriginalImage, rectangle );

    return finalImage;
}
于 2009-12-07T17:03:27.440 回答
1

这是因为绘制图像时边缘的平滑(与背景混合)。

你可以画两次,一次没有,一次启用平滑。或者你可以把它画大一点。或者,如果原始背景颜色已知,您可以先用背景颜色填充图像。

于 2009-12-07T17:22:53.063 回答
1

这些都不适合我。

但是,将格式从

System.Drawing.Imaging.PixelFormat.Format24bppRgb

System.Drawing.Imaging.PixelFormat.Format32bppArgb 

确实解决了问题

using (System.Drawing.Bitmap newImage = new System.Drawing.Bitmap(newWidth, newHeight,
                // System.Drawing.Imaging.PixelFormat.Format24bppRgb // OMG bug
                    System.Drawing.Imaging.PixelFormat.Format32bppArgb 
                ))
            {
于 2015-01-21T11:45:09.550 回答