C#已resize的图像具有黑色边框

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

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

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

样本输出:

样本输出

尝试:

 graphic.CompositingMode = CompositingMode.SourceCopy; 

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

不过这是解决方案:

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

另见这个答案

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

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

 toReturn.MakeTransparent(); 

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

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

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

 ///  /// Resizes a square image ///  /// Image to resize /// Width and height of new image /// A scaled version of the image 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; } 

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

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

这些都不适合我。

但是,更改格式

 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 )) { 

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

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

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

这一切都加起来是半黑色(即灰色)边缘。

删除这些工件的正确方法是使用PixelOffsetMode.Half并使用ImageAttributes对象指定边缘平铺,以便HighQualityBicubic采样器具有除透明像素之外的其他内容。

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

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

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

如果您正确地使用其他设置,则代码中使用的CompositingQuality.HighQuality设置将没有视觉效果,并且与CompositingMode.SourceOver的默认值相结合实际上会显着损害性能。 您可以省略CompositingQuality设置并设置CompositingMode.SourceCopy以获得具有更好性能的相同结果。

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

您可以在此处阅读有关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; }