2009-07-27 18 views
59

Planeo usarlo con javascript para recortar una imagen para que se ajuste a toda la ventana.¿Cuál es el algoritmo para calcular la relación de aspecto? Necesito una salida como: 4: 3, 16: 9

Editar: Voy a estar utilizando una tercera parte componente que sólo acepta la relación de aspecto en el formato como: 4: 3, 16: 9

+0

Parece que hay una pieza que falta en esta pregunta. Si ya conoce la relación de aspecto de la fuente ... el título de la q no tiene sentido para mí. – Gishu

+0

Lo arreglé. :) – Nathan

+0

Cuando dices "ventana", ¿te refieres a "pantalla"? – Nosredna

Respuesta

157

Deduzco que busca una relación de aspecto utilizable integer:integer solución como 16:9 en lugar de una solución float:1 como 1.77778:1.

Si es así, lo que necesita hacer es encontrar el máximo común divisor (GCD) y dividir ambos valores por eso. El GCD es el número más alto que divide equitativamente ambos números. Entonces el GCD para 6 y 10 es 2, el GCD para 44 y 99 es 11.

Por ejemplo, un monitor de 1024x768 tiene un GCD de 256. Cuando divide ambos valores, obtiene 4x3 o 4: 3.

A (recursivo) algoritmo GCD:

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

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

Y aquí hay algo de HTML completa/Javascript que muestra una manera de detectar el tamaño de la pantalla y calcular la relación de aspecto de ese. Esto funciona en FF3, no estoy seguro de qué soporte tienen otros navegadores para screen.width y 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> 

Genera (en mi extraña monitor de pantalla ancha):

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

Otros que he probado este artículo en:

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 

Me gustaría tener esto último en casa, pero , no, desafortunadamente es una máquina de trabajo.

Lo que hace si descubre que la relación de aspecto no es compatible con su herramienta de cambio de tamaño de gráfico es otra cuestión. Sospecho que la mejor opción sería agregar líneas de letter boxing (como las que obtienes en la parte superior e inferior de tu viejo televisor cuando estás viendo una película de pantalla ancha). Los agregaría en la parte superior/inferior o en los laterales (lo que resulte en el menor número de líneas de letter-boxing) hasta que la imagen cumpla con los requisitos.

Una cosa que quizás desee considerar es la calidad de una imagen que ha cambiado de 16: 9 a 5: 4 - Todavía recuerdo los vaqueros increíblemente altos y delgados que solía ver en mi juventud en televisión antes de la carta- el boxeo fue introducido.Puede que sea mejor tener una imagen diferente por relación de aspecto y simplemente cambiar el tamaño de la correcta para las dimensiones reales de la pantalla antes de enviarla por el cable.

+1

Esta fue la primera respuesta que pensé dar, pero le preocupaba que no devolviera los resultados útiles a su componente de terceros si su ventana tiene el tamaño de algo así como 1021x711, por ejemplo. – Nosredna

+1

Parece una exageración. Y no funciona para los casos que menciona Nosredna. Tengo una solución basada en la aproximación. –

+1

Mi cliente me dijo que necesita la relación de aspecto del espectador. Es un servicio para una imprenta.Es para estadísticas creo – Nathan

0
Width/Height 

?

+0

Usaré un componente que solo acepta la relación de aspecto como: 4: 3, 16: 9 – Nathan

31
Aspect Ratio = width/height 

si eso es lo que buscas. Luego puede multiplicarlo por una de las dimensiones del espacio objetivo para descubrir el otro (que mantiene la proporción) p.

widthT = heightT * Aspect Ratio 
heightT = widthT/Aspect Ratio 
0

I creen que la relación de aspecto es el ancho dividido por la altura.

r = w/h 
1

Creo que esto hace lo que está pidiendo:

webdeveloper.com - decimal to fraction

anchura/altura se consigue un decimal, convertida en una fracción con ":" en lugar de '/' le da una "relación de ".

1

This algorithm in Python te lleva parte del camino.


Dime qué sucede si las ventanas son de un tamaño divertido.

Tal vez lo que debe tener es una lista de todas las relaciones aceptables (al componente 3 ª parte). Luego, busca la coincidencia más cercana a tu ventana y regresa esa relación de la lista.

10

supongo que quiere decidir cuál de 4: 3 y 16: 9 es el mejor ajuste.

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

Si bien su solución está bien para 4x3 y 16x9, esto no parece que sería compatible con todas las relaciones de aspecto posibles (aunque tal vez eso no es importante para el OP). La relación para la mayoría de los monitores de pantalla ancha, por ejemplo, es 16x10 (1920x1200, 1600x1000)? – Falaina

+0

Realmente no tenemos suficiente información para responder bien la pregunta. :-) – Nosredna

+0

gracias! ¡Funcionó como por arte de magia! – moeiscool

1

Como una solución alternativa a la búsqueda de GCD, le sugiero que consulte un conjunto de valores estándar. Puede encontrar una lista en Wikipedia.

1

Im suponiendo que su hablando de video aquí, en cuyo caso es posible que tenga que preocuparse por la relación de aspecto de píxeles de la fuente de vídeo. Por ejemplo.

DV PAL viene en una resolución de 720x576. Que se vería como 4: 3. Ahora, dependiendo de la relación de aspecto de píxeles (PAR), la relación de pantalla puede ser 4: 3 o 16: 9.

Para obtener más información echar un vistazo aquí http://en.wikipedia.org/wiki/Pixel_aspect_ratio

Usted puede obtener la relación de aspecto de píxeles cuadrados, y una gran cantidad de vídeo en la web es que, aunque es posible que desee tener cuidado de los otros casos.

Esperanza esto ayuda

Marcos

0

poco de una extraña manera de hacer esto, pero utilizar la resolución como el aspecto. E.G.

1024: 768

o puede probar la respuesta

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

http://jsbin.com/amafad/1/edit Probé tu código y no funcionó .. – JasonWyatt

11

de paxdiablo es grande, pero hay una gran cantidad de resoluciones comunes que tienen sólo unos pocos más o menos píxeles en una dirección dada, y el mayor enfoque de divisor común les da resultados horribles.

Tomemos por ejemplo la resolución de 1360x765 buen comportamiento que da una buena relación de 16: 9 utilizando el enfoque mcd. Según Steam, esta resolución solo la usa el 0.01% de sus usuarios, mientras que 1366x768 es utilizada por un 18.9%. Vamos a ver lo que obtenemos utilizando el enfoque mcd:

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

que nos gustaría que reunir a 683: relación de 384 a los más cercanos, formato 16: 9.

Escribí un script de Python que analiza un archivo de texto con números pegados de la página de Steam Hardware Survey e imprime todas las resoluciones y proporciones conocidas más cercanas, así como la prevalencia de cada relación (que era mi objetivo cuando comencé este):

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

Para los curiosos, estos son la prevalencia de relaciones de pantalla entre los usuarios de Steam (a partir de octubre de 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% 
1

sobre la base de las otras respuestas, aquí es cómo llegué a los números Lo necesitaba en 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

Aquí está mi solución es bastante simple ya que todo lo que importa no es necesariamente GCD o incluso proporciones precisas: porque entonces se obtiene cosas extrañas como 345/113, que no son humanos comprensible.

Básicamente puse paisaje aceptable, o relaciones retrato y su "valor" como un flotador ... Entonces comparo mi versión de flotación de la relación de cada uno, y que cada vez tiene el menor valor absoluto diferencia es la relación más próxima a la ít. De esta forma cuando el usuario hace que sea 16: 9, pero a continuación, elimina 10 píxeles desde la parte inferior todavía cuenta como 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 
0

Aquí está una versión de mejor algoritmo de aproximación racional de James Farey con nivel ajustable de fuzziness portado a javascript desde el aspect ratio calculation code escrito originalmente en python.

El método toma un flotante (width/height) y un límite superior para el numerador/denominador de la fracción.

En el siguiente ejemplo estoy fijando un límite superior de 50 porque necesito 1035x582 (1.77835051546) a tratar como 16:9 (1.77777777778) en lugar de 345:194 la que se obtiene con el algoritmo sencillo gcd que aparece en otras respuestas.

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

El resultado:

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