Michael Baranov
Michael Baranov

Reputation: 809

Resizing image crops one pixel form right and bottom

I had discovered that when resizing image by using System.Drawing.Graphics class resulting image missed one pixel from right and bottom borders. This is bug somewhere in my code or .Net issue? Test code:

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

Source image: Source image http://img110.imageshack.us/img110/4876/sampleaa2.jpg

Result: Result http://img110.imageshack.us/img110/2050/resizedsamplesy4.png

P.S. I had tried all existing combinations of InterpolationMode and SmoothingMode. None of them gave acceptable result.

Upvotes: 4

Views: 2272

Answers (5)

teedyay
teedyay

Reputation: 23521

Shamelessly lifting the answer from this question, I found this fixes it:

using (ImageAttributes wrapMode = new ImageAttributes())
{
    wrapMode.SetWrapMode(WrapMode.TileFlipXY);
    g.DrawImage(input, rect, 0, 0, input.Width, input.Height, GraphicsUnit.Pixel, wrapMode);
}

Upvotes: 2

Lilith River
Lilith River

Reputation: 16468

I've struggled with this issue for ages in my open-source image resizing library.

The best solution I found was to use HighQualityBicubic, WrapMode.TileFlipXY, and use the parallelogram overload of DrawImage. The result still shows a slight degradation of the outer pixel, but doesn't trim anything off. By setting the background color of the graphics object to a color close to the outer edge color, you can even minimize that issue.

Source excerpt:

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

If this doesn't help, I'd suggest disabling the SetResolution call, since that's the last remaining variable.

Upvotes: 0

MusiGenesis
MusiGenesis

Reputation: 75296

This is a bug in .NET graphics, and a tremendously annoying one. It is not a rounding error or an algorithm problem. You can see a variant of the same problem if you create a 100x100 bitmap and then call DrawRectangle using a 100x100 rectangle as one of the parameters: you will not see the bottom or right sides of the drawn rectangle.

Upvotes: 2

BenAlabaster
BenAlabaster

Reputation: 39836

That will occur if your line is only 1 pixel wide in the original. When it resizes, the algorithm causes the border on right and bottom to appear "trimmed off", when in fact, what is occurring is the algorithm places a degree of importance based on pixel hue/color/saturation/placement. It deems that the interior cells take precedence and the border gets lost. Various graphic applications such as Adobe Photoshop and Corel PaintShop Pro give you various resize options [interpolation modes] to help avoid things like this. You need to change the interpolationMode to something different. I haven't used GDI+ in a while, so I forget what the available modes are. Try each of the modes to see which is most acceptable to you.

What I would likely do is this to guarantee the integrity of the border:

  • Strip the border off the original picture
  • Resize the remaining portion of the picture
  • Add a border back to the resulting picture

This way your border will remain unchanged - a single red pixel surrounding the resized image.

Upvotes: 1

casperOne
casperOne

Reputation: 74530

Given that the percentage that you are scaling by will not always yield an integral result, that is going to lead to a rounding error which will produce the border.

You are going to have to determine if rounding in any dimension will add a pixel border (you can check the rounding). If it does, then you can create a new bitmap and then copy the section of the picture over that does not include the border.

Upvotes: 0

Related Questions