4

我发现当使用 System.Drawing.Graphics 类调整图像大小时,结果图像从右边框和下边框丢失了一个像素。这是我的代码中某处的错误或 .Net 问题?测试代码:

public static void Resize(string imagePath,int width) {
  InterpolationMode[] interpolationModes = new InterpolationMode[]{InterpolationMode.Bicubic, InterpolationMode.Bilinear, InterpolationMode.Default, InterpolationMode.High,
    InterpolationMode.HighQualityBicubic, InterpolationMode.HighQualityBilinear, InterpolationMode.Low, InterpolationMode.NearestNeighbor};
  SmoothingMode[] smoothingModes = new SmoothingMode[]{SmoothingMode.AntiAlias, SmoothingMode.Default, SmoothingMode.HighQuality, SmoothingMode.HighSpeed,
    SmoothingMode.None};

  for(int i = 0; i < interpolationModes.Length; i++) {
    for(int j = 0; j < smoothingModes.Length; j++) {
      Resize(imagePath, width, interpolationModes[i], smoothingModes[j]);
    }
  }
}


public static void Resize(string imagePath,int width, InterpolationMode interpolationMode, SmoothingMode smoothingMode) {
  Image imgPhoto = Image.FromFile(imagePath);
  float percent = (float)width / (float)imgPhoto.Width;
  int height = (int)(imgPhoto.Height * percent);

  Bitmap bmPhoto = new Bitmap(width, height, PixelFormat.Format24bppRgb);
  bmPhoto.SetResolution(imgPhoto.HorizontalResolution, imgPhoto.VerticalResolution);

  Graphics grPhoto = Graphics.FromImage(bmPhoto);
  grPhoto.InterpolationMode = interpolationMode;
  grPhoto.SmoothingMode = smoothingMode;

  grPhoto.DrawImage(imgPhoto,
  new Rectangle(0, 0, width, height),
  new Rectangle(0, 0, imgPhoto.Width, imgPhoto.Height ),
  GraphicsUnit.Pixel);

  grPhoto.Dispose();

  string fileName = Path.GetFileName(imagePath);
  string path = Path.GetDirectoryName(imagePath)+"\\resized";

  if (!Directory.Exists(path))
    Directory.CreateDirectory(path);

  bmPhoto.Save(String.Format("{0}\\{1}_{2}_{3}", path, interpolationMode.ToString(), smoothingMode.ToString(),fileName));
}

源图: 源图http://img110.imageshack.us/img110/4876/sampleaa2.jpg

结果: 结果 http://img110.imageshack.us/img110/2050/resizedsamplesy4.png

PS 我已经尝试过所有现有的 InterpolationMode 和 SmoothingMode 组合。他们都没有给出可接受的结果。

4

5 回答 5

2

这是 .NET 图形中的一个错误,也是一个非常烦人的错误。这不是舍入误差或算法问题。如果您创建一个 100x100 位图,然后使用 100x100 矩形作为参数之一调用 DrawRectangle,您会看到相同问题的变体:您将看不到绘制矩形的底部或右侧。

于 2009-01-21T19:58:30.527 回答
2

无耻地从这个问题中提出答案,我发现这可以解决它:

using (ImageAttributes wrapMode = new ImageAttributes())
{
    wrapMode.SetWrapMode(WrapMode.TileFlipXY);
    g.DrawImage(input, rect, 0, 0, input.Width, input.Height, GraphicsUnit.Pixel, wrapMode);
}
于 2011-11-29T14:35:52.037 回答
1

如果您的线条在原始线条中只有 1 像素宽,则会发生这种情况。当它调整大小时,算法会导致右侧和底部的边框出现“修剪”,而实际上,算法会根据像素色调/颜色/饱和度/位置放置一定程度的重要性。它认为内部单元格优先并且边界丢失。Adobe Photoshop 和 Corel PaintShop Pro 等各种图形应用程序为您提供各种调整大小选项 [插值模式],以帮助避免此类情况。您需要将 interpolationMode 更改为不同的东西。我有一段时间没有使用 GDI+,所以我忘记了可用的模式是什么。尝试每种模式,看看哪种模式最适合您。

我可能会这样做以保证边界的完整性:

  • 去掉原始图片的边框
  • 调整图片剩余部分的大小
  • 将边框添加回生成的图片

这样,您的边框将保持不变 - 调整大小的图像周围有一个红色像素。

于 2009-01-21T19:47:40.453 回答
0

鉴于您按比例缩放的百分比并不总是会产生积分结果,这将导致舍入误差,从而产生边界。

您将必须确定任何维度的舍入是否会添加像素边框(您可以检查舍入)。如果是这样,那么您可以创建一个新的位图,然后复制不包括边框的图片部分。

于 2009-01-21T19:46:54.870 回答
0

在我的开源图像大小调整库中,我多年来一直在努力解决这个问题。

我找到的最佳解决方案是使用 HighQualityBicubic、WrapMode.TileFlipXY,并使用 DrawImage 的平行四边形重载。结果仍然显示外部像素略有下降,但没有修剪任何东西。通过将图形对象的背景颜色设置为接近外边缘颜色的颜色,您甚至可以最大限度地减少该问题。

来源摘录:

g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
g.CompositingMode = CompositingMode.SourceOver;


s.copyAttibutes.SetWrapMode(WrapMode.TileFlipXY);

s.destGraphics.DrawImage(s.sourceBitmap, PolygonMath.getParallelogram(s.layout["image"]), s.copyRect, GraphicsUnit.Pixel, s.copyAttibutes);

如果这没有帮助,我建议禁用 SetResolution 调用,因为这是最后剩下的变量。

于 2011-05-18T18:11:10.750 回答