【问题标题】:Image Resize in C# - Algorith to determine resize dimensions (height and width)C# 中的图像调整大小 - 确定调整大小(高度和宽度)的算法
【发布时间】:2011-07-10 12:17:14
【问题描述】:

我需要缩小高度或宽度大于预定义像素值的图像。

我编写了一些代码来查看原始图像,检查宽度、高度或高度和宽度是否大于最大宽度/最大高度设置。

我现在需要根据后一个值的最大值确定要调整到哪些尺寸。

例如:如果图像为900h x 300w,最大高度为700h,我需要将高度调整为700,将宽度调整为????

创建和保存图像文件很简单,超出了本文的范围:

// First I get the max height and width allowed:

int resizeMaxHeight =  int.Parse(Utility.GetConfigValue("ResizeMaxHeight")); // in config: 700px
int resizeMaxWidth =  int.Parse(Utility.GetConfigValue("ResizeMaxWidth"));  //  in config: 500px

// Save original: 
try
{
    filebase.SaveAs(savedFileName);
}
catch (System.IO.DirectoryNotFoundException ex)
{
    Logger.Instance.LogException(ex, 0, "FileTransfer");
}

// Determin original dimensions:
Image image = System.Drawing.Image.FromFile(Server.MapPath(savedFileName));

int resizeHeight, resizeWidth;
bool doResize = true;

// both height and width are greater than the allowed height and width:
if (image.Width > resizeMaxWidth && image.Height > resizeMaxHeight)
{
    if (image.Height > image.Width) 
        resizeHeight = resizeMaxHeight;
    else
        resizeWidth = resizeMaxWidth;
}
else if (image.Width > resizeMaxWidth)
{
    // width is too great, but height is ok
    resizeWidth = resizeMaxWidth;
}
else if (image.Height > resizeMaxHeight)
{
    // height is too great, but width is ok
    resizeHeight = resizeMaxHeight;
}
else
{
    // image is ok size, don't resize:
    doResize = false;
}

创建缩略图: 这就是我现在正在做的……不完整:

if (doResize)
{
    ImageUtilities.ResizeImage(image, resizeWidth, resizeHeight);
}

【问题讨论】:

    标签: c# image-processing bitmap image-manipulation system.drawing


    【解决方案1】:

    这里有两种计算方法。根据您对问题的看法,一个似乎比另一个更直观。它们在数学上相当于几个小数位。

    两者对于 Math.Round 都是安全的,但只有 ConstrainVerbose 产生的结果总是小于 maxWidth/maxHeight。

    SizeF ConstrainConcise(int imageWidth, int imageHeight, int maxWidth, int maxHeight){
        // Downscale by the smallest ratio (never upscale)
        var scale = Math.Min(1, Math.Min(maxWidth / (float)imageWidth, maxHeight / (float) imageHeight));
        return new SizeF(scale * imageWidth, scale * imageHeight);
    }
    
    SizeF ConstrainVerbose(int imageWidth, int imageHeight, int maxWidth, int maxHeight){
        // Coalculate the aspect ratios of the image and bounding box
        var maxAspect = (float) maxWidth / (float) maxHeight;
        var aspect =  (float) imageWidth / (float) imageHeight;
        // Bounding box aspect is narrower
        if (maxAspect <= aspect && imageWidth > maxWidth)
        {
            // Use the width bound and calculate the height
            return new SizeF(maxWidth, Math.Min(maxHeight, maxWidth / aspect));
        }
        else if (maxAspect > aspect && imageHeight > maxHeight)
        {
            // Use the height bound and calculate the width
            return new SizeF(Math.Min(maxWidth, maxHeight * aspect), maxHeight);
        }else{
            return new SizeF(imageWidth, imageHeight);
        }
    }
    

    Brute force unit-test here

    【讨论】:

    • 是的,是的!计算纵横比。有我一直在寻找的术语。太棒了,非常感谢。
    • 不客气。我建议你也阅读这篇文章 - .NET 图像大小调整有很多陷阱,你应该避开:nathanaeljones.com/163/20-image-resizing-pitfalls
    • 如果图像的高度大于宽度,这个答案实际上会失败!
    • @DanSoper 感谢您的提醒!我以某种方式颠倒了&lt;&gt;。它现在已修复(我通过蛮力测试运行它)。
    【解决方案2】:

    我为 Bitmaps 做了类似的事情,但想法是一样的:

    1. get image height and width
    2. get current screen resolution
    3. calculate aspect ratio (ASR) from image size
    
    Handle following cases:
    
    4. if ASR >=1 and image width > image height
        if image width > screen width {}
            if image height > screen height {}
            else if image width > screen width {}
        else {}
       else
        if image height > screen height {}
        else if image width > screen width {}
        else {}
    

    //SCREEN_SIZE 是可配置的; Defs.SCREEN_SIZE = 100; // 并且 boolPixelAR 为真;

    试试下面的代码:

                // PERCENTAGE OF IMAGE -> TODO: Configurable? IMAZE ZOOM / SCREEN PERCENTAGE
                Double HScale = __bmp.Width;// *Defs.SCREEN_SIZE / 100;
                Double VScale = __bmp.Height;// *Defs.SCREEN_SIZE / 100;
                Double __aspectRatio;
                Double __screenRatio = _currentScreenSize.Width / _currentScreenSize.Height;
    
                // PERCENTAGE OF SCREEN
                if (!_boolPixelAR) {
                    HScale = _currentScreenSize.Width * Defs.SCREEN_SIZE / 100;
                    VScale = _currentScreenSize.Height * Defs.SCREEN_SIZE / 100;
                }
                else {
                    __aspectRatio = HScale / VScale;
                    if( __aspectRatio >= 1)
                        if (HScale >= _currentScreenSize.Width) {  // Long Edge is WIDTH. For 100%, HScale = WIDTH
                            VScale = ((VScale * _currentScreenSize.Width) / HScale) * Defs.SCREEN_SIZE / 100;
                            HScale = _currentScreenSize.Width * Defs.SCREEN_SIZE / 100;
    
                            if (VScale > _currentScreenSize.Height) {                  // Long Edge is HEIGHT. For 100%, VScale = HEIGHT
                                //__aspectRatio = VScale / HScale;
                                HScale = ((HScale * _currentScreenSize.Height) / VScale) * Defs.SCREEN_SIZE / 100;
                                VScale = _currentScreenSize.Height * Defs.SCREEN_SIZE / 100;
                            }
                        }
                        else if (VScale > _currentScreenSize.Height) {                  // Long Edge is HEIGHT. For 100%, VScale = HEIGHT
                            //__aspectRatio = VScale / HScale;
                            HScale = ((HScale * _currentScreenSize.Height) / VScale) * Defs.SCREEN_SIZE / 100;
                            VScale = _currentScreenSize.Height * Defs.SCREEN_SIZE / 100;
                        } 
                        else {
                            //Do nothing... Just set Zoom.
                            HScale = HScale * Defs.SCREEN_SIZE / 100;
                            VScale = VScale * Defs.SCREEN_SIZE / 100;
                        }
                    else 
                        if (VScale > _currentScreenSize.Height) {                  // Long Edge is HEIGHT. For 100%, VScale = HEIGHT
                            //__aspectRatio = VScale / HScale;
                            HScale = ((HScale * _currentScreenSize.Height) / VScale) * Defs.SCREEN_SIZE / 100;
                            VScale = _currentScreenSize.Height * Defs.SCREEN_SIZE / 100;
                        }
                        else if (HScale >= _currentScreenSize.Width) {  // Long Edge is WIDTH. For 100%, HScale = WIDTH
                            VScale = ((VScale * _currentScreenSize.Width) / HScale) * Defs.SCREEN_SIZE / 100;
                            HScale = _currentScreenSize.Width * Defs.SCREEN_SIZE / 100;
                        } 
                        else {
                            //Do nothing... Just set Zoom.
                            HScale = HScale * Defs.SCREEN_SIZE / 100;
                            VScale = VScale * Defs.SCREEN_SIZE / 100;
                        }
    
                    ////__aspectRatio = VScale / HScale;
                    //HScale = ((HScale * _currentScreenSize.Height) / VScale) * Defs.SCREEN_SIZE / 100;
                    //VScale = _currentScreenSize.Height * Defs.SCREEN_SIZE / 100;
                }
    
                Bitmap scaledBmp = GraphicsFactory.ResizeImage(
                                            __bmp,
                                            Convert.ToInt32(HScale),
                                            Convert.ToInt32(VScale));
    

    【讨论】:

      【解决方案3】:

      您可以避免使用一些整数技巧来计算纵横比(并使用双精度数)..

      // You have the new height, you need the new width
      int orgHeight = 1200;
      int orgWidth = 1920;
      
      int newHeight = 400;
      int newWidth = (newHeight * orgWidth) / orgHeight; // 640
      

      或者...

      // You have the new width, you need the new height.
      int orgWidth = 1920;
      int orgHeight = 1200;
      
      int newWidth = 800;
      int newHeight = (newWidth * orgHeight) / orgWidth; // 500
      

      以下示例将图像大小调整为任何所需的矩形(desWidth 和 desHeight)并将图像在该矩形内居中。

      static Image ResizeImage(Image image, int desWidth, int desHeight)
      {
          int x, y, w, h;
      
          if (image.Height > image.Width)
          {
              w = (image.Width * desHeight) / image.Height;
              h = desHeight;
              x = (desWidth - w) / 2;
              y = 0;
          }
          else
          {
              w = desWidth;
              h = (image.Height * desWidth) / image.Width;
              x = 0;
              y = (desHeight - h) / 2;
          }
      
          var bmp = new Bitmap(desWidth, desHeight);
      
          using (Graphics g = Graphics.FromImage(bmp))
          {
              g.CompositingQuality = CompositingQuality.HighQuality;
              g.InterpolationMode = InterpolationMode.HighQualityBicubic;
              g.DrawImage(image, x, y, w, h);
          }
      
          return bmp;
      }
      

      【讨论】:

      • 我不认为作者事先知道他需要什么,因此需要比较纵横比。
      • 嗯.. 在我看来他知道需要调整哪个轴的大小,他只是不确定如何计算纵横比。我会扩大我的答案。
      • (不知道如何在这里回复答案:您的解决方案使图像成为您提供的比例。因此它会裁剪不适合的部分。例如,如果原始图像是 500x500,而您为该方法提供 200x100,您将丢失一半的图像。我认为在这种情况下,原始海报需要 100x100 的图像。但是,如果您正在寻找裁剪,那么您的解决方案具有很大的价值。
      【解决方案4】:

      如果图像高度大于图像宽度,Nathaniel 发布的解决方案实际上会失败。下面的例子产生了正确的结果:

      private Size ResizeFit(Size originalSize, Size maxSize)
      {
          var widthRatio = (double)maxSize.Width / (double)originalSize.Width;
          var heightRatio = (double) maxSize.Height/(double) originalSize.Height;
          var minAspectRatio = Math.Min(widthRatio, heightRatio);
          if (minAspectRatio > 1)
              return originalSize;
          return new Size((int)(originalSize.Width*minAspectRatio), (int)(originalSize.Height*minAspectRatio));
      }
      

      【讨论】:

        【解决方案5】:

        使图像适应新尺寸需要两个操作:

        1. 调整大小 - 调整源图像的大小以恰好适合一个维度(宽度或高度 - 比例较小的那个)

        2. 裁剪 - 将上一个操作的结果裁剪到目标尺寸

        这是一个小样本:

            private static Image Resize(Image img, int width, int height)
            {
                Bitmap b = new Bitmap(width, height);
                using (Graphics g = Graphics.FromImage((Image)b))
                {
                    g.DrawImage(img, 0, 0, width, height);
                }
        
                return (Image)b;
            }
        
            public static Image Crop(Image image, int width, int height)
            {
                int cropx = image.Width > width ? image.Width / 2 - width / 2 : 0;
                int cropy = image.Height > height ? image.Height / 2 - height / 2 : 0;
                width = image.Width > width ? width : image.Width;
                height = image.Height > height ? height : image.Height;
        
                Rectangle cropRect = new Rectangle(cropx, cropy, width, height);
        
                var target = new Bitmap(cropRect.Width, cropRect.Height);
        
                using (Graphics g = Graphics.FromImage(target))
                {
                    g.DrawImage(image, new Rectangle(0, 0, target.Width, target.Height), cropRect, GraphicsUnit.Pixel);
                }
        
                return target;
            }
        
            public static Image FitToSize(Image image, int width, int height)
            {
                var wratio = 1.0 * image.Width / width;
                var hratio = 1.0 * image.Height / height;
        
                int wresize;
                int hresize;
        
                if (wratio >= hratio && wratio > 1)
                {
                    wresize = (int)Math.Round((double)image.Width / hratio);
                    hresize = height;
        
                    image = Resize(image, wresize, hresize);
                    image = Crop(image, width, height);  
                }
                else if (hratio >= wratio && hratio > 1)
                {
                    hresize = (int)Math.Round((double)image.Height / wratio);
                    wresize = width;
        
                    image = Resize(image, wresize, hresize);
                    image = Crop(image, width, height);
                }
                return image;
        
            }
        

        【讨论】:

          猜你喜欢
          • 2013-04-28
          • 2017-12-08
          • 2015-05-19
          • 1970-01-01
          • 2011-08-08
          • 2016-03-30
          • 2013-09-14
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多