3

I am trying to precisely and predictably scale an image in C# to a different resolution, both up and down. When I open the resulting images with external tools such as Gimp, the results are not satisfying with my current settings.

public Image Square(Image image, int res) {
    Bitmap sq = new Bitmap(res, res, image.PixelFormat);
    Graphics canvas = Graphics.FromImage(sq);
    canvas.CompositingQuality = CompositingQuality.HighQuality;
    canvas.SmoothingMode = SmoothingMode.None;
    canvas.InterpolationMode = InterpolationMode.Bicubic;
    canvas.DrawImage(sq, 0, 0, res, res);
    return sq;
}

The results are okay when scaling down (but far from perfect), but there are side-effects when scaling up:

This picture has a resolution of 2x2 pixels. The alpha channel is set to opaque for all pixels. 2x2 opaque

After scaling it to 4x4 pixels, this is the result: 4x4 with alpha

Apparently, the C# graphics library introduced transparency while scaling the picture. This method should still work if the given image has transparent pictures, so removing the alpha channel is not an option.

Similarly, when scaling pictures down, there are problems at the edges of the resulting images as well, usually either very dark or transparent.

Is there any way to circumvent this behavior?


Edit: I already tried NearestNeighbor for downscaling only, but it results in this:

4x4 nearest neighbor


Edit 2: With WrapMode.TileFlipXY, the transparent edge is gone, but red only makes up 25% of the image instead of 50% as it should be:

4x4 tileflipxy

just.me
  • 2,155
  • 5
  • 16
  • 25

3 Answers3

2

You're asking for bicubic interpolation and you're getting it. What you want is the "nearest neighbor" option as outlined in the docs:

canvas.InterpolationMode = InterpolationMode.NearestNeighbor;
tadman
  • 208,517
  • 23
  • 234
  • 262
  • Thank you for your answer! I updated my question with the result, which is still pretty far from where I would like to be. – just.me Nov 14 '17 at 18:00
  • Somehow I think you have a 4x4 image with 3x3 worth of coloured pixels and a one pixel margin on two sides that's transparent. – tadman Nov 14 '17 at 18:11
  • Yes, that's exactly what I get when I change the mode to `NearestNeighbor`. – just.me Nov 14 '17 at 18:13
2

A way to avoid edge artifacts is to wrap the image:

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

Direct copy/paste from:

Ghost-borders ('ringing') when resizing in GDI+

j4nw
  • 2,227
  • 11
  • 26
  • Thank you for your answer. I added the result to my question, it is still not what I am trying to achieve. – just.me Nov 14 '17 at 18:15
  • Are you using bicubic with the wrap mode? It is what should solve the edge artifacts issue for bicubic scaling, which is what you asked about, but will obviously look different from nearest neighbor scaling. – j4nw Nov 14 '17 at 18:18
  • Thank you, that actually helped! I would prefer a combination of `NearestNeighbor` and `TileFlipXY` though. – just.me Nov 14 '17 at 18:20
2

I believe you need to combine NearestNeighbor interpolation with Half pixel offset. As pointed out in a similar question here.

canvas.InterpolationMode = InterpolationMode.NearestNeighbor;
canvas.PixelOffsetMode = PixelOffsetMode.Half;
Derrick Moeller
  • 4,808
  • 2
  • 22
  • 48
  • Thank you, this finally seems to works! I will probably use this for downsizing and a different interpolation mode for upscaling. Let me just make some tests before accepting your answer. – just.me Nov 14 '17 at 18:43
  • @just.me Sounds good, do you actually need more pixels though? Maybe you can just change the dpi of the image? – Derrick Moeller Nov 14 '17 at 19:03
  • Sadly, yes. We are using an algorithm which requires all images to have exactly the same resolution. – just.me Nov 14 '17 at 20:12