web-dev-qa-db-fra.com

Quel est l'algorithme pour calculer le format d'image? J'ai besoin d'une sortie comme: 4: 3, 16: 9

Je prévois de l'utiliser avec javascript pour rogner une image afin qu'elle s'adapte à toute la fenêtre.

Edit: Je vais utiliser un composant de la 3ème partie qui accepte uniquement les proportions au format suivant: 4: 3, 16: 9

69
Nathan

Je suppose que vous cherchez une solution utilisable avec un rapport de format d'image integer:integer tel que 16:9 plutôt qu'une solution float:1 telle que 1.77778:1.

Si tel est le cas, vous devez rechercher le plus grand commun diviseur (GCD) et diviser les deux valeurs par cela. Le GCD est le nombre le plus élevé qui divise les deux nombres de manière égale. Donc, le GCD pour 6 et 10 est 2, le GCD pour 44 et 99 est 11.

Par exemple, un moniteur 1024x768 a un GCD de 256. Lorsque vous divisez les deux valeurs par le résultat, vous obtenez 4x3 ou 4: 3.

Un algorithme GCD (récursif):

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

Et voici un HTML/Javascript complet qui montre une façon de détecter la taille de l’écran et d’en calculer le format. Cela fonctionne dans FF3, je ne suis pas sûr de la prise en charge des autres navigateurs pour screen.width et 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>

Il affiche (sur mon étrange moniteur à écran large):

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

D'autres que j'ai testé cela sur:

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

J'aurais aimé avoir ce dernier à la maison, mais non, c'est malheureusement une machine à travailler.

Ce que vous faites si vous découvrez que le rapport de format n'est pas pris en charge par votre outil de redimensionnement graphique est un autre problème. Je pense que la meilleure solution serait d’ajouter des lignes de type «box-letter» (comme celles que vous voyez en haut et en bas de votre ancien téléviseur lorsque vous regardez un film sur grand écran). Je les ajouterais en haut/en bas ou sur les côtés (quelle que soit celle qui génère le moins de lignes de lettres) jusqu'à ce que l'image réponde aux exigences.

Une chose que vous voudrez peut-être prendre en compte est la qualité d'une image qui a été modifiée de 16: 9 à 5: 4 - Je me souviens encore des cowboys incroyablement grands et minces que je regardais dans ma jeunesse à la télévision avant l'introduction de la boxe-lettre. Il peut être préférable d’avoir une image différente par format d’image et de redimensionner la bonne pour les dimensions réelles de l’écran avant de l’envoyer par le fil.

172
paxdiablo
aspectRatio = width / height

si c'est ce que vous recherchez. Vous pouvez ensuite le multiplier par l’une des dimensions de l’espace cible pour trouver l’autre (qui maintient le rapport)

widthT = heightT * aspectRatio
heightT = widthT / aspectRatio
36
Gishu

la réponse de paxdiablo est excellente, mais il existe de nombreuses résolutions communes qui ne comportent que quelques pixels plus ou moins dans une direction donnée, et la méthode du plus grand commun diviseur leur donne des résultats horribles.

Prenons par exemple la résolution de 1360x765, qui se comporte bien, ce qui donne un bon rapport 16: 9 en utilisant l’approche gcd. Selon Steam, cette résolution n'est utilisée que par 0,01% de ses utilisateurs, alors que 1366x768 est utilisée par 18,9%. Voyons ce que nous obtenons en utilisant l'approche gcd:

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

Nous voudrions arrondir ce rapport de 683: 384 au rapport de 16: 9 le plus proche.

J'ai écrit un script python qui analyse un fichier texte avec des nombres collés à partir de la page de sondage de Steam Hardware, et imprime toutes les résolutions et les ratios les plus proches connus, ainsi que la prévalence de chaque ratio (ce qui était mon objectif initial):

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

Pour les plus curieux, voici la prévalence des taux de trame parmi les utilisateurs de Steam (en octobre 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%
12
Petrucio

Je suppose que vous voulez décider lequel de 4: 3 et 16: 9 est le meilleur ajustement.

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

Voici une version du meilleur algorithme d'approximation rationnelle de James Farey avec un niveau de flou ajustable porté en javascript à partir du code de calcul de rapport d'aspect aspect écrit à l'origine en python.

La méthode prend un float (width/height) et une limite supérieure pour le numérateur/dénominateur de fraction. 

Dans l'exemple ci-dessous, je fixe une limite supérieure de 50 parce que j'ai besoin de 1035x582 (1.77835051546) pour être traité comme 16:9 (1.77777777778) plutôt que 345:194 que vous obtenez avec l'algorithme plain gcd répertorié dans d'autres réponses.

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

Le résultat:

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

Juste au cas où vous êtes un maniaque de la performance ...

Le moyen le plus rapide (en JavaScript) de calculer un rapport de rectangle consiste à utiliser un véritable algorithme binaire Great Common Divisor.

(Tous les tests de vitesse et de chronométrage ont été effectués par d'autres, vous pouvez vérifier un point de repère ici: https://lemire.me/blog/2013/12/26/fastest-way-tocompute-the-greatest-common -diviseur/ )

Le voici:

/* 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
Arthur da Paz

Sur la base des autres réponses, voici comment j'ai obtenu les chiffres dont j'avais besoin 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')}
1
sleepycal

Je pense que cela fait ce que vous demandez:

webdeveloper.com - décimal en fraction

Largeur/hauteur vous donne une décimale, convertie en une fraction avec ":" à la place de "/" vous donne un "ratio".

1
Tobias Cohen

Cet algorithme en Python vous amène une partie du chemin.


Dis-moi ce qui se passe si les fenêtres ont une taille amusante.

Vous devriez peut-être avoir une liste de tous les ratios acceptables (par rapport au composant tiers). Recherchez ensuite la correspondance la plus proche de votre fenêtre et renvoyez ce rapport dans la liste.

1
Nosredna

Comme solution alternative à la recherche GCD, je vous suggère de vérifier par rapport à un ensemble de valeurs standard. Vous pouvez trouver une liste sur Wikipedia .

1
mouviciel

Je suppose que vous parlez de vidéo ici, auquel cas vous devrez peut-être également vous soucier du format de pixel de la vidéo source. Par exemple.

PAL DV est disponible dans une résolution de 720x576. Ce qui ressemblerait à son 4: 3. Maintenant, en fonction du format d'image (PAR), le format de l'écran peut être 4: 3 ou 16: 9. 

Pour plus d'informations, regardez ici http://en.wikipedia.org/wiki/Pixel_aspect_ratio

Vous pouvez obtenir un rapport hauteur/largeur en pixels carrés, ce qui est le cas de la plupart des vidéos Web, mais vous voudrez peut-être voir les autres cas.

J'espère que cela t'aides

Marque

1
Mark Lakewood
Width / Height

?

0
Frank Schwieterman

Je crois que le rapport d'aspect est la largeur divisée par la hauteur.

 r = w/h
0
Sean A.O. Harney

c'est un peu étrange de le faire, mais utilisez la résolution comme aspect . E.G.

1024: 768

ou tu peux essayer

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
Aron

Voici ma solution, elle est assez simple, car tout ce qui me préoccupe n’est pas nécessairement la PCD ou même des ratios précis: c’est ainsi que vous obtenez des choses étranges comme 345/113 qui ne sont pas compréhensibles pour l’homme.

Je mets en principe des ratios de paysage ou de portrait acceptables et leur "valeur" en tant que float ... Je compare ensuite ma version float du ratio à chacun et la différence ayant la plus faible valeur absolue est le ratio le plus proche de l'élément. Ainsi, lorsque l'utilisateur passe au format 16: 9 mais élimine ensuite 10 pixels du bas, il compte toujours pour le format 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
Zargold