C#已调整大小的图像具有黑色边框

Iev*_*ida 12 .net c# scaling image

我在.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;
    }
Run Code Online (Sandbox Code Playgroud)

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

它们为什么出现以及我能做些什么使它们消失?

样本输出:

样本输出

Mar*_*som 7

尝试:

graphic.CompositingMode = CompositingMode.SourceCopy;
Run Code Online (Sandbox Code Playgroud)

  • 不幸的是,这根本不是解决方案.它没有效果.灰色边框不断生成.如果它适用于OP那么仅仅是偶然的.解决方案在于指定图像属性,如本答案所述:http://stackoverflow.com/questions/2319983/resizing-an-image-in-asp-net-without-losing-the-image-quality/2324414# 2324414 (4认同)
  • @Developer Art,你的源码是24位还是32位alpha?使用alpha调整图像大小将导致沿边缘的半透明像素; 通过设置此模式,这些像素将与背景混合,否则它们将与黑色混合,从而导致边框.我不知道24位图像会发生什么. (4认同)

Cam*_*tin 7

这可能是由于边缘周围的像素被错误插值引起的.我称这是一个错误.

不过这是解决方案:

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

// Draw your image here.

graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;

// Draw it again.
Run Code Online (Sandbox Code Playgroud)

这样做首先绘制正确填充边缘的"背景",然后再用插值绘制它.如果您不需要插值,那么这不是必需的.


sau*_*rol 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;
}
Run Code Online (Sandbox Code Playgroud)

  • 这是目前唯一正确的答案。同时使用 WrapMode.TileFlipXY 和 PixelOffsetMode.Half。我建议仅设置 jpg 的换行模式,否则半透明/抗锯齿会在透明 png 的边缘附近丢失。 (2认同)

mar*_*pet 6

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

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

using (var ia = new ImageAttributes())
{
    ia.SetWrapMode(WrapMode.TileFlipXY);
    aGraphic.DrawImage(..., ia);
}
Run Code Online (Sandbox Code Playgroud)

另见这个答案


Jer*_*oen 5

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

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

toReturn.MakeTransparent();
Run Code Online (Sandbox Code Playgroud)

因为在这一行之后你将绘制一个没有任何背景颜色的新图像,边框将消失.