종횡비를 계산하는 알고리즘은 무엇입니까?


87

전체 창에 맞게 이미지를 자르기 위해 JavaScript와 함께 사용할 계획입니다.

편집 : 4:3, 16:9.


이 질문에 빠진 부분이있는 것 같습니다. 이미 소스 종횡비를 알고 있다면 .. q 제목이 이해가되지 않습니다.
Gishu

"창"이라고하면 "화면"을 의미합니까?
Nosredna

사실, 나는 필요합니다 : 이미지를 창에 맞게 만들고 ajax를 통해 데이터베이스에 종횡비를 보냅니다.
Nathan

글쎄, 창문은 펑키 한 크기가 될 수 있습니다. 창을 대부분 수직으로 만들 수 있습니다.
Nosredna

내 잘못은 이미지를 화면에 맞게 만드는 것입니다. (사용자는 배경 화면으로 사용합니다)
나단

답변:


203

나는 당신이 integer:integer같은 솔루션 16:9보다는 같은 사용 가능한 종횡비 솔루션을 찾고 있다고 float:1생각 1.77778:1합니다.

그렇다면 가장 큰 공약수 (GCD)를 찾아 두 값을 그 값으로 나누면됩니다. GCD는 두 숫자를 균등하게 나누는 가장 높은 숫자입니다. 따라서 6과 10의 GCD는 2이고 44와 99의 GCD는 11입니다.

예를 들어 1024x768 모니터의 GCD는 256입니다. 두 값을 모두 나누면 4x3 또는 4 : 3이됩니다.

(재귀) 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.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>

내 이상한 와이드 스크린 모니터에서 다음과 같이 출력됩니다.

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

마지막으로 집에 있었으면 좋겠는데, 안타깝게도 작업 기계입니다.

그래픽 크기 조정 도구에서 가로 세로 비율을 지원하지 않는 경우 수행 할 작업은 또 다른 문제입니다. 레터 박스 라인을 추가하는 것이 최선의 방법이라고 생각합니다 (예 : 와이드 스크린 영화를 볼 때 기존 TV의 상단과 하단에 표시되는 라인). 이미지가 요구 사항을 충족 할 때까지 상단 / 하단 또는 측면 (둘 중 가장 적은 수의 레터 박스 라인이 생성됨)에 추가합니다.

고려할 수있는 한 가지 사항은 16 : 9에서 5 : 4로 변경된 사진의 품질입니다. 저는 레터 박스가 소개되기 전에 어린 시절 텔레비전에서 보던 엄청나게 키가 크고 얇은 카우보이를 기억합니다. 종횡비 당 하나의 다른 이미지를 사용하는 것이 더 좋을 수 있으며 유선으로 보내기 전에 실제 화면 크기에 맞게 올바른 크기를 조정하면됩니다.


1
이것이 제가 제공하려고 생각한 첫 번째 대답이지만, 예를 들어 창 크기가 1021x711과 같은 크기로 조정되면 타사 구성 요소에 유용한 결과를 반환하지 않을까 걱정했습니다.
Nosredna

2
과잉처럼 보입니다. 그리고 Nosredna가 언급 한 경우에는 작동하지 않습니다. 근사치를 기반으로 한 솔루션이 있습니다.
Chetan S

1
제 고객은 시청자의 가로 세로 비율이 필요하다고 말했습니다. 인쇄소를위한 서비스입니다. 그 통계에 대한 생각
나단

1
테스트 케이스 : 728x90-> 364:45오전하지 않도록 원하는 결과입니다
라파엘 Herscovici

@Dementic, 분획, 따라서 올바른 종횡비 및 (영업 포함) (158), 다른 사람의 간단한 형태 :-) 동의 같다. 무엇이 더 좋을지에 대한 다른 아이디어가있는 경우 알려 주시면 답변을 조정하겠습니다.
paxdiablo

56
aspectRatio = width / height

그것이 당신이 추구하는 것이라면. 그런 다음 대상 공간의 차원 중 하나를 곱하여 비율을 유지하는 다른 하나를 찾을 수 있습니다.

widthT = heightT * aspectRatio
heightT = widthT / aspectRatio

13

paxdiablo의 대답은 훌륭하지만 주어진 방향으로 몇 개의 픽셀이 더 많거나 적은 일반적인 해상도가 많이 있으며 최대 공약수 접근 방식은 끔찍한 결과를 제공합니다.

예를 들어 gcd 접근 방식을 사용하여 멋진 16 : 9 비율을 제공하는 1360x765의 잘 작동하는 해상도를 고려하십시오. Steam에 따르면이 해상도는 사용자의 0.01 % 만 사용하는 반면 1366x768은 무려 18.9 %가 사용합니다. gcd 접근 방식을 사용하여 얻을 수있는 내용을 살펴 보겠습니다.

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

이 683 : 384 비율을 가장 가까운 16 : 9 비율로 반올림하고 싶습니다.

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 년 10 월 기준).

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

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

1
귀하의 솔루션은 4x3 및 16x9에 적합하지만 가능한 모든 종횡비를 지원하지는 않는 것 같습니다 (OP에는 중요하지 않을 수도 있음). 예를 들어 대부분의 와이드 스크린 모니터의 비율은 16x10 (1920x1200, 1600x1000)입니까?
Falaina 2009-07-27

우리는 질문에 잘 답할 수있는 충분한 정보가 없습니다. :-)
Nosredna

4

다음은 종횡비 계산 코드 에서 자바 스크립트로 포팅 된 조정 가능한 퍼지 수준이있는 James Farey의 최고의 합리적인 근사 알고리즘 버전입니다. 원래 파이썬으로 작성된 .

이 메서드는 width/height분수 분자 / 분모에 대한 부동 소수점 ( )과 상한을 사용합니다.

아래 예제에서는 다른 답변에 나열된 일반 알고리즘을 사용하는 대신 (1.77835051546) 을 (1.77777777778) 로 처리 50해야하기 때문에 상한을 설정하고 1035x582있습니다 .16:9345:194gcd

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

3

당신이 공연 광이라면 ...

직사각형 비율을 계산하는 가장 빠른 방법 (JavaScript에서)은 진정한 이진 Great Common Divisor 알고리즘을 사용합니다.

(모든 속도 및 타이밍 테스트는 다른 사람이 수행했습니다. 여기에서 하나의 벤치 마크를 확인할 수 있습니다 : 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]
*/


2

여기 내 솔루션이 있습니다. 내가 신경 쓰는 모든 것이 반드시 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'
        return find_closest_ratio(divided)
    except TypeError:
        return None

1

GCD 검색에 대한 대안으로 표준 값 세트와 비교하여 확인하는 것이 좋습니다. Wikipedia 에서 목록을 찾을 수 있습니다 .


1

여기에서 비디오에 대해 이야기하고 있다고 가정합니다.이 경우 소스 비디오의 픽셀 종횡비에 대해 걱정해야 할 수도 있습니다. 예를 들면.

PAL DV는 720x576의 해상도로 제공됩니다. 4 : 3처럼 보일 것입니다. 이제 픽셀 종횡비 (PAR)에 따라 화면 비율은 4 : 3 또는 16 : 9가 될 수 있습니다.

자세한 내용은 여기를 참조하십시오. http://en.wikipedia.org/wiki/Pixel_aspect_ratio

정사각형 픽셀 종횡비를 얻을 수 있으며 많은 웹 비디오가 그렇습니다.하지만 다른 경우를보고 싶을 수도 있습니다.

도움이 되었기를 바랍니다


1

다른 답변을 바탕으로 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

가로 세로 비율은 너비를 높이로 나눈 것이라고 생각합니다.

 r = w/h


0

파이썬의이 알고리즘 은 당신을 거기에 데려다줍니다.


창문이 우스운 크기이면 어떻게되는지 말해주세요.

아마도 당신이 가져야 할 것은 (타사 구성 요소에 대한) 모든 허용 가능한 비율의 목록입니다. 그런 다음 창과 가장 일치하는 항목을 찾아 목록에서 해당 비율을 반환합니다.


0

이 작업을 수행하는 약간의 이상한 방법이지만 해상도를 측면으로 사용하십시오. EG

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

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

제 경우에는 다음과 같은 것을 원합니다

[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

일반적인 종횡비를 기반으로 룩업 테이블을 만들어 언제든지 시작할 수 있습니다. https://en.wikipedia.org/wiki/Display_aspect_ratio 확인 그런 다음 간단히 분할 할 수 있습니다.

실제 문제의 경우 다음과 같이 할 수 있습니다.

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

그런 다음 임의의 순서로 치수를 제공합니다.

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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.