Призрачные границы ( "звон" ) при изменении размера в GDI +

Что происходит (только заметно на некоторых изображениях), я увижу белую рамку с 1 пикселем, которая будет вставить один пиксель. Это происходит в областях, которые светлые, но не белые (например, небо). Это похоже на то, что что-то перечеркнуто, а граница с призраком можно увидеть рядом с высокими контрастными краями.

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

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

Любые идеи? Я полностью сбит с толку. Я прочитал массу вопросов/ответов, и ни один из них, похоже, не повлиял на мою ситуацию.


Edit

Это пример перед изображением: http://img14.imageshack.us/img14/4174/mg1647.jpg

Это пример после изображения: http://img64.imageshack.us/img64/3156/afterringing.jpg

Он более выражен с исходными файлами (до того, как служба хостинга "оптимизирует" их), но вы можете видеть в небе более светлую полосу на один пиксель на меньшем изображении.

Ответ 1

Наконец-то я нашел статью, в которой говорится об этом.

Libor Tinka случайно упоминает об этом, прежде чем продолжать демонстрировать свой обширный набор фильтров, который превосходит масштабирование GDI +:

По его совету, похоже, что он делает именно то, что мы подозревали: он вытягивает усреднение деталей из окружающих пикселей за пределы изображения. Это кажется недостатком в алгоритме для меня, но это открыто для обсуждения. Чтобы решить эту проблему, существует класс ImageAttributes, в котором вы можете указать, что пиксели за пределами являются просто зеркальными изображениями пикселей внутри. Установка этого, кажется, полностью удаляет звонок:

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

Огромное спасибо как Libor Tinka за решение, так и Mark Ransom за то, что помогли мне продумать это и дать мне термин "звон", из-за которого решение Libor Tinka появилось даже в моих поисках.

Ответ 2

Try:

g.CompositingMode = CompositingMode.SourceCopy;

Из моего ответа здесь, исправленного для синтаксиса.

Изменение размера создает частичную прозрачность вокруг границы. Установка SourceCopy говорит ему заменить этот частично прозрачный пиксель полностью непрозрачным.