2009-07-09 92 views
34

一个简单的问题,但由于某种原因,我今天无法弄清楚这一点。调整图像以适合边框

我需要将图像调整到最大可能大小,将适合在边界框,同时保持高宽比。

Basicly我正在寻找的代码填写此功能:

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

其中,w & h为原来的高度和宽度(以)和新的高度和宽度(出)和MaxWidth和。MaxHeight定义图像必须适合边框

+7

像请不要滥用裁判。最好制作一个带有宽度和高度字段的_immutable_结构Rectangle,然后编写一个方法ExpandToBound,该方法需要两个Rectangle并返回生成的Rectangle。当你将它们作为_functions_实现时,推理功能要容易得多。争论进入,结果出来;函数不会改变它们不拥有的状态。 – 2009-07-09 20:55:40

+0

@Eric Lippert - 同意,这个例子不是我实际实现的函数,只是一个简化版本,以避免与Rectangle结构或其他不属于问题核心的问题混淆。 – 2009-12-22 15:02:26

回答

74

查找哪一个较小:MaxWidth/wMaxHeight/h 然后由数乘以wh

说明:

你需要找到的缩放因子使图像配合。

要查找比例因子s,则为宽度,那么s必须为: s * w = MaxWidth。 因此,缩放因子是MaxWidth/w

类似的高度。

,需要最缩放的一个(小s)是你必须调整整个图像的因素。

+0

如果你用float来做这件事,你可能会发现与边界框相匹配的尺寸略微偏离(有时候,以不可预知的方式)。当你确定哪个维度不匹配时,最好简单地假设另一个维度应该是什么? – 2009-08-19 23:02:55

+4

+1三年后,我坐在Google结果的顶部。没有麻烦,没有大惊小怪。谢谢! – chaosTechnician 2012-06-04 16:21:46

+1

就像你的回复没有写在c中一样,所以我可以将它应用到python – 2015-06-28 23:18:18

0

我倒是类似的问题,我发现非常有用:article。正如我理解正确,你需要调整图像的大小?

2

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) 
26

基于埃里克的建议,我会做这样的事情:

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

我可能去了一下在演职员表上过度工作,但我只是想在计算中保持精确度。

6

要执行方面填充而不是方面拟合,请改用较大的比例。也就是说,将Matt的代码从Math.Min更改为Math.Max。

(纵横填充不会将边界框留空,但可能会将某些图像放在边界外,而纵横比不会超出边界的图像,但可能会使边界框的某些部分为空。)

6

试用了沃伦先生的代码,但没有产生可靠的结果。

例如,

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} 

可以看到,高度= 49,并且在另一个高度= 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)); 
} 
4

下面的代码生成更加准确的结果:

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

根据前面的答案,这里是一个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 
    }; 
} 
1

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

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)