【问题标题】:C# Resized images have black bordersC# 调整大小的图像有黑色边框
【发布时间】:2010-12-24 02:17:43
【问题描述】:

我在 .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;
    }

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

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

样本输出:

【问题讨论】:

  • 为这些图像发送到浏览器的 HTML 是什么样的?
  • 图片的原始类型是什么?
  • 我遇到了同样的问题并发布了我的解决方案。
  • 图像不再向上,但如果您在边缘看到 1px 边框,您可以通过设置 TileFlipXY 将 ImageAttributes 实例传递给 DrawImage() 来解决此问题。这使得插值重新使用外部像素边缘,而不是根据背景或透明颜色对其进行平均。来源:imageresizing.net

标签: c# .net image scaling


【解决方案1】:

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

问题

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

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

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

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

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

请记住,Bitmap 类表示内存中图像的非托管副本。它需要被处理,以便 GDI+ 可以被告知在您完成它时释放该内存。我假设您在接收返回 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-that-affect-it

修改后的代码应该是这样的:

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;
}

【讨论】:

  • 我已经厌倦了如上所述但仍然出现灰色边框:( stackoverflow.com/questions/63087257/…
  • 您链接的示例没有填充整个位图/画布,这是一个不同的问题,尽管行为与使用不正确的 PixelOffsetMode 相同。我留下了关于那个问题的答案。
  • 这是目前唯一正确的答案。同时使用 WrapMode.TileFlipXY 和 PixelOffsetMode.Half。我建议只为 jpg 设置环绕模式,否则半透明/抗锯齿在具有透明度的 png 的边缘附近会丢失。
  • 真正的解决方案!
  • 我的代码在 FW 4.8 上运行良好,并在 NET 5 的一些图像顶部出现黑线。通过这个解决方案,我解决了。 Tnx
【解决方案2】:

这些都不适合我。

但是,从

更改格式
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 
                ))
            {

【讨论】:

    【解决方案3】:

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

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

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

    另见this answer

    【讨论】:

    • 这才是真正解决这个问题的方法。
    • 我已经尝试解决这个问题大约 3 个小时。这个答案是唯一有效的。我希望这是公认的解决方案。
    • 这也看设置PixelOffsetMode.Half。请参阅 saucecontrols 答案。
    【解决方案4】:

    试试:

    graphic.CompositingMode = CompositingMode.SourceCopy;
    

    【讨论】:

    • 不幸的是,这根本不是解决方案。它没有效果。灰色边框不断生成。如果它对 OP 有效,那只是偶然。解决方案在于指定此答案中指出的图像属性:stackoverflow.com/questions/2319983/…
    • @Developer Art,您的源代码是 24 位还是 32 位的 alpha?使用 alpha 调整图像大小将导致边缘出现半透明像素;通过设置此模式,这些像素将与背景混合,否则它们将与黑色混合,从而导致边框。我不知道 24 位图像会发生什么。
    • @Mark Ransom,看我的回答,你说对了一部分,但没有给出正确的解决方案。
    • 查看下面@marapet 的答案。这是唯一对我有用的。
    【解决方案5】:

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

    不过,解决方案如下:

    graphics.CompositingMode = CompositingMode.SourceCopy;
    graphics.PixelOffsetMode = PixelOffsetMode.Half;
    graphics.InterpolationMode = InterpolationMode.NearestNeighbor;
    
    // Draw your image here.
    
    graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
    
    // Draw it again.
    

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

    【讨论】:

    • @Tillito 没问题 :) 请注意,尽管这很有效,但在性能方面并不是最佳解决方案。除非您有大量图像,或者您正在制作要分发、重用等的自定义组件,否则这应该无关紧要,您可能需要一些更复杂的方法来解决这个问题,例如使用 @987654321 @ 和“手动”绘图/插值。
    【解决方案6】:

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

    解决办法是去掉默认的黑色背景,调用:

    toReturn.MakeTransparent();
    

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

    【讨论】:

      【解决方案7】:

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

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

      【讨论】:

      • @Downvoter,我真的很感谢您发表评论,说明为什么我的回答如此不恰当或糟糕......
      【解决方案8】:

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

      【讨论】:

        【解决方案9】:

        以下内容对您有何帮助?这是我用来做同样事情的代码。我注意到的主要区别是我不使用 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;
        }
        

        【讨论】:

        • 他从头开始创建了一个默认黑色背景的图像。您正在加载可能没有背景的图像。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-07-16
        • 2011-10-12
        • 2013-12-24
        相关资源
        最近更新 更多