数学/算法:使图片适应屏幕并保持纵横比

56

我需要数学/算法方面的帮助,将一个已知尺寸的图像适配到两个屏幕尺寸之一:

720 x 480 或者 1280 x 1024。

图像尺寸来自 XML 文件,但这些尺寸是网页尺寸,我还从 XML 中获取了一组可能比网页尺寸更高或更低分辨率的图像。

我想要的是使用网页尺寸的纵横比,在高清 (1280x720) 屏幕上显示更高分辨率的图像(如果有),或者在标清 (720x480) 屏幕上显示该图像。如果我知道图像的分辨率在两个维度上都小于标清屏幕的分辨率(在这种情况下,我只知道图像文件的网页尺寸和水平尺寸),则在该屏幕上按实际大小显示它也会很有用。


你能否发布样本数据以及你期望的结果是什么? - svick
好的,这里有一些真实的示例数据:web dimension= width="340" height="517",其中如果有图片可用,则x维度实际上是1280。我想将其调整大小,使其高度不超过720,并等比例缩小宽度,以便在高清屏幕上显示图像,或者如果用户使用的是SD屏幕,则该尺寸就是我要缩放到的尺寸。输出将为targetrect={x,y,xx,yy}。 - alphablender
https://math.stackexchange.com/a/180809/210893 - Stack Underflow
4个回答

180

非常通用:

图像数据:(wi,hi),并定义ri= wi/ hi
屏幕分辨率:(ws,hs),并定义rs= ws/ hs

缩放后的图像尺寸:

rs > ri ? (wi * hs/hi, hs) : (ws, hi * ws/wi)

所以,例如:

         20
|------------------|
    10
|---------|

--------------------     ---   ---
|         |        |      | 7   |
|         |        |      |     | 10
|----------        |     ---    |
|                  |            |
--------------------           ---

ws = 20
hs = 10
wi = 10
hi = 7

20/10 > 10/7 ==> (wi * hs/hi, hs) = (10 * 10/7, 10) = (100/7, 10) ~ (14.3, 10)

正如您可以清晰地看到,该图像可以根据屏幕尺寸进行缩放,因为高度是屏幕的高度,但它明显保持了纵横比,因为 14.3/10 ~ 10/7

更新

按以下方式将图像居中:

将 (wnew, hnew) 称为新的尺寸。

top = (hs - hnew)/2
left = (ws - wnew)/2

2
@alphablender,不是数组,i代表图像,s代表屏幕。 - davin
这看起来有点像您所描述的吗?:imagewidth = x imageheight = y imageratio = imagewidth / imageheightscreenwidth = 1280 screenheight = 720screenratio = screenwidth / screenheight如果 screenratio 大于 imageratio,则resultwidth = imagewidth * screenheight / imageheight resultheight = screenheight否则resultwidth = screenwidth resultheight = imageheight * screenwidth / imagewidth结束如果 - alphablender
那个表达式解释了一切。整洁而有用! - Au Ris
谢谢!我在Swift中混合了这段代码:https://dev59.com/63E85IYBdhLWcg3wr1ke#30519019... 但是我发现通过反转“>”,我能够得到“填充”而不是“letterbox”。这是非常糟糕的测试结果,还是正确的呢? - Dan Rosenstark
如果您将条件反转,像这样 rs < ri,它将执行 fillfit --> rs > ri ? (wi * hs/hi, hs) : (ws, hi * ws/wi)fill --> rs < ri ? (wi * hs/hi, hs) : (ws, hi * ws/wi) - mehdok
显示剩余4条评论

31

我理解被接受的答案并且它行得通,但是我一直发现以下方法对于“最佳匹配”更加简单和简洁:

// prep
let maxWidth = 190,
    maxHeight = 150;
let imgWidth = img.width,
    imgHeight = img.height;

// calc
let widthRatio = maxWidth / imgWidth,
    heightRatio = maxHeight / imgHeight;
let bestRatio = Math.min(widthRatio, heightRatio);

// output
let newWidth = imgWidth * bestRatio,
    newHeight = imgHeight * bestRatio;

...当然,这可以被概括为:

const maxWidth = 190, maxHeight = 150;
const bestRatio = Math.min(maxWidth / img.width, maxHeight / img.height);
img.width *= bestRatio;
img.height *= bestRatio;

很棒的答案。让人容易理解算法实际在做什么。 - Keith Pickering

12

这是简单的C代码。

你想通过返回的比例因子来缩放两个坐标。

/* 计算一个矩形在屏幕中的缩放比例系数,使得该矩形可以缩放而不产生拉伸或压缩。*/
float 
aspect_correct_scale_for_rect(const float screen[2], const float rect[2])
{
    float screenAspect = screen[0] / screen[1];
    float rectAspect = rect[0] / rect[1];
float scaleFactor; if (screenAspect > rectAspect) scaleFactor = screen[1] / rect[1]; else scaleFactor = screen[0] / rect[0];
return scaleFactor; }

选择高度和宽度比率与纵横比<或>之间的关系。 - Buddhika

5

使用Letterboxing或适应屏幕进行宽高比校正

最近我写了一种在iOS上处理这个确切问题的方法。我使用Eigen矩阵库进行缩放,但是原理(缩放因子)没有矩阵也是相同的。

Eigen::Matrix4x4f aspectRatioCorrection(bool fillScreen, const Eigen::Vector2f &screenSize, const Eigen::Vector2f &imageSize)
{
    Eigen::Matrix4x4f scalingMatrix(Eigen::Matrix4x4f::Identity());

    float screenWidth = screenSize.x();
    float screenHeight = screenSize.y();
    float screenAspectRatio = screenWidth / screenHeight;
    float imageWidth = imageSize.x();
    float imageHeight = imageSize.y();
    float imageAspectRatio = imageWidth / imageHeight;

    float scalingFactor;
    if (fillScreen) {
        if (screenAspectRatio > imageAspectRatio) {
            scalingFactor = screenWidth / imageWidth;
        } else {
            scalingFactor = screenHeight / imageHeight;
        }
    } else {
        if (screenAspectRatio > imageAspectRatio) {
            scalingFactor =  screenHeight / imageHeight;
        } else {
            scalingFactor = screenWidth / imageWidth;
        }
    }

    scalingMatrix(0, 0) = scalingFactor;
    scalingMatrix(1, 1) = scalingFactor;

    return scalingMatrix;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接