Biblioteca de escalado de imágenes de alta calidad [cerrado]

141

Quiero escalar una imagen en C # con un nivel de calidad tan bueno como Photoshop. ¿Hay alguna biblioteca de procesamiento de imágenes C # disponible para hacer esto?

Ramesh Soni
fuente
47
Esto está en C #, esa otra pregunta es C ++, por lo que no es un duplicado en absoluto.
Doctor Jones
77
La biblioteca imageresizing.net ofrece el cambio de tamaño de imagen de la más alta calidad y el más alto rendimiento que puede obtener. La respuesta aceptada es víctima de uno de los muchos escollos de GDI + y provocará un artefacto de borde de 1px de ancho alrededor de cada imagen que genere. Eso se soluciona mediante el uso de una instancia de ImageAttributes con TileModeXY establecido para el último parámetro de la llamada DrawImage.
Lilith River el
2
@Computer Linguist: ¿TileModeXY es un error tipográfico? Copiaste este comentario en varias respuestas y una búsqueda en Google de exactamente "TileModeXY" solo muestra tus publicaciones. El siguiente enlace para System.Drawing.Drawing2D.WrapMode solo muestra 5 valores posibles: Tile, TileFlipX, TileFlipY, TileFlipXY, Clamp msdn.microsoft.com/en-us/library/…
JasDev
1
Sí, debería ser TileFlipXY, ¡gracias por la corrección!
Lilith River

Respuestas:

233

Aquí hay una clase auxiliar de manipulación de imagen muy bien comentada que puede ver y usar. Lo escribí como un ejemplo de cómo realizar ciertas tareas de manipulación de imágenes en C #. Te interesará la función ResizeImage que toma System.Drawing.Image, el ancho y el alto como argumentos.

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

Actualizar

Algunas personas han estado pidiendo en los comentarios muestras de cómo consumir la clase ImageUtilities, así que aquí tienes.

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

Nota

Recuerde que las imágenes son desechables, por lo que debe asignar el resultado de su cambio de tamaño a una declaración de uso (o podría intentarlo finalmente y asegurarse de llamar a dispose en su finalmente).

Doctor jones
fuente
ImageCodecInfo jpegCodec = getEncoderInfo ("image / jpeg"); - ¿Dónde definiste getEncoderInfo porque no puedo compilarlo?
ilija veselica
3
Debería leer GetEncoderInfo y no getEncoderInfo. Arreglé el error tipográfico y las compilaciones de clase ahora.
Doctor Jones
55
¡+1 esto funciona genial! Un problema que debe corregir en este código es convertir la variable de calidad mucho antes de pasarla al parámetro del codificador o obtendrá una excepción de tiempo de ejecución de parámetro no válida.
James
1
@Behzad, si miras, la función SaveJpeg toma un parámetro int llamado calidad. Debe llamar a eso y especificar el valor correcto para el parámetro de calidad (acepta un valor entre 0 y 100).
Doctor Jones
1
Después de una larga búsqueda, la parte de dimensionamiento de esta respuesta ( no usó código completo ) funcionó para cambiar el tamaño de qrcode sin pérdida de calidad. La configuración correcta es importante para la calidad del resultado.
Furkan Ekinci
15

Cuando dibujas la imagen usando GDI +, en mi opinión se escala bastante bien. Puede usar esto para crear una imagen a escala.

Si desea escalar su imagen con GDI +, puede hacer algo como esto:

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));
}
Hallgrim
fuente
No estoy seguro si el código ha cambiado, pero tuve que omitirlo new Sizeen la declaración de scaled:new Bitmap(original.Width * 4, original.Height * 4);
Kirk Woll
10

Las bibliotecas probadas como Imagemagick y GD están disponibles para .NET

También puede leer sobre cosas como la interpolación bicúbica y escribir la suya.

kitsune
fuente
4

Pruebe los diferentes valores para Graphics.InterpolationMode. Hay varios algoritmos de escalado típicos disponibles en GDI +. Si uno de estos es suficiente para sus necesidades, puede seguir esta ruta en lugar de confiar en una biblioteca externa.

OregonGhost
fuente
3

Puede probar dotImage , uno de los productos de mi empresa, que incluye un objeto para volver a muestrear imágenes que tiene 18 tipos de filtro para varios niveles de calidad.

El uso típico es:

// BiCubic is one technique available in PhotoShop
ResampleCommand resampler = new ResampleCommand(newSize, ResampleMethod.BiCubic);
AtalaImage newImage = resampler.Apply(oldImage).Image;

Además, dotImage incluye 140 comandos de procesamiento de imágenes extrañas que incluyen muchos filtros similares a los de PhotoShop, si eso es lo que está buscando.

pedestal
fuente
El SDK con esta función ahora está disponible de forma gratuita para formatos de fotos comunes (JPEG, PNG, etc.) atalasoft.com/photofree
Lou Franco
/ Lou Franco: ¿se puede usar la versión gratuita de formato común en implementaciones de producción, también gratis?
Oskar Austegard
Sí, DotImage Photo Free es de implementación gratuita.
Lou Franco
2

Esto puede ayudar

    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;

    }

Tenga en cuenta que InterpolationMode.HighQualityBicubic-> esto generalmente es una buena compensación entre el rendimiento y los resultados.

Leslie Marshall
fuente
2

Pruebe este fragmento de código básico:

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;
}
Temerario
fuente
0

Hay un artículo sobre Code Project sobre el uso de GDI + para .NET para redimensionar fotos usando, por ejemplo, la interpolación Bicubic.

También había otro artículo sobre este tema en otro blog (creo que un empleado de MS), pero no puedo encontrar el enlace en ninguna parte. :( Quizás alguien más pueda encontrarlo?


fuente
0

Este es un artículo que vi haciendo referencia en el código de Paint.NET para el remuestreo de imágenes: varias técnicas simples de procesamiento de imágenes por Paul Bourke.

Igor Brejc
fuente
1: buen artículo. No se pudo acceder al enlace, pero encontré este otro: local.wasp.uwa.edu.au/~pbourke/texture_colour/imageprocess
Thomas Bratt
Corregí el enlace en la publicación original ya que el enlace de Thomas también estaba roto ... paulbourke.net/texture_colour/imageprocess
Oskar Austegard
Esta respuesta sería mejor si explicara las partes pertinentes de la respuesta, en lugar de depender del enlace.
KatieK
0

Podrías probar el núcleo mágico . Produce menos artefactos de pixelación que el remuestreo bicúbico al escalar y también ofrece muy buenos resultados al escalar. El código fuente está disponible en c # desde el sitio web.

rold2007
fuente
0

Tengo algunas mejoras para la respuesta del doctor Jones.

Funciona para quién quería cómo cambiar el tamaño proporcional de la imagen. Probó y funcionó para mí.

Los métodos de clase que agregué:

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

y nuevo disponible según estos códigos:

using (var resized = ImageUtilities.ResizeImage(image, ImageUtilities.GetProportionedSize(image, 50, 100)))
{
    ImageUtilities.SaveJpeg(@"C:\myimage.jpeg", resized, 90);
}
bafsar
fuente