Библиотека масштабирования изображений высокого качества

Я хочу масштабировать изображение на C# с таким же уровнем качества, как в Photoshop. Есть ли для этого какая-нибудь библиотека обработки изображений C#?

Это на C#, другой вопрос - на C++, так что это вообще не дубликат.

Doctor Jones 09.12.2008 19:14

Библиотека imageresizing.net предлагает самое высокое качество и высокую производительность изменения размера изображения, которое вы можете получить. Принятый ответ становится жертвой к одной из многих ловушек GDI + и вызывает артефакт границы шириной 1 пиксель вокруг каждого создаваемого изображения. Это исправлено с помощью экземпляра ImageAttributes с TileModeXY, установленным в качестве последнего параметра для вызова DrawImage.

Lilith River 10.01.2012 01:32

@Computer Linguist - TileModeXY - это опечатка? Вы скопировали и вставили этот комментарий в несколько ответов, и поиск в Google по запросу "TileModeXY" показывает только ваши сообщения. Следующая ссылка на System.Drawing.Drawing2D.WrapMode показывает только 5 возможных значений: Tile, TileFlipX, TileFlipY, TileFlipXY, Clamp msdn.microsoft.com/en-us/library/…

JasDev 27.10.2012 20:58

Да, это должен быть TileFlipXY, спасибо за исправление!

Lilith River 28.10.2012 21:07
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
142
4
136 890
14
Перейти к ответу Данный вопрос помечен как решенный

Ответы 14

На мой взгляд, когда вы рисуете изображение с помощью 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);

Kirk Woll 30.10.2011 03:20

Протестированные библиотеки, такие как Imagemagick и GD, доступны для .NET.

Вы также можете прочитать о таких вещах, как бикубическая интерполяция, и написать свои собственные.

Попробуйте разные значения для Graphics.InterpolationMode. В GDI + доступно несколько типичных алгоритмов масштабирования. Если одного из них достаточно, вы можете пойти по этому пути вместо того, чтобы полагаться на внешнюю библиотеку.

Существует статья о Code Project об использовании GDI + для .NET для изменения размера фотографий с использованием, скажем, бикубической интерполяции.

Еще одна статья на эту тему была в другом блоге (кажется, сотрудник MS), но я нигде не могу найти ссылку. :( Может еще кто-нибудь найдет?

Вы можете попробовать 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

Lou Franco 13.09.2010 19:40

/ Лу Франко: можно ли использовать бесплатную версию общего формата в производственных развертываниях, причем бесплатно?

Oskar Austegard 09.06.2011 19:30

Да, DotImage Photo Free можно развернуть бесплатно.

Lou Franco 28.12.2011 18:45
Ответ принят как подходящий

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

ilija veselica 18.05.2010 18:41

Он должен читать GetEncoderInfo, а не getEncoderInfo. Я исправил опечатку, и теперь класс компилируется.

Doctor Jones 18.05.2010 19:30

Как использовать этот код для отображения эскиза на лету (без сохранения эскиза в файл)?

ilija veselica 27.05.2010 14:38

Используйте функцию ResizeImage, она возвращает изображение с измененным размером как объект System.Drawing.Bitmap, который вы можете использовать.

Doctor Jones 27.05.2010 23:31

+1 работает блестяще! Одна проблема, которую вам нужно исправить в этом коде, - преобразовать переменную качества в long до передачи ее в параметр кодировщика, иначе вы получите исключение времени выполнения недопустимого параметра.

James 04.08.2010 22:00

Добавьте result.SetResolution(image.HorizontalResolution, image.VerticalResolution);, чтобы убедиться, что вы не столкнетесь со странными различиями между разрешением исходного изображения и разрешением растрового изображения по умолчанию.

Jaap 23.07.2012 00:42

Свойство Encoders не является потокобезопасным, но его довольно легко сделать так, используя блокировку с двойной проверкой (en.wikipedia.org/wiki/Double-checked_locking).

dana 15.01.2013 00:15

@DoctorJones Когда я изменяю размер изображения с размером 300 * 308 и размером 16 КБ на изображение с размером 150 * 120, изображение с измененным размером, которое меньше исходного, имеет больший размер, его размер составляет 30 КБ! Я изменил три свойства метода с измененным размером на самое низкое, но без разницы

Behzad 03.04.2013 20:13

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

Doctor Jones 04.04.2013 13:09

@DoctorJones, поэтому, если исходное изображение закодировано с более низким качеством, что мне делать, чтобы изменить его размер с таким качеством? Спасибо :)

Behzad 05.04.2013 11:04

@Behzad, если вы посмотрите, функция SaveJpeg принимает параметр типа int, называемый качеством. Вам нужно вызвать это и указать правильное значение для параметра качества (он принимает значение от 0 до 100).

Doctor Jones 05.04.2013 21:05

@DoctorJones, большое спасибо. Если файл не является файлом Jpeg, я должен сохранить его с помощью метода Image.save по умолчанию без параметра формата?

Behzad 06.04.2013 12:56

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

Learning 11.09.2013 11:42

@KnowledgeSeeker Я добавил несколько примеров использования, которые, надеюсь, помогут

Doctor Jones 11.09.2013 14:36

Это решение создает границы-призраки. См. здесь для решения.

mpen 13.06.2014 08:59

Рекомендую включить одно из этих изменений параллелизма в этот ответ, чтобы предотвратить проблемы параллелизма, с которыми я столкнулся: stackoverflow.com/questions/35068294/…

Jane Panda 28.01.2016 22:39

Привет, Боб, спасибо за комментарий. Я добавил защиту параллелизма по вашему запросу. Я использовал шаблон блокировки с двойной проверкой вместо Lazy, чтобы обеспечить совместимость с более ранними версиями платформы.

Doctor Jones 29.01.2016 12:41

а что с graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; он не используется полный?

Shaiju T 10.01.2017 12:10

После долгого поиска эта часть определения размера ответа (не использовал весь код) работала для изменения размера qrcode без потери качества. Правильные настройки важны для качества результата.

Furkan Ekinci 14.08.2017 15:46

Я заметил, что эта статья упоминается в коде Paint.NET для передискретизации изображения: Различные простые методы обработки изображений Пола Бурка.

1: Хорошая статья. Не удалось получить доступ к ссылке, но нашел эту другую: local.wasp.uwa.edu.au/~pbourke/texture_colour/imageprocess

Thomas Bratt 26.06.2012 16:42

Я исправил ссылку в исходном посте, так как ссылка Томаса тоже не работает ... paulbourke.net/texture_colour/imageprocess

Oskar Austegard 02.11.2012 22:21

Этот ответ был бы лучше, если бы он объяснял соответствующие части ответа, а не полагался бы на ссылку.

KatieK 02.11.2012 22:24

Вы можете попробовать это, если это 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);
}

Другие вопросы по теме