【问题标题】:Resize Image to fit in bounding box调整图像大小以适合边界框
【发布时间】:2020-06-05 19:05:20
【问题描述】:

一个简单的问题,但由于某种原因我今天无法解决这个问题。

我需要在保持纵横比的同时将图像调整为适合边界框的最大可能尺寸。

基本上我在找代码来填写这个函数:

void CalcNewDimensions(ref int w, ref int h, int MaxWidth, int MaxHeight);

其中 w & h 是原始高度和宽度 (in) 以及新的高度和宽度 (out),MaxWidth 和 MaxHeight 定义了图像必须适合的边界框。

【问题讨论】:

  • 请不要这样滥用裁判。最好制作一个具有宽度和高度字段的 不可变 struct Rectangle,然后编写一个方法 ExpandToBound,该方法接受两个 Rectangle 并返回结果 Rectangle。当您将函数实现为函数时,推理函数要容易得多。争论进去,结果出来;函数不会改变它们不拥有的状态。
  • @Eric Lippert - 同意,该示例不是我实际实现的功能,只是一个简化版本,以避免将问题与 Rectangle 结构或其他不属于核心的东西混淆的问题。

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


【解决方案1】:

找出哪个更小:MaxWidth / wMaxHeight / h 然后将wh 乘以那个数字

说明:

您需要找到使图像适合的比例因子。

要找到宽度的比例因子s,那么s 必须满足: s * w = MaxWidth。 因此,缩放因子为MaxWidth / w

身高也是如此。

需要最多缩放(较小的s)是您必须缩放整个图像的因素。

【讨论】:

  • 如果你用浮点数做这个,你可能会发现与边界框匹配的尺寸略有偏差(有时,以一种不可预测的方式)。当您确定哪个维度不匹配时,是否最好简单地假设另一个维度正是它应该是的?
  • +1 三年后,我在 Google 搜索结果中名列前茅。没有混乱,没有大惊小怪。谢谢!
  • 就像你的回复不是用c写的,所以我可以将它应用到python
  • 感谢您提供算法和解释,而不仅仅是代码!我希望更多的解决方案是这样写的。
  • 谢谢,这很好用。解释也确实有帮助
【解决方案2】:

根据 Eric 的建议,我会这样做:

private static Size ExpandToBound(Size image, Size boundingBox)
{       
    double widthScale = 0, heightScale = 0;
    if (image.Width != 0)
        widthScale = (double)boundingBox.Width / (double)image.Width;
    if (image.Height != 0)
        heightScale = (double)boundingBox.Height / (double)image.Height;                

    double scale = Math.Min(widthScale, heightScale);

    Size result = new Size((int)(image.Width * scale), 
                        (int)(image.Height * scale));
    return result;
}

我在演员表上可能有点过火了,但我只是想保持计算的精度。

【讨论】:

  • 别忘了return result;
【解决方案3】:

要执行纵横比填充而不是纵横匹配,请改用较大的比率。即把Matt的代码从Math.Min改成Math.Max。

(方面填充不会使任何边界框为空,但可能会将某些图像置于边界之外,而方面拟合不会将任何图像留在边界之外,但可能会使某些边界框为空。)

【讨论】:

  • 很好的信息。看其他答案对我来说并不直观
【解决方案4】:

尝试了 Warren 先生的代码,但没有产生可靠的结果。

例如,

ExpandToBound(new Size(640,480), new Size(66, 999)).Dump();
// {Width=66, Height=49}

ExpandToBound(new Size(640,480), new Size(999,50)).Dump();
// {Width=66, Height=50}

你可以看到,height = 49 和 height = 50 在另一个。

这是我的(沃伦先生的代码的基础版本),没有差异和轻微的重构:

// Passing null for either maxWidth or maxHeight maintains aspect ratio while
//        the other non-null parameter is guaranteed to be constrained to
//        its maximum value.
//
//  Example: maxHeight = 50, maxWidth = null
//    Constrain the height to a maximum value of 50, respecting the aspect
//    ratio, to any width.
//
//  Example: maxHeight = 100, maxWidth = 90
//    Constrain the height to a maximum of 100 and width to a maximum of 90
//    whichever comes first.
//
private static Size ScaleSize( Size from, int? maxWidth, int? maxHeight )
{
   if ( !maxWidth.HasValue && !maxHeight.HasValue ) throw new ArgumentException( "At least one scale factor (toWidth or toHeight) must not be null." );
   if ( from.Height == 0 || from.Width == 0 ) throw new ArgumentException( "Cannot scale size from zero." );

   double? widthScale = null;
   double? heightScale = null;

   if ( maxWidth.HasValue )
   {
       widthScale = maxWidth.Value / (double)from.Width;
   }
   if ( maxHeight.HasValue )
   {
       heightScale = maxHeight.Value / (double)from.Height;
   }

   double scale = Math.Min( (double)(widthScale ?? heightScale),
                            (double)(heightScale ?? widthScale) );

   return new Size( (int)Math.Floor( from.Width * scale ), (int)Math.Ceiling( from.Height * scale ) );
}

【讨论】:

  • 你应该在最后一行的两种情况下都使用 Math.Round,因为在你的代码中我们得到了错误的结果。
【解决方案5】:

以下代码产生更准确的结果:

    public static Size CalculateResizeToFit(Size imageSize, Size boxSize)
    {
        // TODO: Check for arguments (for null and <=0)
        var widthScale = boxSize.Width / (double)imageSize.Width;
        var heightScale = boxSize.Height / (double)imageSize.Height;
        var scale = Math.Min(widthScale, heightScale);
        return new Size(
            (int)Math.Round((imageSize.Width * scale)),
            (int)Math.Round((imageSize.Height * scale))
            );
    }

【讨论】:

    【解决方案6】:

    Python 代码,但也许它会为您指明正确的方向:

    def fit_within_box(box_width, box_height, width, height):
        """
        Returns a tuple (new_width, new_height) which has the property
        that it fits within box_width and box_height and has (close to)
        the same aspect ratio as the original size
        """
        new_width, new_height = width, height
        aspect_ratio = float(width) / float(height)
    
        if new_width > box_width:
            new_width = box_width
            new_height = int(new_width / aspect_ratio)
    
        if new_height > box_height:
            new_height = box_height
            new_width = int(new_height * aspect_ratio)
    
        return (new_width, new_height)
    

    【讨论】:

    • 如果图像必须缩小,您的代码就可以工作,而不是必须扩展以适应容器。我认为你应该像这样更新你的条件:if new_width &gt; box_width or new_height &lt; box_height: 和:if new_height &gt; box_height or new_width &lt; box_width:
    【解决方案7】:

    超简单。 :) 问题是找到一个你需要乘以宽度和高度的因素。解决方案是尝试使用一个,如果不合适,使用另一个。所以...

    private float ScaleFactor(Rectangle outer, Rectangle inner)
    {
        float factor = (float)outer.Height / (float)inner.Height;
        if ((float)inner.Width * factor > outer.Width) // Switch!
            factor = (float)outer.Width / (float)inner.Width;
        return factor;
    }
    

    像这样将图片(pctRect)适合窗口(wndRect)调用

    float factor=ScaleFactor(wndRect, pctRect); // Outer, inner
    RectangleF resultRect=new RectangleF(0,0,pctRect.Width*factor,pctRect.Height*Factor)
    

    【讨论】:

      【解决方案8】:

      根据前面的答案,这里有一个 Javascript 函数:

      /**
      * fitInBox
      * Constrains a box (width x height) to fit in a containing box (maxWidth x maxHeight), preserving the aspect ratio
      * @param width      width of the box to be resized
      * @param height     height of the box to be resized
      * @param maxWidth   width of the containing box
      * @param maxHeight  height of the containing box
      * @param expandable (Bool) if output size is bigger than input size, output is left unchanged (false) or expanded (true)
      * @return           {width, height} of the resized box
      */
      function fitInBox(width, height, maxWidth, maxHeight, expandable) {
          "use strict";
      
          var aspect = width / height,
              initWidth = width,
              initHeight = height;
      
          if (width > maxWidth || height < maxHeight) {
              width = maxWidth;
              height = Math.floor(width / aspect);
          }
      
          if (height > maxHeight || width < maxWidth) {
              height = maxHeight;
              width = Math.floor(height * aspect);
          }
      
          if (!!expandable === false && (width >= initWidth || height >= initHeight)) {
              width = initWidth;
              height = initHeight;
          }
      
          return {
              width: width,
              height: height
          };
      }
      

      【讨论】:

        【解决方案9】:

        此任务的 Python 代码基于 Jason 的回答,并修复了使用 img.shape 传递常规参数的放大和参数重新排序。

        def fit_within_box(box_height, box_width, height, width):
            """
            Returns a tuple (new_width, new_height) which has the property
            that it fits within box_width and box_height and has (close to)
            the same aspect ratio as the original size
            """
            new_width, new_height = width, height
            aspect_ratio = float(width) / float(height)
        
            if new_width > box_width or new_height < box_height:
                new_width = box_width
                new_height = int(new_width / aspect_ratio)
        
            if new_height > box_height or new_width < box_width:
                new_height = box_height
                new_width = int(new_height * aspect_ratio)
        
            return new_height, new_width
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-05-05
          • 2018-09-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-05-24
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多