Thuật toán để tính toán tỷ lệ khung hình là gì?


87

Tôi dự định sử dụng nó với JavaScript để cắt một hình ảnh cho vừa với toàn bộ cửa sổ.

Chỉnh sửa : Tôi sẽ sử dụng thành phần của bên thứ 3 chỉ chấp nhận tỷ lệ khung hình ở định dạng như: 4:3, 16:9.


Có vẻ như còn thiếu một phần trong câu hỏi này. Nếu bạn đã biết tỷ lệ khung hình nguồn .. tiêu đề của q không có ý nghĩa đối với tôi.
Gishu

Khi bạn nói "cửa sổ", bạn có nghĩa là "màn hình?"
Nosredna

Trên thực tế, tôi cần: làm cho hình ảnh vừa với cửa sổ, gửi qua ajax tỷ lệ khung hình tới cơ sở dữ liệu.
Nathan

Chà, cửa sổ có thể có kích thước bất kỳ, đúng không? Họ có thể làm cho cửa sổ gần như thẳng đứng.
Nosredna

Ý tôi là làm cho hình ảnh phù hợp với màn hình. (Người dùng sẽ sử dụng nó làm hình nền)
Nathan

Câu trả lời:


203

Tôi hiểu rằng bạn đang tìm kiếm một integer:integergiải pháp tỷ lệ khung hình có thể sử dụng được 16:9hơn là một float:1giải pháp như thế 1.77778:1.

Nếu vậy, những gì bạn cần làm là tìm ước số chung lớn nhất (GCD) và chia cả hai giá trị cho đó. GCD là số cao nhất chia đều cả hai số. Vì vậy, GCD cho 6 và 10 là 2, GCD cho 44 và 99 là 11.

Ví dụ: màn hình 1024x768 có GCD là 256. Khi bạn chia cả hai giá trị đó, bạn nhận được 4x3 hoặc 4: 3.

Thuật toán GCD (đệ quy):

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

Trong 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));
}

Và đây là một số HTML / Javascript hoàn chỉnh cho thấy một cách để phát hiện kích thước màn hình và tính toán tỷ lệ khung hình từ đó. Điều này hoạt động trong FF3, tôi không chắc các trình duyệt khác có hỗ trợ gì screen.widthscreen.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>

Nó xuất ra (trên màn hình rộng kỳ lạ của tôi):

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

Những người khác mà tôi đã thử nghiệm điều này:

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

Tôi ước mình có cái cuối cùng đó ở nhà nhưng, không, thật không may, đó là một chiếc máy làm việc.

Bạn phải làm gì nếu phát hiện ra tỷ lệ khung hình không được công cụ thay đổi kích thước đồ họa của bạn hỗ trợ là một vấn đề khác. Tôi nghi ngờ cách tốt nhất là nên thêm các dòng đấm bốc chữ cái (như những dòng bạn nhận được ở đầu và cuối TV cũ khi bạn đang xem phim màn hình rộng trên đó). Tôi sẽ thêm chúng ở trên cùng / dưới cùng hoặc ở hai bên (tùy theo điều kiện nào dẫn đến số dòng chữ-boxing ít nhất) cho đến khi hình ảnh đáp ứng yêu cầu.

Một điều bạn có thể muốn xem xét là chất lượng của hình ảnh đã được thay đổi từ 16: 9 thành 5: 4 - Tôi vẫn nhớ những anh chàng cao bồi cực kỳ cao gầy mà tôi từng xem hồi trẻ trên truyền hình trước khi môn đấm bốc được giới thiệu. Tốt hơn là bạn nên có một hình ảnh khác nhau trên mỗi tỷ lệ khung hình và chỉ cần thay đổi kích thước hình ảnh chính xác cho kích thước màn hình thực tế trước khi gửi nó xuống dây.


1
Đây là câu trả lời đầu tiên tôi nghĩ đến, nhưng tôi lo lắng rằng nó sẽ không trả lại kết quả hữu ích cho thành phần bên thứ 3 của anh ấy nếu cửa sổ của anh ấy có kích thước như 1021x711 chẳng hạn.
Nosredna 27/07/09

2
Có vẻ như một sự quá mức cần thiết. Và nó không hoạt động với những trường hợp mà Nosredna đã đề cập. Tôi có một giải pháp dựa trên sự gần đúng.
Chetan S

1
Khách hàng của tôi nói với tôi rằng anh ta cần tỷ lệ khung hình của người xem. Nó là một dịch vụ cho một cửa hàng in. Tôi nghĩ đó là số liệu thống kê
Nathan

1
trường hợp thử nghiệm: 728x90-> 364:45không chắc chắn đó là kết quả mong muốn
Rafael Herscovici

@Dementic, đó dạng đơn giản nhất của phân số, do đó có tỷ lệ khung hình chính xác và 158 người khác (bao gồm cả OP) dường như đồng ý :-). Nếu bạn có một số ý tưởng khác về cách tốt hơn, vui lòng cho tôi biết và tôi sẽ xem xét điều chỉnh câu trả lời.
paxdiablo

56
aspectRatio = width / height

nếu đó là những gì bạn đang theo đuổi. Sau đó, bạn có thể nhân nó với một trong các kích thước của không gian đích để tìm ra kích thước còn lại (duy trì tỷ lệ), ví dụ

widthT = heightT * aspectRatio
heightT = widthT / aspectRatio

13

Câu trả lời của paxdiablo là tuyệt vời, nhưng có rất nhiều độ phân giải phổ biến chỉ có một vài pixel nhiều hơn hoặc ít hơn theo một hướng nhất định và phương pháp ước số chung lớn nhất mang lại kết quả khủng khiếp cho chúng.

Lấy ví dụ, độ phân giải hoạt động tốt là 1360x765 mang lại tỷ lệ 16: 9 đẹp bằng cách sử dụng phương pháp gcd. Theo Steam, độ phân giải này chỉ được 0,01% người dùng sử dụng, trong khi 1366x768 được sử dụng bởi 18,9%. Hãy xem những gì chúng ta nhận được khi sử dụng phương pháp gcd:

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

Chúng tôi muốn làm tròn tỷ lệ 683: 384 đó thành tỷ lệ 16: 9 gần nhất.

Tôi đã viết tập lệnh python phân tích cú pháp tệp văn bản với các số được dán từ trang khảo sát Phần cứng Steam và in tất cả các độ phân giải và tỷ lệ đã biết gần nhất, cũng như mức độ phổ biến của từng tỷ lệ (đó là mục tiêu của tôi khi tôi bắt đầu điều này):

# 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]) + '%')

Đối với những người tò mò, đây là tỷ lệ màn hình phổ biến giữa những người dùng Steam (tính đến tháng 10 năm 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%

11

Tôi đoán bạn muốn quyết định xem 4: 3 và 16: 9 nào là phù hợp nhất.

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

1
Mặc dù giải pháp của bạn là tốt cho 4x3 và 16x9, nhưng điều này có vẻ như không hỗ trợ tất cả các tỷ lệ khung hình có thể có (mặc dù có thể điều đó không quan trọng đối với OP). Ví dụ: tỷ lệ cho hầu hết các màn hình rộng là 16x10 (1920x1200, 1600x1000)?
Falaina

Chúng tôi thực sự không có đủ thông tin để trả lời tốt câu hỏi. :-)
Nosredna

4

Đây là phiên bản của thuật toán xấp xỉ hợp lý tốt nhất của James Farey với mức độ mờ có thể điều chỉnh được được chuyển sang javascript từ mã tính toán tỷ lệ khung hình ban đầu được viết bằng python.

Phương thức nhận float ( width/height) và giới hạn trên cho tử số / mẫu số của phân số.

Trong ví dụ dưới đây, tôi đang đặt giới hạn trên 50vì tôi cần 1035x582(1.77835051546) được coi là 16:9(1.77777777778) chứ không phải là giới hạn 345:194bạn nhận được với gcdthuật toán đơn giản được liệt kê trong các câu trả lời khác.

<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>

Kết quả:

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

3

Đề phòng trường hợp bạn là một người không thích trình diễn ...

Cách nhanh nhất (trong JavaScript) để tính tỷ lệ hình chữ nhật là sử dụng thuật toán Great Common Divisor nhị phân thực sự.

(Tất cả các bài kiểm tra tốc độ và thời gian đã được thực hiện bởi những người khác, bạn có thể kiểm tra một điểm chuẩn tại đây: https://lemire.me/blog/2013/12/26/fastest-way-to-compute-the-greatest-common-divisor / )

Nó đây rồi:

/* 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]
*/


2

Đây là giải pháp của tôi, nó khá đơn giản vì tất cả những gì tôi quan tâm không nhất thiết phải là GCD hoặc thậm chí là tỷ lệ chính xác: bởi vì sau đó bạn nhận được những thứ kỳ lạ như 345/113 mà con người không thể hiểu được.

Về cơ bản, tôi đặt tỷ lệ ngang hoặc tỷ lệ dọc có thể chấp nhận được và "giá trị" của chúng dưới dạng giá trị nổi ... Sau đó, tôi so sánh phiên bản tỷ lệ nổi của mình với mỗi tỷ lệ và cái nào có chênh lệch giá trị tuyệt đối thấp nhất là tỷ lệ gần nhất với mục. Theo cách đó, khi người dùng đặt nó thành 16: 9 nhưng sau đó xóa 10 pixel từ dưới cùng thì nó vẫn được tính là 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'
        return find_closest_ratio(divided)
    except TypeError:
        return None

1

Là một giải pháp thay thế cho việc tìm kiếm GCD, tôi khuyên bạn nên kiểm tra với một tập hợp các giá trị tiêu chuẩn. Bạn có thể tìm thấy một danh sách trên Wikipedia .


1

Tôi giả sử bạn đang nói về video ở đây, trong trường hợp đó, bạn có thể cần phải lo lắng về tỷ lệ khung hình pixel của video nguồn. Ví dụ.

PAL DV có độ phân giải 720x576. Nó sẽ giống như 4: 3 của nó. Giờ đây, tùy thuộc vào tỷ lệ khung hình Pixel (PAR), tỷ lệ màn hình có thể là 4: 3 hoặc 16: 9.

Để biết thêm thông tin, hãy xem tại đây http://en.wikipedia.org/wiki/Pixel_aspect_ratio

Bạn có thể nhận được Tỷ lệ khung hình pixel vuông và rất nhiều video trên web là như vậy, nhưng bạn có thể muốn xem các trường hợp khác.

Hi vọng điêu nay co ich

dấu


1

Dựa trên các câu trả lời khác, đây là cách tôi có được các số tôi cần trong 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')}

0

Tôi tin rằng tỷ lệ khung hình là chiều rộng chia cho chiều cao.

 r = w/h


0

Thuật toán này trong Python giúp bạn làm được điều đó.


Hãy cho tôi biết điều gì sẽ xảy ra nếu cửa sổ có kích thước hài hước.

Có thể những gì bạn nên có là danh sách tất cả các tỷ lệ có thể chấp nhận được (đối với thành phần bên thứ 3). Sau đó, tìm kết quả phù hợp nhất với cửa sổ của bạn và trả về tỷ lệ đó từ danh sách.


0

một cách hơi kỳ lạ để làm điều này nhưng sử dụng độ phân giải làm khía cạnh. VÍ DỤ

1024: 768

hoặc bạn có thể thử

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);
  }
}

0
function ratio(w, h) {
    function mdc(w, h) {
        var resto;
        do {
            resto = w % h;

            w = h;
            h = resto;

        } while (resto != 0);

        return w;
    }

    var mdc = mdc(w, h);


    var width = w/mdc;
    var height = h/mdc;

    console.log(width + ':' + height);
}

ratio(1920, 1080);

0

trong trường hợp của tôi, tôi muốn một cái gì đó giống như

[10,5,15,20,25] -> [2, 1, 3, 4, 5]

function ratio(array){
  let min = Math.min(...array);
  let ratio = array.map((element)=>{
    return element/min;
  });
  return ratio;
}
document.write(ratio([10,5,15,20,25]));  // [ 2, 1, 3, 4, 5 ]


0

Bạn luôn có thể bắt đầu bằng cách lập bảng tra cứu dựa trên các tỷ lệ khung hình phổ biến. Kiểm tra https://en.wikipedia.org/wiki/Display_aspect_ratio Sau đó, bạn chỉ cần thực hiện phép chia

Đối với các vấn đề trong cuộc sống thực, bạn có thể làm như sau

let ERROR_ALLOWED = 0.05
let STANDARD_ASPECT_RATIOS = [
  [1, '1:1'],
  [4/3, '4:3'],
  [5/4, '5:4'],
  [3/2, '3:2'],
  [16/10, '16:10'],
  [16/9, '16:9'],
  [21/9, '21:9'],
  [32/9, '32:9'],
]
let RATIOS = STANDARD_ASPECT_RATIOS.map(function(tpl){return tpl[0]}).sort()
let LOOKUP = Object()
for (let i=0; i < STANDARD_ASPECT_RATIOS.length; i++){
  LOOKUP[STANDARD_ASPECT_RATIOS[i][0]] = STANDARD_ASPECT_RATIOS[i][1]
}

/*
Find the closest value in a sorted array
*/
function findClosest(arrSorted, value){
  closest = arrSorted[0]
  closestDiff = Math.abs(arrSorted[0] - value)
  for (let i=1; i<arrSorted.length; i++){
    let diff = Math.abs(arrSorted[i] - value)
    if (diff < closestDiff){
      closestDiff = diff
      closest = arrSorted[i]
    } else {
      return closest
    }
  }
  return arrSorted[arrSorted.length-1]
}

/*
Estimate the aspect ratio based on width x height (order doesn't matter)
*/
function estimateAspectRatio(dim1, dim2){
  let ratio = Math.max(dim1, dim2) / Math.min(dim1, dim2)
  if (ratio in LOOKUP){
    return LOOKUP[ratio]
  }

  // Look by approximation
  closest = findClosest(RATIOS, ratio)
  if (Math.abs(closest - ratio) <= ERROR_ALLOWED){
    return '~' + LOOKUP[closest]
  }

  return 'non standard ratio: ' + Math.round(ratio * 100) / 100 + ':1'
}

Sau đó, bạn chỉ cần cung cấp các kích thước theo thứ tự bất kỳ

estimateAspectRatio(1920, 1080) // 16:9
estimateAspectRatio(1920, 1085) // ~16:9
estimateAspectRatio(1920, 1150) // non standard ratio: 1.65:1
estimateAspectRatio(1920, 1200) // 16:10
estimateAspectRatio(1920, 1220) // ~16:10

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.