调整图像大小以适应边界框
一个简单的问题,但由于某种原因,我今天无法弄清楚这一点。
我需要调整图像的大小,以适应边界框,同时保持纵横比。
基本上我正在寻找代码来填写这个function:
void CalcNewDimensions(ref int w, ref int h, int MaxWidth, int MaxHeight);
其中w&h是原始高度和宽度(in),新的高度和宽度(out)以及MaxWidth和MaxHeight定义图像必须适合的边界框。
查找哪个更小: MaxWidth / w
或MaxHeight / h
然后乘以该数字w
和h
说明:
您需要find使图像适合的比例因子。
要find缩放因子s
,对于宽度,则s
必须是: s * w = MaxWidth
。 因此,缩放因子是MaxWidth / w
。
同样的高度。
需要最大比例(较小)的那个是您必须缩放整个图像的因素。
基于埃里克的build议,我会做这样的事情:
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; }
我可能在演员阵容上有点过头了,但我只是想在计算中保持精确度。
要执行方面填充而不是方面拟合,请改用较大的比例。 也就是说,将Matt的代码从Math.Min改为Math.Max。
(纵横填充不会将边界框留空,但可以将一些图像放在边界之外,而一个方面不允许边界外的图像离开,但可能会使边界框的一部分为空。
尝试了沃伦先生的代码,但没有产生可靠的结果。
例如,
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 ) ); }
以下代码产生更准确的结果:
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)) ); }
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)
非常简单。 :)问题是要find一个因素,你需要乘以宽度和高度。 解决办法是尝试使用一个,如果不适合,使用另一个。 所以…
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)
我也遇到类似的问题,我发现非常有用: 文章 。 正如我理解正确,你需要调整图像?
根据以前的答案,这里有一个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 }; }