Каков алгоритм расчета соотношения сторон? Мне нужен результат: 4:3, 16:9


Я планирую использовать его с javascript, чтобы обрезать изображение, чтобы соответствовать всему окну.

Edit: я буду использовать компонент 3-й части, который принимает только соотношение сторон в формате, например: 4:3, 16:9

15 62

15 ответов:

Я так понимаю, вы ищете полезное соотношение сторон integer:integer решение, как 16:9, а не float:1 решение, как 1.77778:1.

если это так, то вам нужно найти наибольший общий делитель (GCD) и разделить оба значения на это. НОД-это наибольшее число, которое нацело делит оба числа. Поэтому нод для 6 и 10-это 2, НОД по 44 и 99, 11.

например, монитор 1024x768 имеет GCD 256. Когда вы делите оба значения на то, что вы получаете 4x3 или 4: 3.

a (рекурсивный) алгоритм GCD:

function gcd (a,b):
    if b == 0:
        return a
    return gcd (b, a mod b)

В C:

static int gcd (int a, int b) {
    return (b == 0) ? a : gcd (b, a%b);
}

int main(void) {
    printf ("gcd(1024,768) = %d\n",gcd(1024,768));
}

и вот некоторые полные HTML / Javascript, который показывает один из способов определить размер экрана и вычислить соотношение сторон от этого. Это работает в FF3, я не уверен, что поддержка других браузеров есть для screen.width и screen.height.

<html><body>
    <script type="text/javascript">
        function gcd (a, b) {
            return (b == 0) ? a : gcd (b, a%b);
        }
        var w = screen.width;
        var h = screen.height;
        var r = gcd (w, h);
        document.write ("<pre>");
        document.write ("Dimensions = ", w, " x ", h, "<br>");
        document.write ("Gcd        = ", r, "<br>");
        document.write ("Aspect     = ", w/r, ":", h/r);
        document.write ("</pre>");
    </script>
</body></html>

он выводит (на моем странном широкоэкранном мониторе):

Dimensions = 1680 x 1050
Gcd        = 210
Aspect     = 8:5

другие, что я проверил это на:

Dimensions = 1280 x 1024
Gcd        = 256
Aspect     = 5:4

Dimensions = 1152 x 960
Gcd        = 192
Aspect     = 6:5

Dimensions = 1280 x 960
Gcd        = 320
Aspect     = 4:3

Dimensions = 1920 x 1080
Gcd        = 120
Aspect     = 16:9

жаль, что у меня не было этого последнего дома, но, к сожалению, это рабочая машина.

что вы делаете, если вы обнаружите, что соотношение сторон не поддерживается вашим графическим инструментом изменения размера-это другое дело. Я подозреваю, что лучше всего было бы добавить буквы-боксерские линии (например, те, которые вы получаете в верхней и нижней части вашего старого телевизора, когда вы смотрите на нем широкоэкранный фильм). Я бы добавил их сверху / снизу или по бокам (в зависимости от того, какой результат меньше всего количество черных линий), пока изображение не соответствует требованиям.

aspectRatio = width / height

если это то, что вам нужно. Затем вы можете умножить его на одно из измерений целевого пространства, чтобы узнать другое (что поддерживает соотношение) например,

widthT = heightT * aspectRatio
heightT = widthT / aspectRatio

ответ paxdiablo велик, но есть много общих разрешений, которые имеют всего несколько более или менее пикселей в заданном направлении, и самый большой общий подход к делителю дает им ужасные результаты.

возьмите, например, хорошее разрешение 1360x765, которое дает хорошее соотношение 16:9 с использованием подхода gcd. Согласно Steam, это разрешение используется только 0,01% его пользователей, в то время как 1366x768 используется whoping 18,9%. Давайте посмотрим, что мы получаем с помощью НОД подход:

1360x765 - 16:9 (0.01%)
1360x768 - 85:48 (2.41%)
1366x768 - 683:384 (18.9%)

мы хотели бы округлить это соотношение 683:384 до ближайшего, 16:9 соотношение.

Я написал скрипт python, который анализирует текстовый файл с вставленными номерами со страницы обзора оборудования Steam и печатает все разрешения и самые близкие известные отношения, а также распространенность каждого отношения (что было моей целью, когда я начал это):

# Contents pasted from store.steampowered.com/hwsurvey, section 'Primary Display Resolution'
steam_file = './steam.txt'

# Taken from http://upload.wikimedia.org/wikipedia/commons/thumb/f/f0/Vector_Video_Standards4.svg/750px-Vector_Video_Standards4.svg.png
accepted_ratios = ['5:4', '4:3', '3:2', '8:5', '5:3', '16:9', '17:9']

#-------------------------------------------------------
def gcd(a, b):
    if b == 0: return a
    return gcd (b, a % b)

#-------------------------------------------------------
class ResData:

    #-------------------------------------------------------
    # Expected format: 1024 x 768 4.37% -0.21% (w x h prevalence% change%)
    def __init__(self, steam_line):
        tokens = steam_line.split(' ')
        self.width  = int(tokens[0])
        self.height = int(tokens[2])
        self.prevalence = float(tokens[3].replace('%', ''))

        # This part based on pixdiablo's gcd answer - http://stackoverflow.com/a/1186465/828681
        common = gcd(self.width, self.height)
        self.ratio = str(self.width / common) + ':' + str(self.height / common)
        self.ratio_error = 0

        # Special case: ratio is not well behaved
        if not self.ratio in accepted_ratios:
            lesser_error = 999
            lesser_index = -1
            my_ratio_normalized = float(self.width) / float(self.height)

            # Check how far from each known aspect this resolution is, and take one with the smaller error
            for i in range(len(accepted_ratios)):
                ratio = accepted_ratios[i].split(':')
                w = float(ratio[0])
                h = float(ratio[1])
                known_ratio_normalized = w / h
                distance = abs(my_ratio_normalized - known_ratio_normalized)
                if (distance < lesser_error):
                    lesser_index = i
                    lesser_error = distance
                    self.ratio_error = distance

            self.ratio = accepted_ratios[lesser_index]

    #-------------------------------------------------------
    def __str__(self):
        descr = str(self.width) + 'x' + str(self.height) + ' - ' + self.ratio + ' - ' + str(self.prevalence) + '%'
        if self.ratio_error > 0:
            descr += ' error: %.2f' % (self.ratio_error * 100) + '%'
        return descr

#-------------------------------------------------------
# Returns a list of ResData
def parse_steam_file(steam_file):
    result = []
    for line in file(steam_file):
        result.append(ResData(line))
    return result

#-------------------------------------------------------
ratios_prevalence = {}
data = parse_steam_file(steam_file)

print('Known Steam resolutions:')
for res in data:
    print(res)
    acc_prevalence = ratios_prevalence[res.ratio] if (res.ratio in ratios_prevalence) else 0
    ratios_prevalence[res.ratio] = acc_prevalence + res.prevalence

# Hack to fix 8:5, more known as 16:10
ratios_prevalence['16:10'] = ratios_prevalence['8:5']
del ratios_prevalence['8:5']

print('\nSteam screen ratio prevalences:')
sorted_ratios = sorted(ratios_prevalence.items(), key=lambda x: x[1], reverse=True)
for value in sorted_ratios:
    print(value[0] + ' -> ' + str(value[1]) + '%')

для любопытных, это Распространенность коэффициентов экрана среди пользователей Steam (по состоянию на октябрь 2012):

16:9 -> 58.9%
16:10 -> 24.0%
5:4 -> 9.57%
4:3 -> 6.38%
5:3 -> 0.84%
17:9 -> 0.11%

Я думаю, вы хотите решить, какой из 4:3 и 16:9 лучше всего подходит.

function getAspectRatio(width, height) {
    var ratio = width / height;
    return ( Math.abs( ratio - 4 / 3 ) < Math.abs( ratio - 16 / 9 ) ) ? '4:3' : '16:9';
}

Я думаю, что это делает то, что вы просите:

webdeveloper.com -десятичная дробь

Width / height получает десятичную дробь, преобразованную в дробь с": "вместо" / "дает вам "отношение".

этот алгоритм в Python получает вас часть пути там.


скажите мне, что происходит, если окна смешного размера.

может быть, то, что вы должны иметь, это список всех приемлемых соотношений (к компоненту 3rd party). Затем найдите ближайшее совпадение с вашим окном и верните это соотношение из списка.

в качестве альтернативного решения для поиска GCD я предлагаю вам проверить набор стандартных значений. Вы можете найти список на Википедия.

Я предполагаю, что вы говорите о видео здесь, и в этом случае вам также может понадобиться беспокоиться о соотношении сторон пикселей исходного видео. Например.

PAL DV поставляется в разрешении 720x576. Что выглядело бы как его 4:3. Теперь в зависимости от соотношения сторон пикселей (PAR) соотношение экрана может быть либо 4:3, либо 16:9.

для получения дополнительной информации смотрите здесь http://en.wikipedia.org/wiki/Pixel_aspect_ratio

вы можете получить квадратный пиксельный аспект Соотношение, и много веб-видео является то, что, но вы можете посмотреть из других случаев.

надеюсь, что это помогает

Марк

основываясь на других ответах, вот как я получил нужные мне числа в Python;

from decimal import Decimal

def gcd(a,b):
    if b == 0:
        return a
    return gcd(b, a%b)

def closest_aspect_ratio(width, height):
    g = gcd(width, height)
    x = Decimal(str(float(width)/float(g)))
    y = Decimal(str(float(height)/float(g)))
    dec = Decimal(str(x/y))
    return dict(x=x, y=y, dec=dec)

>>> closest_aspect_ratio(1024, 768)
{'y': Decimal('3.0'), 
 'x': Decimal('4.0'), 
 'dec': Decimal('1.333333333333333333333333333')}

на всякий случай, если вы помешаны на производительности...

самый быстрый способ (в JavaScript) вычислить отношение прямоугольника это использовать истинный двоичный большой общий алгоритм делителя.

(все тесты скорости и времени были сделаны другими, вы можете проверить один тест здесь: https://lemire.me/blog/2013/12/26/fastest-way-to-compute-the-greatest-common-divisor/)

вот это:

/* the binary Great Common Divisor calculator */
function gcd (u, v) {
    if (u === v) return u;
    if (u === 0) return v;
    if (v === 0) return u;

    if (~u & 1)
        if (v & 1)
            return gcd(u >> 1, v);
        else
            return gcd(u >> 1, v >> 1) << 1;

    if (~v & 1) return gcd(u, v >> 1);

    if (u > v) return gcd((u - v) >> 1, v);

    return gcd((v - u) >> 1, u);
}

/* returns an array with the ratio */
function ratio (w, h) {
	var d = gcd(w,h);
	return [w/d, h/d];
}

/* example */
var r1 = ratio(1600, 900);
var r2 = ratio(1440, 900);
var r3 = ratio(1366, 768);
var r4 = ratio(1280, 1024);
var r5 = ratio(1280, 720);
var r6 = ratio(1024, 768);


/* will output this: 
r1: [16, 9]
r2: [8, 5]
r3: [683, 384]
r4: [5, 4]
r5: [16, 9]
r6: [4, 3]
*/
Width / Height

?

Я считаю, что соотношение ширины к высоте.

 r = w/h

немного странный способ сделать это, но использовать разрешение в качестве аспекта. Г. Е.

1024:768

или можно попробовать

var w = screen.width;
var h = screen.height;
for(var i=1,asp=w/h;i<5000;i++){
  if(asp*i % 1==0){
    i=9999;
    document.write(asp*i,":",1*i);
  }
}

вот мое решение это довольно прямо вперед, так как все, что меня волнует, это не обязательно GCD или даже точные соотношения: потому что тогда вы получаете странные вещи, такие как 345/113, которые не понятны человеку.

Я в основном устанавливаю приемлемые ландшафтные или портретные отношения и их "значение" как поплавок... Затем я сравниваю свою плавающую версию отношения к каждому и которая когда-либо имела самую низкую разницу абсолютных значений, является отношением, ближайшим к элементу. Таким образом, когда пользователь делает это 16: 9 но затем удаляет 10 пикселей снизу он по-прежнему считается 16:9...

accepted_ratios = {
    'landscape': (
        (u'5:4', 1.25),
        (u'4:3', 1.33333333333),
        (u'3:2', 1.5),
        (u'16:10', 1.6),
        (u'5:3', 1.66666666667),
        (u'16:9', 1.77777777778),
        (u'17:9', 1.88888888889),
        (u'21:9', 2.33333333333),
        (u'1:1', 1.0)
    ),
    'portrait': (
        (u'4:5', 0.8),
        (u'3:4', 0.75),
        (u'2:3', 0.66666666667),
        (u'10:16', 0.625),
        (u'3:5', 0.6),
        (u'9:16', 0.5625),
        (u'9:17', 0.5294117647),
        (u'9:21', 0.4285714286),
        (u'1:1', 1.0)
    ),
}


def find_closest_ratio(ratio):
    lowest_diff, best_std = 9999999999, '1:1'
    layout = 'portrait' if ratio < 1.0 else 'landscape'
    for pretty_str, std_ratio in accepted_ratios[layout]:
        diff = abs(std_ratio - ratio)
        if diff < lowest_diff:
            lowest_diff = diff
            best_std = pretty_str
    return best_std


def extract_ratio(width, height):
    try:
        divided = float(width)/float(height)
        if divided == 1.0:
            return '1:1'
        else:
            return find_closest_ratio(divided)
    except TypeError:
        return None

вот версия лучшего алгоритма рациональной аппроксимации Джеймса Фари с регулируемым уровнем нечеткости, перенесенным на javascript из код расчета соотношения сторон первоначально написано на python.

метод принимает тип float (width/height) и верхний предел для числителя/знаменателя дроби.

в примере ниже я устанавливаю верхний предел 50 потому что мне нужно 1035x582 (1.77835051546) следует рассматривать как 16:9 (1.77777777778), а не 345:194 что вы получаете с обычного gcd алгоритм, указанный в других ответах.

<html>
<body>
<script type="text/javascript">
function aspect_ratio(val, lim) {

    var lower = [0, 1];
    var upper = [1, 0];

    while (true) {
        var mediant = [lower[0] + upper[0], lower[1] + upper[1]];

        if (val * mediant[1] > mediant[0]) {
            if (lim < mediant[1]) {
                return upper;
            }
            lower = mediant;
        } else if (val * mediant[1] == mediant[0]) {
            if (lim >= mediant[1]) {
                return mediant;
            }
            if (lower[1] < upper[1]) {
                return lower;
            }
            return upper;
        } else {
            if (lim < mediant[1]) {
                return lower;
            }
            upper = mediant;
        }
    }
}

document.write (aspect_ratio(800 / 600, 50) +"<br/>");
document.write (aspect_ratio(1035 / 582, 50) + "<br/>");
document.write (aspect_ratio(2560 / 1440, 50) + "<br/>");

    </script>
</body></html>

результат:

 4,3  // (1.33333333333) (800 x 600)
 16,9 // (1.77777777778) (2560.0 x 1440)
 16,9 // (1.77835051546) (1035.0 x 582)