【问题标题】:High Quality Image Scaling Library [closed]高质量图像缩放库[关闭]
【发布时间】:2010-09-19 23:01:42
【问题描述】:

我想以与 Photoshop 一样好的质量水平在 C# 中缩放图像。有没有可用的 C# 图像处理库来做这件事?

【问题讨论】:

  • 这是在 C# 中,另一个问题是 C++,所以它根本不是重复的。
  • imageresizing.net 库提供了您可以获得的最高质量和最高性能的图像大小调整。接受的答案成为受害者to one of the many GDI+ pitfalls 并将在它生成的每个图像周围产生一个 1px 宽的边框伪影。这可以通过使用 ImageAttributes 实例来解决,该实例为 DrawImage 调用的最后一个参数设置了 TileModeXY。
  • @Computer Linguist -- TileModeXY 是错字吗?您已将此评论复制粘贴到多个答案中,并且谷歌搜索完全“TileModeXY”只会显示您的帖子。 System.Drawing.Drawing2D.WrapMode 的以下链接仅显示 5 个可能的值:Tile、TileFlipX、TileFlipY、TileFlipXY、Clamp msdn.microsoft.com/en-us/library/…
  • 对,应该是TileFlipXY,谢谢指正!

标签: c# image image-processing image-manipulation image-scaling


【解决方案1】:

这是一个注释很好的 Image Manipulation 帮助器类,您可以查看和使用它。我将其编写为如何在 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;
        } 
    }
}

更新

一些人一直在 cmets 中询问如何使用 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 并确保在 finally 中调用 dispose)。

【讨论】:

  • ImageCodecInfo jpegCodec = getEncoderInfo("image/jpeg"); - 你在哪里定义 getEncoderInfo 因为我无法编译它
  • 它应该读取 GetEncoderInfo 而不是 getEncoderInfo。我修正了错字,现在可以编译类了。
  • +1 这非常棒!您需要在此代码中更正的一个问题是在将质量变量传递给编码器参数之前将其转换为 long,否则您将获得无效参数运行时异常。
  • @Behzad,如果你看的话,SaveJpeg 函数采用一个名为 quality 的 int 参数。您需要调用它并为质量参数指定正确的值(它接受 0 到 100 之间的值)。
  • 经过长时间的搜索,这个答案的大小部分(没有使用整个代码)适用于 qrcode 调整大小而不会丢失质量。正确的设置对于结果质量很重要。
【解决方案2】:

在我看来,当您使用 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));
}

【讨论】:

  • 不确定代码是否改变了,但我不得不在scaled的声明中省略new Sizenew Bitmap(original.Width * 4, original.Height * 4);
【解决方案3】:

ImagemagickGD 等经过测试的库可用于 .NET

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

【讨论】:

    【解决方案4】:
    【解决方案5】:

    使用这个库:http://imageresizing.net

    阅读图书馆作者的这篇文章:20 Image Sizing Pitfalls with .NET

    【讨论】:

      【解决方案6】:

      为 Graphics.InterpolationMode 尝试不同的值。 GDI+ 中有几种典型的缩放算法可用。如果其中一个足以满足您的需求,您可以走这条路,而不是依赖外部库。

      【讨论】:

        【解决方案7】:

        您可以尝试dotImage,这是我公司的产品之一,其中包括object for resampling 图像,其中18 filter types 用于各种质量级别。

        典型用法是:

        // 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:通用格式免费版能否用于生产部署,也是免费的?
        • 是的,DotImage Photo Free 可以免费部署。
        【解决方案8】:

        这可能会有所帮助

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

        【讨论】:

          【解决方案9】:

          试试这个基本代码sn-p:

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

          【讨论】:

            【解决方案10】:

            有一个 article on Code Project 关于使用 GDI+ for .NET 来调整照片大小,比如使用双三次插值。

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

            【讨论】:

              【解决方案11】:

              如果是低分辨率 cgi,你可以试试这个 2D Image Filter

              【讨论】:

                【解决方案12】:

                这是我发现在 Paint.NET 的图像重采样代码中引用的一篇文章:Various Simple Image Processing Techniques Paul Bourke。

                【讨论】:

                【解决方案13】:

                你可以试试the magic kernel。与双三次重采样相比,它在放大时产生的像素化伪影更少,并且在缩小时也能提供非常好的结果。 源代码可从网站获得的 c# 格式。

                【讨论】:

                  【解决方案14】:

                  我对琼斯医生的回答有些改进。

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

                  我添加的类的方法:

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

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 2010-09-26
                    • 2019-01-30
                    • 1970-01-01
                    • 1970-01-01
                    • 2021-11-06
                    • 1970-01-01
                    相关资源
                    最近更新 更多