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

C#调整大小的图像有黑色边框,c#,.net,image,scaling,C#,.net,Image,Scaling,我在.NET中遇到图像缩放问题。我使用标准图形类型调整图像大小,如本例所示: public static Image Scale(Image sourceImage, int destWidth, int destHeight) { Bitmap toReturn = new Bitmap(sourceImage, destWidth, destHeight); toReturn.SetResolution(sourceImage.HorizontalResol

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

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;
    }
但是我对调整大小的图像有一个很大的问题:它们有灰色和黑色的边框,所以制作没有边框的图像非常重要

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

样本输出:


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

//
///调整正方形图像的大小
/// 
///要调整大小的图像
///新图像的宽度和高度
///图像的缩放版本
内部静态图像大小图像(图像原始图像,整数大小)
{
图像最终图像=新位图(大小、大小);
Graphics graphic=Graphics.FromImage(最终图像);
graphic.CompositingQuality=System.Drawing.Drawing2D.CompositingQuality.HighSpeed;
graphic.SmoothingMode=System.Drawing.Drawing2D.SmoothingMode.HighSpeed;
graphic.InterpolationMode=System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
矩形矩形=新矩形(0,0,大小,大小);
图形.绘图图像(原始图像,矩形);
返回最终授权;
}

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

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

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

尝试:

graphic.CompositingMode = CompositingMode.SourceCopy;

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

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

toReturn.MakeTransparent();

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

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

不过,以下是解决方案:

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

// Draw your image here.

graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;

// Draw it again.

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

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

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

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

另请参见

这些都不适合我

但是,将格式从

System.Drawing.Imaging.PixelFormat.Format24bppRgb

问题解决了吗

using (System.Drawing.Bitmap newImage = new System.Drawing.Bitmap(newWidth, newHeight,
                // System.Drawing.Imaging.PixelFormat.Format24bppRgb // OMG bug
                    System.Drawing.Imaging.PixelFormat.Format32bppArgb 
                ))
            {

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

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

  • 默认的
    Graphics.PixelOffsetMode
    设置会导致像素值采样错误,从而导致图像轻微失真,尤其是边缘周围
  • InterpolationMode.HighQualityBicubic
    从图像边缘以外的像素进行采样,默认情况下这些像素是透明的。取样器将这些透明像素与边缘像素混合,形成半透明边缘
  • 以不支持透明度的格式(例如JPEG)保存半透明图像时,透明值将替换为黑色
  • 所有这些加起来就是半黑色(即灰色)边

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

    您使用的
    位图
    构造函数正在通过调整原始图像的大小初始化新的
    位图
    ,因此您要执行两次调整大小操作。您应该使用只包含所需维度的构造函数重载来创建空白画布

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

    如果其他设置正确,代码中使用的
    CompositingQuality.HighQuality
    设置将不会产生视觉效果,并且与默认值
    CompositingMode.SourceOver组合使用时,实际上会显著降低性能。您可以省略
    CompositingQuality
    设置并设置
    CompositingMode.SourceCopy
    ,以获得相同的结果和更好的性能

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

    解决方案 移除这些瑕疵的正确方法是使用
    PixelOffsetMode.Half
    ,并使用
    ImageAttributes
    对象指定边缘平铺,以便
    HighQualityBicubic
    采样器具有透明像素以外的采样

    您可以在此处阅读有关
    图形
    类设置及其对图像质量和性能的影响的更多信息:

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

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

    对于这些图像,发送到浏览器的HTML是什么样子的?图像的原始类型是什么?我遇到了同样的问题,发布了我的解决方案。图像不再向上,但如果在边缘上看到1px边框,可以通过将ImageAttributes实例传递到设置了TileFlipXY的DrawImage()来修复。这使得相互作用重复使用外部像素边缘,而不是对背景或透明颜色进行平均。资料来源:不幸的是,这根本不是解决办法。它有
    using (System.Drawing.Bitmap newImage = new System.Drawing.Bitmap(newWidth, newHeight,
                    // System.Drawing.Imaging.PixelFormat.Format24bppRgb // OMG bug
                        System.Drawing.Imaging.PixelFormat.Format32bppArgb 
                    ))
                {
    
    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;
    }