Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

What happens (only noticeable on certain images) is I will see a 1 pixel white border that is inset one pixel. It seems to happen in areas that are light but not white (e.g. the sky). It is similar to when something is oversharpened and a ghost border can be seen next to high contrast edges.

Here is the repro code that reproduces it perfectly. I'm using all the highest quality settings for scaling.

ImageCodecInfo encoder = null; EncoderParameters encoderParams = null;  foreach (ImageCodecInfo codec in ImageCodecInfo.GetImageEncoders()) {     if (codec.MimeType == "image/jpeg")     {         encoder = codec;          // use highest quality compression settings         encoderParams = new EncoderParameters(1);         encoderParams.Param[0] = new EncoderParameter(Encoder.Quality, 100L);         break;     } }  using (Bitmap input = (Bitmap)Bitmap.FromFile(inputPath, true)) {     // shrink by multiple of 2     Rectangle rect = new Rectangle(0, 0, input.Width/32, input.Height/32);      using (Bitmap output = new Bitmap(rect.Width, rect.Height))     {         using (Graphics g = Graphics.FromImage(output))         {             // use highest quality settings (updated per Mark Ransom's answer)             g.CompositingMode = CompositingMode.SourceCopy;             g.InterpolationMode = InterpolationMode.HighQualityBicubic;             g.PixelOffsetMode = PixelOffsetMode.HighQuality;             g.SmoothingMode = SmoothingMode.HighQuality;              g.DrawImage(input, rect);         }          output.Save(outputPath, encoder, encoderParams);     } } 

Any ideas? I'm completely baffled. I've read through a ton of questions/answers and none of them seem to affect my situation.


Edit:

This is an example before image: http://img14.imageshack.us/img14/4174/mg1647.jpg

This is an example after image: http://img64.imageshack.us/img64/3156/afterringing.jpg

It is more pronounced with the original files (before the hosting service "optimizes" them), but you can see in the sky a lighter band one-pixel in on the smaller image.

like image 325
mckamey Avatar asked Dec 11 '09 20:12

mckamey


2 Answers

I finally found an article which talks about this.

Libor Tinka casually mentions this before going on to show his extensive set of filters which way outperform GDI+ scaling:

  • http://www.codeproject.com/KB/GDI-plus/imgresizoutperfgdiplus.aspx

From his advice, it sounds like it is doing exactly what we suspected: it is pulling averaging detail from surrounding pixels beyond the edge of the image. This seems like a flaw in the algorithm to me, but that is open to debate. To solve this, there is an ImageAttributes class where you can specify that the pixels beyond are simply mirror images of the pixels within. Setting this seems to completely remove the ringing:

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

Huge thanks to both Libor Tinka for the solution, and to Mark Ransom for helping me think through this and for giving me the term "ringing" which was what made Libor Tinka's solution even show up in my searches.

like image 117
mckamey Avatar answered Sep 27 '22 21:09

mckamey


Try:

g.CompositingMode = CompositingMode.SourceCopy; 

From my answer here, corrected for syntax.

The resizing is creating partial transparency around the border. Setting SourceCopy tells it to replace that partially transparent pixel with a fully opaque one.

like image 40
Mark Ransom Avatar answered Sep 27 '22 21:09

Mark Ransom