Призрачные границы ("звон") при изменении размера в 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'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);
    }
}

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


Изменить:

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

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

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


person mckamey    schedule 11.12.2009    source источник
comment
Я прочитал ваше описание немного внимательнее, и то, что вы описываете, называется звонком. Он может появиться прямо на краю или совсем недалеко от него. Однако я не вижу в вашем коде ничего очевидного, что могло бы вызвать это. Примеры изображений до и после могут помочь.   -  person Mark Ransom    schedule 12.12.2009
comment
Да, это как если бы он выполняет алгоритм повышения резкости, который пытается смотреть на значения пикселей за краем изображения. Я пытаюсь найти хорошее место для публикации изображений до и после.   -  person mckamey    schedule 12.12.2009
comment
Этот образец изображения после не совсем показывает это так ярко, как я вижу в своем приложении, но вы все равно можете сказать, что это не так.   -  person mckamey    schedule 12.12.2009


Ответы (2)


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

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

person mckamey    schedule 11.12.2009
comment
Основная причина - чрезмерно агрессивный фильтр при изменении размера в сочетании с несоответствующей маскировкой пикселей по краям. Пиксели за пределами изображения вообще не должны влиять на результат, но, очевидно, они влияют, поскольку упаковка исправляет это. Вы все еще можете видеть звон в переходах между темным и светлым, например на линии крыши в вашем примере. - person Mark Ransom; 12.12.2009
comment
Что действительно интересно, так это то, что теперь, похоже, исправлен звон вдоль линии крыши. Это может быть побочным эффектом добавления всех других HighQuality настроек, таких как CompositingMode и PixelOffsetMode. Текущее состояние измененных изображений значительно улучшено. - person mckamey; 12.12.2009
comment
Отлично, искал и пробовал много решений, но это сработало. - person net_prog; 03.01.2012
comment
Я смотрел на эти загадочные звонкие границы в наших приложениях уже ГОДЫ. Это сработало. Спасибо! - person Nicholas Piasecki; 01.05.2014
comment
мои изображения теперь мозаичны, а не изменяются с помощью этого метода - person DavidB; 02.05.2014
comment
в моем случае это не работает. stackoverflow.com/ questions / 63087257 / - person Salman; 25.07.2020

Пытаться:

g.CompositingMode = CompositingMode.SourceCopy;

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

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

person Mark Ransom    schedule 11.12.2009
comment
Думаю, вы правы в том, что он считает его частично прозрачным. Если я добавлю g.Clear(Color.Black); перед тем, как нарисовать его, тогда белый цвет останется, но теперь самый крайний пиксель затемнен. К сожалению, установка g.CompositingMode=CompositingMode.SourceCopy; избавляет от проступания сзади (например, темной границы), но по-прежнему имеет белую вставку в 1 пиксель. - person mckamey; 11.12.2009
comment
Я не вижу нигде, где вы определяете quality, вы уверены, что используете сжатие JPEG самого высокого качества? - person Mark Ransom; 12.12.2009
comment
Да, я передаю 100 литров за качество. Я обновлю пример. Спасибо за вашу помощь в этом. - person mckamey; 12.12.2009
comment
+1 за ответ Марка. Это исправило странную белую границу размером 1 пиксель вверху и слева от масштабированного изображения. - person Ashraf Fayad; 04.02.2014