Я хочу масштабировать изображение на C# с таким же уровнем качества, как в Photoshop. Есть ли для этого какая-нибудь библиотека обработки изображений C#?
Библиотека imageresizing.net предлагает самое высокое качество и высокую производительность изменения размера изображения, которое вы можете получить. Принятый ответ становится жертвой к одной из многих ловушек GDI + и вызывает артефакт границы шириной 1 пиксель вокруг каждого создаваемого изображения. Это исправлено с помощью экземпляра ImageAttributes с TileModeXY, установленным в качестве последнего параметра для вызова DrawImage.
@Computer Linguist - TileModeXY - это опечатка? Вы скопировали и вставили этот комментарий в несколько ответов, и поиск в Google по запросу "TileModeXY" показывает только ваши сообщения. Следующая ссылка на System.Drawing.Drawing2D.WrapMode показывает только 5 возможных значений: Tile, TileFlipX, TileFlipY, TileFlipXY, Clamp msdn.microsoft.com/en-us/library/…
Да, это должен быть TileFlipXY, спасибо за исправление!





На мой взгляд, когда вы рисуете изображение с помощью GDI +, оно хорошо масштабируется. Вы можете использовать это для создания масштабированного изображения.
Если вы хотите масштабировать изображение с помощью GDI +, вы можете сделать что-то вроде этого:
Bitmap original = ...
Bitmap scaled = new Bitmap(new Size(original.Width * 4, original.Height * 4));
using (Graphics graphics = Graphics.FromImage(scaled)) {
graphics.DrawImage(original, new Rectangle(0, 0, scaled.Width, scaled.Height));
}
Не уверен, изменился ли код, но мне пришлось опустить new Size в объявлении scaled: new Bitmap(original.Width * 4, original.Height * 4);
Протестированные библиотеки, такие как Imagemagick и GD, доступны для .NET.
Вы также можете прочитать о таких вещах, как бикубическая интерполяция, и написать свои собственные.
Попробуйте разные значения для Graphics.InterpolationMode. В GDI + доступно несколько типичных алгоритмов масштабирования. Если одного из них достаточно, вы можете пойти по этому пути вместо того, чтобы полагаться на внешнюю библиотеку.
Существует статья о Code Project об использовании GDI + для .NET для изменения размера фотографий с использованием, скажем, бикубической интерполяции.
Еще одна статья на эту тему была в другом блоге (кажется, сотрудник MS), но я нигде не могу найти ссылку. :( Может еще кто-нибудь найдет?
Статьи CodeProject, в которых обсуждается и публикуется исходный код масштабирование изображений:
Вы можете попробовать dotImage, один из продуктов моей компании, который включает изображения объект для передискретизации с 18 типов фильтров для различных уровней качества.
Типичное использование:
// BiCubic is one technique available in PhotoShop
ResampleCommand resampler = new ResampleCommand(newSize, ResampleMethod.BiCubic);
AtalaImage newImage = resampler.Apply(oldImage).Image;
кроме того, dotImage включает 140 некоторых команд обработки нечетных изображений, включая множество фильтров, подобных тем, что есть в PhotoShop, если это то, что вы ищете.
SDK с этой функцией теперь доступен бесплатно для распространенных форматов фотографий (JPEG, PNG и т. д.) atalasoft.com/photofree
/ Лу Франко: можно ли использовать бесплатную версию общего формата в производственных развертываниях, причем бесплатно?
Да, DotImage Photo Free можно развернуть бесплатно.
Вот хорошо прокомментированный вспомогательный класс для обработки изображений, который вы можете посмотреть и использовать. Я написал это как пример того, как выполнять определенные задачи по работе с изображениями на C#. Вас заинтересует функция ResizeImage, которая принимает в качестве аргументов System.Drawing.Image, ширину и высоту.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
namespace DoctaJonez.Drawing.Imaging
{
/// <summary>
/// Provides various image untilities, such as high quality resizing and the ability to save a JPEG.
/// </summary>
public static class ImageUtilities
{
/// <summary>
/// A quick lookup for getting image encoders
/// </summary>
private static Dictionary<string, ImageCodecInfo> encoders = null;
/// <summary>
/// A lock to prevent concurrency issues loading the encoders.
/// </summary>
private static object encodersLock = new object();
/// <summary>
/// A quick lookup for getting image encoders
/// </summary>
public static Dictionary<string, ImageCodecInfo> Encoders
{
//get accessor that creates the dictionary on demand
get
{
//if the quick lookup isn't initialised, initialise it
if (encoders == null)
{
//protect against concurrency issues
lock (encodersLock)
{
//check again, we might not have been the first person to acquire the lock (see the double checked lock pattern)
if (encoders == null)
{
encoders = new Dictionary<string, ImageCodecInfo>();
//get all the codecs
foreach (ImageCodecInfo codec in ImageCodecInfo.GetImageEncoders())
{
//add each codec to the quick lookup
encoders.Add(codec.MimeType.ToLower(), codec);
}
}
}
}
//return the lookup
return encoders;
}
}
/// <summary>
/// Resize the image to the specified width and height.
/// </summary>
/// <param name = "image">The image to resize.</param>
/// <param name = "width">The width to resize to.</param>
/// <param name = "height">The height to resize to.</param>
/// <returns>The resized image.</returns>
public static System.Drawing.Bitmap ResizeImage(System.Drawing.Image image, int width, int height)
{
//a holder for the result
Bitmap result = new Bitmap(width, height);
//set the resolutions the same to avoid cropping due to resolution differences
result.SetResolution(image.HorizontalResolution, image.VerticalResolution);
//use a graphics object to draw the resized image into the bitmap
using (Graphics graphics = Graphics.FromImage(result))
{
//set the resize quality modes to high quality
graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
//draw the image into the target bitmap
graphics.DrawImage(image, 0, 0, result.Width, result.Height);
}
//return the resulting bitmap
return result;
}
/// <summary>
/// Saves an image as a jpeg image, with the given quality
/// </summary>
/// <param name = "path">Path to which the image would be saved.</param>
/// <param name = "quality">An integer from 0 to 100, with 100 being the
/// highest quality</param>
/// <exception cref = "ArgumentOutOfRangeException">
/// An invalid value was entered for image quality.
/// </exception>
public static void SaveJpeg(string path, Image image, int quality)
{
//ensure the quality is within the correct range
if ((quality < 0) || (quality > 100))
{
//create the error message
string error = string.Format("Jpeg image quality must be between 0 and 100, with 100 being the highest quality. A value of {0} was specified.", quality);
//throw a helpful exception
throw new ArgumentOutOfRangeException(error);
}
//create an encoder parameter for the image quality
EncoderParameter qualityParam = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, quality);
//get the jpeg codec
ImageCodecInfo jpegCodec = GetEncoderInfo("image/jpeg");
//create a collection of all parameters that we will pass to the encoder
EncoderParameters encoderParams = new EncoderParameters(1);
//set the quality parameter for the codec
encoderParams.Param[0] = qualityParam;
//save the image using the codec and the parameters
image.Save(path, jpegCodec, encoderParams);
}
/// <summary>
/// Returns the image codec with the given mime type
/// </summary>
public static ImageCodecInfo GetEncoderInfo(string mimeType)
{
//do a case insensitive search for the mime type
string lookupKey = mimeType.ToLower();
//the codec to return, default to null
ImageCodecInfo foundCodec = null;
//if we have the encoder, get it to return
if (Encoders.ContainsKey(lookupKey))
{
//pull the codec from the lookup
foundCodec = Encoders[lookupKey];
}
return foundCodec;
}
}
}
Несколько человек просили в комментариях образцы того, как использовать класс ImageUtilities, так что готово.
//resize the image to the specified height and width
using (var resized = ImageUtilities.ResizeImage(image, 50, 100))
{
//save the resized image as a jpeg with a quality of 90
ImageUtilities.SaveJpeg(@"C:\myimage.jpeg", resized, 90);
}
Помните, что изображения одноразовые, поэтому вам нужно назначить результат изменения размера объявлению using (или вы можете использовать try finally и убедиться, что вы вызываете dispose в своем finally).
ImageCodecInfo jpegCodec = getEncoderInfo ("изображение / JPEG"); - где вы определили getEncoderInfo, потому что я не могу его скомпилировать
Он должен читать GetEncoderInfo, а не getEncoderInfo. Я исправил опечатку, и теперь класс компилируется.
Как использовать этот код для отображения эскиза на лету (без сохранения эскиза в файл)?
Используйте функцию ResizeImage, она возвращает изображение с измененным размером как объект System.Drawing.Bitmap, который вы можете использовать.
+1 работает блестяще! Одна проблема, которую вам нужно исправить в этом коде, - преобразовать переменную качества в long до передачи ее в параметр кодировщика, иначе вы получите исключение времени выполнения недопустимого параметра.
Добавьте result.SetResolution(image.HorizontalResolution, image.VerticalResolution);, чтобы убедиться, что вы не столкнетесь со странными различиями между разрешением исходного изображения и разрешением растрового изображения по умолчанию.
Свойство Encoders не является потокобезопасным, но его довольно легко сделать так, используя блокировку с двойной проверкой (en.wikipedia.org/wiki/Double-checked_locking).
@DoctorJones Когда я изменяю размер изображения с размером 300 * 308 и размером 16 КБ на изображение с размером 150 * 120, изображение с измененным размером, которое меньше исходного, имеет больший размер, его размер составляет 30 КБ! Я изменил три свойства метода с измененным размером на самое низкое, но без разницы
@Behzad вы сохраняете изображение в формате jpeg? В таком случае вам необходимо установить параметр качества. Я предполагаю, что ваше исходное изображение было закодировано с более низким качеством, чем то, что вы пытаетесь сохранить сейчас.
@DoctorJones, поэтому, если исходное изображение закодировано с более низким качеством, что мне делать, чтобы изменить его размер с таким качеством? Спасибо :)
@Behzad, если вы посмотрите, функция SaveJpeg принимает параметр типа int, называемый качеством. Вам нужно вызвать это и указать правильное значение для параметра качества (он принимает значение от 0 до 100).
@DoctorJones, большое спасибо. Если файл не является файлом Jpeg, я должен сохранить его с помощью метода Image.save по умолчанию без параметра формата?
Было бы здорово, если бы также был опубликован полный и окончательный код, чтобы помочь другим, поскольку я тоже борюсь с аналогичной проблемой.
@KnowledgeSeeker Я добавил несколько примеров использования, которые, надеюсь, помогут
Это решение создает границы-призраки. См. здесь для решения.
Рекомендую включить одно из этих изменений параллелизма в этот ответ, чтобы предотвратить проблемы параллелизма, с которыми я столкнулся: stackoverflow.com/questions/35068294/…
Привет, Боб, спасибо за комментарий. Я добавил защиту параллелизма по вашему запросу. Я использовал шаблон блокировки с двойной проверкой вместо Lazy, чтобы обеспечить совместимость с более ранними версиями платформы.
а что с graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; он не используется полный?
После долгого поиска эта часть определения размера ответа (не использовал весь код) работала для изменения размера qrcode без потери качества. Правильные настройки важны для качества результата.
Я заметил, что эта статья упоминается в коде Paint.NET для передискретизации изображения: Различные простые методы обработки изображений Пола Бурка.
1: Хорошая статья. Не удалось получить доступ к ссылке, но нашел эту другую: local.wasp.uwa.edu.au/~pbourke/texture_colour/imageprocess
Я исправил ссылку в исходном посте, так как ссылка Томаса тоже не работает ... paulbourke.net/texture_colour/imageprocess
Этот ответ был бы лучше, если бы он объяснял соответствующие части ответа, а не полагался бы на ссылку.
Вы можете попробовать это, если это lowres cgi Фильтр 2D-изображений
Это может помочь
public Image ResizeImage(Image source, RectangleF destinationBounds)
{
RectangleF sourceBounds = new RectangleF(0.0f,0.0f,(float)source.Width, (float)source.Height);
RectangleF scaleBounds = new RectangleF();
Image destinationImage = new Bitmap((int)destinationBounds.Width, (int)destinationBounds.Height);
Graphics graph = Graphics.FromImage(destinationImage);
graph.InterpolationMode =
System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
// Fill with background color
graph.FillRectangle(new SolidBrush(System.Drawing.Color.White), destinationBounds);
float resizeRatio, sourceRatio;
float scaleWidth, scaleHeight;
sourceRatio = (float)source.Width / (float)source.Height;
if (sourceRatio >= 1.0f)
{
//landscape
resizeRatio = destinationBounds.Width / sourceBounds.Width;
scaleWidth = destinationBounds.Width;
scaleHeight = sourceBounds.Height * resizeRatio;
float trimValue = destinationBounds.Height - scaleHeight;
graph.DrawImage(source, 0, (trimValue / 2), destinationBounds.Width, scaleHeight);
}
else
{
//portrait
resizeRatio = destinationBounds.Height/sourceBounds.Height;
scaleWidth = sourceBounds.Width * resizeRatio;
scaleHeight = destinationBounds.Height;
float trimValue = destinationBounds.Width - scaleWidth;
graph.DrawImage(source, (trimValue / 2), 0, scaleWidth, destinationBounds.Height);
}
return destinationImage;
}
Обратите внимание на InterpolationMode.HighQualityBicubic -> это, как правило, хороший компромисс между производительностью и результатами.
Используйте эту библиотеку: http://imageresizing.net
Прочтите статью автора библиотеки: 20 ловушек при изменении размера изображения с .NET
Вы можете попробовать волшебное ядро. Он создает меньше артефактов пикселизации, чем бикубическая передискретизация при апскейлинге, а также дает очень хорошие результаты при даунскейлинге. Исходный код доступен на C# с веб-сайта.
Попробуйте этот базовый фрагмент кода:
private static Bitmap ResizeBitmap(Bitmap srcbmp, int width, int height )
{
Bitmap newimage = new Bitmap(width, height);
using (Graphics g = Graphics.FromImage(newimage))
g.DrawImage(srcbmp, 0, 0, width, height);
return newimage;
}
У меня есть некоторые улучшения для ответа доктора Джонса.
Это работает для тех, кто хотел, как пропорционально изменить размер изображения. Он протестировал и работал у меня.
Методы класса я добавил:
public static System.Drawing.Bitmap ResizeImage(System.Drawing.Image image, Size size)
{
return ResizeImage(image, size.Width, size.Height);
}
public static Size GetProportionedSize(Image image, int maxWidth, int maxHeight, bool withProportion)
{
if (withProportion)
{
double sourceWidth = image.Width;
double sourceHeight = image.Height;
if (sourceWidth < maxWidth && sourceHeight < maxHeight)
{
maxWidth = (int)sourceWidth;
maxHeight = (int)sourceHeight;
}
else
{
double aspect = sourceHeight / sourceWidth;
if (sourceWidth < sourceHeight)
{
maxWidth = Convert.ToInt32(Math.Round((maxHeight / aspect), 0));
}
else
{
maxHeight = Convert.ToInt32(Math.Round((maxWidth * aspect), 0));
}
}
}
return new Size(maxWidth, maxHeight);
}
и новые доступны с использованием в соответствии с этими кодами:
using (var resized = ImageUtilities.ResizeImage(image, ImageUtilities.GetProportionedSize(image, 50, 100)))
{
ImageUtilities.SaveJpeg(@"C:\myimage.jpeg", resized, 90);
}
Это на C#, другой вопрос - на C++, так что это вообще не дубликат.