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 )) {
一个正确的答案可以从其他一些答案拼凑在一起,但没有一个是完整的,有些提出了一些非常糟糕的想法(比如两次绘制图像)。
您看到的工件有三个原因:
- 默认的
Graphics.PixelOffsetMode
设置会导致像素值被错误地采样,从而导致图像轻微失真,尤其是边缘周围。 -
InterpolationMode.HighQualityBicubic
从图像边缘之外采样像素,默认情况下是透明的。 这些透明像素由采样器与边缘像素混合,产生半透明边缘。 - 以不支持透明度的格式(例如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; }