Hochwertige Bildskalierungsbibliothek [geschlossen]

141

Ich möchte ein Bild in C # mit einer so guten Qualitätsstufe wie Photoshop skalieren. Gibt es dafür eine C # -Bildverarbeitungsbibliothek?

Ramesh Soni
quelle
47
Dies ist in C #, diese andere Frage ist C ++, es ist also überhaupt kein Duplikat.
Doktor Jones
7
Die Bibliothek imageresizing.net bietet die Bildqualität von höchster Qualität und Leistung, die Sie erhalten können. Die akzeptierte Antwort fällt einer der vielen Fallstricke von GDI + zum Opfer und verursacht ein 1 Pixel breites Randartefakt um jedes erzeugte Bild. Dies wird durch die Verwendung einer ImageAttributes-Instanz behoben, bei der TileModeXY für den letzten Parameter des DrawImage-Aufrufs festgelegt ist.
Lilith River
2
@Computer Linguist - ist TileModeXY ein Tippfehler? Sie haben diesen Kommentar über mehrere Antworten hinweg kopiert und eine Google-Suche nach genau "TileModeXY" zeigt nur Ihre Beiträge an. Der folgende Link für System.Drawing.Drawing2D.WrapMode zeigt nur 5 mögliche Werte: Tile, TileFlipX, TileFlipY, TileFlipXY, Clamp msdn.microsoft.com/en-us/library/…
JasDev
1
Ja, es sollte TileFlipXY sein, danke für die Korrektur!
Lilith River

Antworten:

233

Hier ist eine gut kommentierte Bildmanipulations-Hilfsklasse, die Sie ansehen und verwenden können. Ich habe es als Beispiel dafür geschrieben, wie bestimmte Bildbearbeitungsaufgaben in C # ausgeführt werden. Sie interessieren sich für die ResizeImage- Funktion, die ein System.Drawing.Image, die Breite und die Höhe als Argumente verwendet.

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

Aktualisieren

Einige Leute haben in den Kommentaren nach Beispielen gefragt, wie die ImageUtilities-Klasse verwendet werden soll.

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

Hinweis

Denken Sie daran, dass Bilder verfügbar sind. Daher müssen Sie das Ergebnis Ihrer Größenänderung einer using-Deklaration zuordnen (oder Sie können einen Versuch endgültig verwenden und sicherstellen, dass Sie in Ihrer Endgültigkeit Dispose aufrufen).

Doktor Jones
quelle
ImageCodecInfo jpegCodec = getEncoderInfo ("image / jpeg"); - Wo hast du getEncoderInfo definiert, weil ich es nicht kompilieren kann
ilija veselica
3
Es sollte GetEncoderInfo und nicht getEncoderInfo lauten. Ich habe den Tippfehler behoben und die Klasse wird jetzt kompiliert.
Doktor Jones
5
+1 das funktioniert hervorragend! Ein Problem, das Sie in diesem Code korrigieren müssen, besteht darin, die Qualitätsvariable in eine lange zu konvertieren, bevor Sie sie an den Encoder-Parameter übergeben. Andernfalls wird eine ungültige Parameter-Laufzeitausnahme angezeigt.
James
1
@Behzad, wenn Sie schauen, nimmt die SaveJpeg-Funktion einen int-Parameter namens Qualität. Sie müssen das aufrufen und den richtigen Wert für den Qualitätsparameter angeben (er akzeptiert einen Wert zwischen 0 und 100).
Doktor Jones
1
Nach einer langen Suche funktionierte der Dimensionierungsteil dieser Antwort ( der nicht den gesamten Code verwendete ) für die Größenänderung von qrcode, ohne dass die Qualität verloren ging. Die richtigen Einstellungen sind wichtig für die Ergebnisqualität.
Furkan Ekinci
15

Wenn Sie das Bild mit GDI + zeichnen, skaliert es meiner Meinung nach recht gut. Sie können dies verwenden, um ein skaliertes Bild zu erstellen.

Wenn Sie Ihr Bild mit GDI + skalieren möchten, können Sie Folgendes tun:

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
quelle
Ich bin mir nicht sicher, ob sich der Code geändert hat, aber ich musste das new Sizein der Erklärung weglassen scaled:new Bitmap(original.Width * 4, original.Height * 4);
Kirk Woll
10

Getestete Bibliotheken wie Imagemagick und GD sind für .NET verfügbar

Sie können sich auch über Dinge wie die bikubische Interpolation informieren und Ihre eigenen schreiben.

Kitsune
quelle
4

Probieren Sie die verschiedenen Werte für Graphics.InterpolationMode aus. In GDI + stehen mehrere typische Skalierungsalgorithmen zur Verfügung. Wenn eine davon für Ihren Bedarf ausreicht, können Sie diesen Weg gehen, anstatt sich auf eine externe Bibliothek zu verlassen.

OregonGhost
quelle
3

Sie können dotImage ausprobieren , eines der Produkte meines Unternehmens, das ein Objekt zum erneuten Abtasten von Bildern enthält, das 18 Filtertypen für verschiedene Qualitätsstufen enthält.

Typische Verwendung ist:

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

Darüber hinaus enthält dotImage einige ungerade Bildverarbeitungsbefehle, darunter viele Filter, die denen in PhotoShop ähneln, wenn Sie danach suchen.

Sockel
quelle
Das SDK mit dieser Funktion ist jetzt kostenlos für gängige Fotoformate (JPEG, PNG usw.) verfügbar. Atalasoft.com/photofree
Lou Franco
/ Lou Franco: Kann die kostenlose Version des allgemeinen Formats in Produktionsbereitstellungen auch kostenlos verwendet werden?
Oskar Austegard
Ja, DotImage Photo Free kann kostenlos bereitgestellt werden.
Lou Franco
2

Dies könnte helfen

    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;

    }

Beachten Sie, dass InterpolationMode.HighQualityBicubic-> dies im Allgemeinen ein guter Kompromiss zwischen Leistung und Ergebnissen ist.

Leslie Marshall
quelle
2

Versuchen Sie dieses grundlegende Code-Snippet:

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;
}
Draufgänger
quelle
0

Es gibt einen Artikel über Code Project über die Verwendung von GDI + für .NET, um die Größe von Fotos mithilfe von beispielsweise bikubischer Interpolation zu ändern.

Es gab auch einen anderen Artikel zu diesem Thema in einem anderen Blog (MS-Mitarbeiter, glaube ich), aber ich kann den Link nirgendwo finden. :( Vielleicht kann es jemand anderes finden?


quelle
0

Sie können dies versuchen, wenn es sich um einen 2D- Bildfilter mit niedriger Auflösung handelt

Cryx
quelle
0

Dies ist ein Artikel, auf den ich im Code von Paint.NET für das Resampling von Bildern verwiesen habe: Verschiedene einfache Bildverarbeitungstechniken von Paul Bourke.

Igor Brejc
quelle
1: Schöner Artikel. Konnte nicht auf den Link zugreifen, fand aber einen anderen: local.wasp.uwa.edu.au/~pbourke/texture_colour/imageprocess
Thomas Bratt
Ich habe den Link im ursprünglichen Beitrag korrigiert, da auch der Link von Thomas unterbrochen wurde ... paulbourke.net/texture_colour/imageprocess
Oskar Austegard
Diese Antwort wäre besser, wenn sie die relevanten Teile der Antwort erläutern würde, als sich auf den Link zu verlassen.
KatieK
0

Sie könnten den magischen Kernel ausprobieren . Es erzeugt beim Hochskalieren weniger Pixelartefakte als das bikubische Resample und liefert auch beim Herunterskalieren sehr gute Ergebnisse. Der Quellcode ist in c # auf der Website verfügbar.

rold2007
quelle
0

Ich habe einige Verbesserungen für Doktor Jones 'Antwort.

Es funktioniert für diejenigen, die eine proportionale Größenänderung des Bildes wünschen. Es hat für mich getestet und funktioniert.

Die Methoden der Klasse, die ich hinzugefügt habe:

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

und neu verfügbar unter Verwendung dieser Codes:

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