Каков алгоритм расчета соотношения сторон? Мне нужен результат: 4:3, 16:9
Я планирую использовать его с javascript, чтобы обрезать изображение, чтобы соответствовать всему окну.
Edit: я буду использовать компонент 3-й части, который принимает только соотношение сторон в формате, например: 4:3, 16:9
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] */
немного странный способ сделать это, но использовать разрешение в качестве аспекта. Г. Е.
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)