Ответ 1
Try:
graphic.CompositingMode = CompositingMode.SourceCopy;
У меня проблема с масштабированием изображения в .NET. Я использую стандартный тип Graphics для изменения размеров изображений, как в этом примере:
public static Image Scale(Image sourceImage, int destWidth, int destHeight)
{
Bitmap toReturn = new Bitmap(sourceImage, destWidth, destHeight);
toReturn.SetResolution(sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
using (Graphics graphics = Graphics.FromImage(toReturn))
{
graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.DrawImage(sourceImage, 0, 0, destWidth, destHeight);
}
return toReturn;
}
Но у меня большая проблема с измененными изображениями: они имеют серые и черные границы, и чрезвычайно важно сделать изображения без них.
Почему они появляются и что я могу сделать, чтобы они исчезли?
Результат вывода:
Try:
graphic.CompositingMode = CompositingMode.SourceCopy;
Это может быть вызвано неправильной интерполяцией пикселей вокруг краев. Я бы назвал это ошибкой.
Здесь решение, хотя:
graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.PixelOffsetMode = PixelOffsetMode.Half;
graphics.InterpolationMode = InterpolationMode.NearestNeighbor;
// Draw your image here.
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
// Draw it again.
Что это значит, сначала рисуем "фон" с правильно заполненными краями, а затем снова рисуем с интерполяцией. Если вам не нужна интерполяция, то это необязательно.
Реальное решение - использовать перегрузку DrawImage
, которая позволяет передать объект ImageAttributes
.
В экземпляре ImageAttributes
вызовите следующий метод, прежде чем передать его в DrawImage
:
using (var ia = new ImageAttributes())
{
ia.SetWrapMode(WrapMode.TileFlipXY);
aGraphic.DrawImage(..., ia);
}
См. также этот ответ
Проблема заключается в том, что ваш растровый образ toReturn
по умолчанию имеет черный фон.
Копирование нового изображения поверх него делает черные или серые границы.
Решение состоит в том, чтобы удалить черный фон по умолчанию, вызвав:
toReturn.MakeTransparent();
Так как после этой строки вы будете рисовать новое изображение без какого-либо цвета фона, границы исчезнут.
Это потому, что выборка была взята из краев фотографии.
Как для вас работает следующая работа? Это код, который я использовал, чтобы сделать то же самое. Основное отличие, которое я замечаю, это то, что я не использую SetResolution (и я принимаю квадратный ввод и вывод, поскольку это было для меня).
/// <summary>
/// Resizes a square image
/// </summary>
/// <param name="OriginalImage">Image to resize</param>
/// <param name="Size">Width and height of new image</param>
/// <returns>A scaled version of the image</returns>
internal static Image ResizeImage( Image OriginalImage, int Size )
{
Image finalImage = new Bitmap( Size, Size );
Graphics graphic = Graphics.FromImage( finalImage );
graphic.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;
graphic.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighSpeed;
graphic.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
Rectangle rectangle = new Rectangle( 0, 0, Size, Size );
graphic.DrawImage( OriginalImage, rectangle );
return finalImage;
}
Это происходит из-за сглаживания (смешивания с фоном) по краям при рисовании изображения.
Возможно, вы можете сделать его дважды, один раз без него и один с включенным сглаживанием. Или вы могли бы сделать это немного больше. Или, если известен исходный цвет фона, вы можете сначала заполнить изображение цветом фона.
Ни один из них не работал у меня.
Однако изменение формата с
System.Drawing.Imaging.PixelFormat.Format24bppRgb
to
System.Drawing.Imaging.PixelFormat.Format32bppArgb
решил проблему
using (System.Drawing.Bitmap newImage = new System.Drawing.Bitmap(newWidth, newHeight,
// System.Drawing.Imaging.PixelFormat.Format24bppRgb // OMG bug
System.Drawing.Imaging.PixelFormat.Format32bppArgb
))
{
Правильный ответ может быть собран вместе с некоторыми другими ответами, но ни один из них не является полным, а некоторые представляют некоторые очень плохие идеи (например, два раза рисуют изображение).
Есть три причины для артефактов, которые вы видите:
Graphics.PixelOffsetMode
приводит к неправильной выборке пикселей, что приводит к небольшому искажению изображения, особенно по краям.InterpolationMode.HighQualityBicubic
выборки пикселей за пределами края изображения, которые по умолчанию прозрачны. Эти прозрачные пиксели смешиваются с крайними пикселями сэмплером, что приводит к полупрозрачным краям.Все это добавляет к получерным (т.е. серым) ребрам.
Правильный способ удаления этих артефактов - использовать PixelOffsetMode.Half
и использовать объект ImageAttributes
для указания кроссировки края, чтобы сэмплер HighQualityBicubic
имел что-то отличное от прозрачных пикселей для образца.
Есть еще несколько проблем с кодом, который вы опубликовали:
Используемый конструктор Bitmap
инициализирует новый Bitmap
путем изменения размера исходного изображения, поэтому вы выполняете операцию изменения размера дважды. Для создания пустого холста следует использовать перегрузку конструктора с нужными размерами.
Помните, что класс Bitmap
представляет неуправляемую копию изображения в памяти. Он должен быть удален таким образом, чтобы GDI + можно было сообщить, чтобы освободить эту память, когда вы закончите с ней. Я предполагаю, что вы делаете это в коде, который получает return Image
, но я указываю это на случай, если кто-то другой заимствует этот код.
Параметр CompositingQuality.HighQuality
, используемый в вашем коде, не будет иметь визуального эффекта, если вы правильно настроите другие настройки и значительно повредите производительность значительно в сочетании со значением по умолчанию CompositingMode.SourceOver
. Вы можете опустить параметр CompositingQuality
и установить CompositingMode.SourceCopy
, чтобы получить те же результаты с лучшей производительностью.
Параметр SmoothingMode
, используемый в вашем коде, вообще не влияет на DrawImage()
, поэтому его можно удалить.
Подробнее о параметрах класса Graphics
и их влиянии на качество и производительность изображения можно узнать здесь: http://photosauce.net/blog/post/image-scaling-with-gdi-part-3-drawimage-and-the-settings-that-affect-it
Пересмотренный код должен выглядеть примерно так:
public static Image Scale(Image sourceImage, int destWidth, int destHeight)
{
var toReturn = new Bitmap(destWidth, destHeight);
using (var graphics = Graphics.FromImage(toReturn))
using (var attributes = new ImageAttributes())
{
toReturn.SetResolution(sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
attributes.SetWrapMode(WrapMode.TileFlipXY);
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.PixelOffsetMode = PixelOffsetMode.Half;
graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.DrawImage(sourceImage, Rectangle.FromLTRB(0, 0, destWidth, destHeight), 0, 0, sourceImage.Width, sourceImage.Height, GraphicsUnit.Pixel, attributes);
}
return toReturn;
}