高质量图像缩放库


141

我想在C#中以与Photoshop一样好的质量缩放图像。是否有任何C#图像处理库可用于执行此操作?


47
这是在C#中,另一个问题是C ++,因此根本不是重复的。
琼斯医生

7
imageresizing.net库提供最高质量和最高性能的图像缩放,你可以得到。可接受的答案是许多GDI +陷阱之一的受害者并且会在生成的每个图像周围造成1px宽的边框伪像。通过使用ImageAttributes实例(将TileModeXY设置为DrawImage调用的最后一个参数)可以解决此问题。
莉莉丝·里弗

2
@计算机语言学家-TileModeXY是错字吗?您已在多个答案中复制并粘贴了此评论,而Google搜索“ TileModeXY”恰好显示了您的帖子。下面的链接,System.Drawing.Drawing2D.WrapMode仅显示5个值:瓷砖,TileFlipX,TileFlipY,TileFlipXY,钳形 msdn.microsoft.com/en-us/library/...
JasDev

1
是的,应该是TileFlipXY,感谢您的纠正!
莉莉丝·里弗

Answers:


233

这是一个很好用的图像处理帮助器类,您可以查看和使用。我将其作为如何在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)。


ImageCodecInfo jpegCodec = getEncoderInfo(“ image / jpeg”); -您在哪里定义了getEncoderInfo导致无法编译它
ilija veselica 2010年

3
它应该读取GetEncoderInfo而不是getEncoderInfo。我修正了错字,现在该类可以编译了。
琼斯医生

5
+1如此出色!您需要在此代码中更正的一个问题是,在将质量变量传递给编码器参数之前将其转换为很长的时间,否则您将获得无效的参数运行时异常。
詹姆斯2010年

1
@Behzad,如果您看的话,SaveJpeg函数采用一个称为quality的int参数。您需要调用它并为quality参数指定正确的值(它接受0到100之间的值)。
琼斯医生

1
经过长时间的搜索,此答案的大小调整部分(未使用完整代码)可用于调整qrcode大小而不会降低质量。正确的设置对于结果质量很重要。
Furkan Ekinci

15

我认为,当您使用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在以下声明中忽略scalednew Bitmap(original.Width * 4, original.Height * 4);
Kirk Woll

10

.NET提供了经过测试的库,例如ImagemagickGD

您还可以阅读双三次插值之类的内容并编写自己的内容。




4

尝试使用Graphics.InterpolationMode的其他值。GDI +中提供了几种典型的缩放算法。如果其中之一足以满足您的需要,则可以采用这种方法,而不是依赖于外部库。


3

您可以尝试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 2010年

/卢·佛朗哥(Lou Franco):通用格式的免费版本是否可以在生产部署中使用,也可以免费使用?
奥斯卡·奥斯塔加德

是的,DotImage Photo Free是免费部署的。
Lou Franco

2

这可能有帮助

    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->这通常是性能和结果之间的良好折衷。


2

试试这个基本代码片段:

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

0

在Code Project上一篇文章,关于使用GDI + for .NET使用Bicubic插值对照片进行大小调整。

在另一个博客上(我认为是MS员工),也有关于该主题的另一篇文章,但是我在任何地方都找不到链接。:(也许其他人可以找到它?



0

我发现这是一篇由Paul Bourke 在Paint.NET的图像重采样代码中引用的文章:各种简单图像处理技术


1:好文章 无法访问链接,但找到了另一个链接:local.wasp.uwa.edu.au/~pbourke/texture_colour/imageprocess
Thomas Bratt

我更正了原始帖子中的链接,因为Thomas的链接也已断开... paulbourke.net/texture_colour/imageprocess
Oskar Austegard 2012年

如果它解释了答案的相关部分,而不是依赖于链接,则该答案会更好。
KatieK

0

您可以尝试魔术内核。放大时,与双三次重采样相比,它产生的像素化伪影更少,而缩小时,也提供非常好的结果。该源代码可以从网站上的c#中获得。


0

我对琼斯医生的答案有所改进。

它适用于想要如何按比例调整图像大小的人。它为我测试并工作。

我添加的类的方法:

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);
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.