Изменение размера изображения обрезает форму одного пикселя справа и снизу - PullRequest
4 голосов
/ 21 января 2009

Я обнаружил, что при изменении размера изображения с помощью класса 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

P.S. Я перепробовал все существующие комбинации InterpolationMode и SmoothingMode. Никто из них не дал приемлемого результата.

Ответы [ 5 ]

2 голосов
/ 29 ноября 2011

Бесстыдно сняв ответ с на этот вопрос , я нашел это исправляет:

using (ImageAttributes wrapMode = new ImageAttributes())
{
    wrapMode.SetWrapMode(WrapMode.TileFlipXY);
    g.DrawImage(input, rect, 0, 0, input.Width, input.Height, GraphicsUnit.Pixel, wrapMode);
}
2 голосов
/ 21 января 2009

Это ошибка в графике .NET, которая чрезвычайно раздражает. Это не ошибка округления или проблема алгоритма. Вы можете увидеть вариант той же проблемы, если создадите растровое изображение 100x100 и затем вызовете DrawRectangle, используя прямоугольник 100x100 в качестве одного из параметров: вы не увидите нижнюю или правую стороны нарисованного прямоугольника.

1 голос
/ 21 января 2009

Это произойдет, если ваша ширина оригинала составляет всего 1 пиксель. Когда он изменяет размеры, алгоритм заставляет границу справа и снизу казаться «обрезанными», тогда как на самом деле алгоритм устанавливает степень важности на основе оттенка / цвета / насыщенности / расположения пикселей. Он считает, что внутренние ячейки имеют приоритет, и граница теряется. Различные графические приложения, такие как Adobe Photoshop и Corel PaintShop Pro, предоставляют различные варианты изменения размера [режимы интерполяции], чтобы избежать подобных вещей. Вам нужно изменить interpolationMode на что-то другое. Я не использовал GDI + некоторое время, поэтому я забыл, какие есть доступные режимы. Попробуйте каждый из режимов, чтобы увидеть, какой из них наиболее приемлем для вас.

Что я мог бы сделать, это гарантировать целостность границы:

  • Снятие границы с оригинальной картинки
  • Изменение размера оставшейся части изображения
  • Добавить границу обратно к полученному изображению

Таким образом, ваша граница останется неизменной - один красный пиксель, окружающий измененное изображение.

0 голосов
/ 18 мая 2011

Я много лет боролся с этой проблемой в моей библиотеке для изменения размера изображений с открытым исходным кодом .

Лучшим решением, которое я нашел, было использование 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, так как это последняя оставшаяся переменная.

0 голосов
/ 21 января 2009

Учитывая, что процент, на который вы масштабируете, не всегда даст интегральный результат, это приведет к ошибке округления, которая приведет к границе.

Вам нужно будет определить, добавит ли округление в каком-либо измерении границу пикселя (вы можете проверить округление). Если это так, то вы можете создать новое растровое изображение, а затем скопировать часть изображения, которая не содержит границы.

...