2009-07-09 144 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)