8 points par GN⁺ 2026-06-02 | 2 commentaires | Partager sur WhatsApp
  • Différence entre la méthode standard qui divise par 255 et la méthode alternative qui ajoute un biais de 0,5 puis divise par 256 lors de la conversion de couleurs entières sur 8 bits en nombres à virgule flottante
  • La méthode 255 mappe l’entier 0 sur 0.0 et 255 sur 1.0, ce qui facilite la manipulation directe du noir et du blanc, et correspond aussi à la conversion UNORM-vers-float des GPU
  • La méthode 256 utilise (img + 0.5) / 256.0 pour placer chaque valeur au centre de son intervalle, ce qui peut simplifier la gestion des bords pour des opérations comme le dithering, mais 0 n’y vaut pas 0.0, ce qui lie la logique de traitement à une entrée 8 bits
  • Avec la méthode 255, les intervalles aux deux extrémités ont une largeur de moitié ; ainsi, si l’on réarrondit en 8 bits des nombres aléatoires uniformes dans [0, 1], 0 et 255 apparaissent deux fois moins souvent que les autres valeurs, mais dans les faits les conversions aller-retour d’images restent sans perte
  • Si vous traitez des images produites par d’autres, la bonne réponse est de normaliser par 255 ; la méthode 256 n’est à envisager que si vous contrôlez à la fois l’enregistrement et le chargement

Cadre du problème

  • Dans un programme qui reçoit une image, la convertit en flottants, la traite puis la réenregistre en couleurs 8 bits, la question porte sur la méthode de conversion entier-flottant
  • Il existe deux approches
    • Méthode standard (division par 255) : pixels = img / 255.0 → traitement → output = np.trunc(result * 255 + 0.5)
    • Méthode alternative (division par 256) : pixels = (img + 0.5) / 256.0 → traitement → output = np.trunc(result * 256)
    • Dans les deux cas, la valeur est limitée à 0–255 avant le cast final : output.clip(0, 255).astype(np.uint8)
  • La méthode standard mappe l’entier 0 sur 0.0 et 255 sur 1.0, comme la conversion UNORM-vers-float des GPU
  • La méthode alternative ajoute un biais de 0,5, si bien que l’entier 0 est mappé sur 0.5/256 = 0.001953125
    • En conséquence, sans connaître cette constante, il est impossible de détecter les pixels noirs
    • Même avec des calculs en flottants, la logique reste liée à une entrée 8 bits
    • Avec la méthode standard, on peut toujours supposer que le noir vaut 0.0

Objections à 255.0

  • Si l’on représente la méthode standard sur une droite numérique, elle paraît un peu étrange
  • Il y a des bins plus petits aux deux extrémités

    • Les bins extrêmes de la formule standard débordent hors de l’intervalle [0,1], ce qui donne une forme « étirée »
    • Lorsqu’on reconvertit les flottants en entiers, la largeur des bins aux extrémités n’est que la moitié de celle des autres bins
      • Cela rend l’émission de valeurs extrêmes « plus difficile » pour l’algorithme
      • Si l’on génère un bruit uniforme dans [0,1] puis qu’on l’arrondit avec la formule standard, les valeurs 0 et 255 n’apparaissent qu’à moitié aussi souvent que les autres entiers
    • Un histogramme de 1 million de nombres aléatoires uniformes permet de vérifier que les bins 0 et 255 ont une hauteur moitié moindre
    • Il reste toutefois difficile d’imaginer des cas réels où ce biais d’évitement des extrêmes poserait problème
      • L’image source continue de faire un aller-retour sans perte (uint8 → float → uint8)
      • Des résultats légèrement inférieurs à 0.0 ou supérieurs à 1.0 sont quand même arrondis vers le bon bin, ce qui rééquilibre la distribution de sortie
      • Exemple : si l’on soustrait 0.005 à la couleur pendant le traitement, avec la méthode standard le noir passe sous 0 alors qu’avec l’alternative il reste positif, mais dans les deux cas la sortie entière finale est 0
  • Imprécision

    • Les valeurs flottantes de la méthode standard ne sont pas exactes ; par exemple 128/255.0 ≈ 0.501961, alors que 128/256.0 = 0.5
    • L’erreur d’arrondi fait varier très légèrement l’espacement entre les valeurs flottantes, mais l’erreur est si faible qu’elle n’a pas d’impact pratique
      • Les flottants 32 bits ont une mantisse de 23 bits, et l’erreur reste au niveau du bit de poids faible, donc inférieure à 2⁻²³
      • Une erreur relative de 0.00001 % n’a pas de sens pratique, même en traitement d’image sophistiqué ; cette imprécision est un problème esthétique, pas technique
  • Valeurs qui ne correspondent pas exactement à une plage entière

    • La méthode alternative place chaque valeur flottante exactement au milieu de deux entiers
      • Comme la valeur quantifiée d’origine est inconnue, le point moyen entre deux entiers consécutifs constitue un compromis raisonnable
    • Certains avancent que cela facilite le dithering (billet de blog d’Andrew Kesler en 2015, « Converting Color Depth »)
      • On peut ajouter du bruit sans se soucier des cas limites
      • À l’inverse, les valeurs extrêmes maladroites de la formule standard demandent un traitement plus soigneux pour conserver une distribution de bruit cohérente

Deux types de quantificateurs

  • On peut voir les deux approches comme deux types de quantificateurs scalaires uniformes
  • D’après l’article de Wikipedia sur la quantification, les quantificateurs uniformes pour données d’entrée signées se répartissent en deux catégories
    • mid-tread : 0 est mappé sur le niveau de reconstruction de valeur 0 (le plat d’une marche)
    • mid-riser : 0 est mappé sur le seuil de décision de valeur 0 (la contremarche)
    • Wikipedia cite comme source un article de 1977 (Allen Gresho, « Quantization »)
  • Formules des quantificateurs (L est le nombre de niveaux de sortie, par exemple 256)
    • Quantificateur en escalier mid-tread : encodage k = trunc(xL + 0.5), décodage yₖ = k/L
    • Quantificateur en escalier mid-riser : encodage k = trunc(xL), décodage yₖ = (k+0.5)/L
  • Appliqué aux deux méthodes
    • Formule standard = mid-tread (L=255)
    • Formule alternative = mid-riser (L=256)
  • La méthode standard utilise un mid-tread sur une entrée non signée avec un code L=255, une combinaison qui n’est pas optimale pour une entrée 8 bits
    • C’est un choix de commodité de programmation pour mapper les extrémités sur 0.0 et 1.0
  • Une erreur de quantification plus élevée, mais pas en pratique

    • Si l’on avait un système qui encode des réels x∈[0,1] en entiers 8 bits puis les reconstruit en réels, la formule standard gaspillerait de la bande passante
      • La plage représentable par la méthode standard est [-0.5/255, 255.5/255], donc plus large que nécessaire pour une entrée [0,1], ce qui accroît l’erreur de reconstruction
      • Selon le calcul de l’utilisateur StackOverflow Peter Mudrievskij, l’erreur absolue moyenne est de 1/1020 avec un diviseur 255 et de 1/1024 avec un diviseur 256 ; diviser par 256 est donc théoriquement plus précis
    • Mais en pratique, ce n’est pas ce type de reconstruction que l’on effectue
      • On part du principe qu’on charge une image RGB 8 bits, qu’on la traite puis qu’on la réenregistre ; au moment de la sauvegarde, on ne contrôle pas forcément la méthode de quantification, et l’information perdue l’est définitivement
      • Si l’image a été enregistrée en multipliant et en arrondissant selon la formule standard, la charger en divisant par 256 ne permet pas de récupérer de précision
      • L’argument de la moindre erreur de reconstruction n’a de sens que si l’on contrôle à la fois la sauvegarde et le chargement
    • Charger l’image de quelqu’un d’autre avec la formule alternative introduit au contraire davantage d’erreur
      • Il est très probable qu’elle ait été quantifiée avec la formule standard ; la décoder avec une mauvaise échelle est donc théoriquement inexact
      • En pratique, les couleurs ne sont pas des mesures absolues ; on traite simplement une plage légèrement plus petite avec un léger décalage
    • Il ne faut pas mélanger les étapes d’encodage et de décodage des deux quantificateurs, erreur fréquente dans du code cassé

Conclusion

  • Si vous traitez des images fournies par d’autres, il faut normaliser les valeurs RGB par 255
    • Les inquiétudes sur l’imprécision des flottants ou sur une erreur de reconstruction abstraite ne sont pas de bonnes raisons de préférer l’alternative
  • Si vous contrôlez entièrement l’enregistrement et le chargement des images, que vous n’avez pas besoin de mapper 0 sur 0 et que cela ne vous gêne pas de lier votre code de traitement à une plage dynamique 8 bits, vous pouvez diviser par 256 pour gagner un tout petit peu en précision
    • Attention toutefois : un collègue pourrait charger l’image avec la formule standard et ruiner votre plan

Autres points de vue

2 commentaires

 
GN⁺ 2026-06-03
Avis sur Hacker News
  • La signification exacte d'une valeur de couleur n'a généralement pas beaucoup d'importance avec 8 bits par composante. L'erreur créée par la différence entre un dénominateur de 255 ou de 256 est très faible, et pour la voir il faudrait avoir une très bonne perception des couleurs et se coller tout près de l'écran, d'autant que les moniteurs et écrans de téléphone ne sont en général pas calibrés
    Mais si l'on génère un signal VGA avec un microcontrôleur et qu'on ne dispose que de 8 broches de sortie couleur (3 pour le rouge, 3 pour le vert, 2 pour le bleu), cela devient assez pénible. Dans ce cas, la valeur de couleur correspond directement au niveau de tension 0 V à 0,7 V à envoyer au moniteur VGA
    Le canal bleu est mappé ainsi : 0→0V, 1→0,23V, 2→0,47V, 3→0,7V, tandis que le rouge et le vert sont mappés ainsi : 0→0V, 1→0,1V, …, 7→0,7V. En dehors des extrémités, les tensions du bleu ne correspondent pas du tout à celles du rouge/vert, ce qui empêche d'obtenir un gris pur, et la couleur la plus proche tire légèrement vers le bleu ou le jaune selon le sens de l'écart
    De plus, presque tous les dégradés mélangeant le bleu avec d'autres canaux paraissent décalés. Par exemple, les couleurs les plus proches sur la ligne allant du rouge pur au blanc pur paraissent un peu orangées ou violacées
    Le code pour une sortie VGA couleur 8 bits sur Raspberry Pi Pico 2 avec framebuffer double buffer 320x240 se trouve ici : https://github.com/moefh/pico-vga-8bit-demo

    • Je me souviens que, enfant, en regardant un écran CRT parasité, je voyais de faibles lignes bleues et jaunes sur les bords. Je me suis toujours demandé pourquoi précisément ces deux couleurs ; si c'est la même cause, j'ai enfin ma réponse
    • On a oublié la correction gamma. Avant de convertir une valeur de l'intervalle 0~255 en tension, le PC élève généralement cette valeur à la puissance 2,2
      Cela rend la différence entre les petites et les grandes valeurs beaucoup plus marquée : 2^2.2 = 4.595, 255^2.2 = 196,964.699
    • Pour ce problème, le dithering temporel semble être la meilleure solution. Une modulation delta-sigma par pixel est assez facile à mettre en place
      Si cela change à 30Hz, il semble peu probable que l'œil humain puisse distinguer une légère dominante bleue d'une légère dominante jaune
    • C'est sans doute pour cela que les couleurs RGBI étaient si courantes dans les années 1980
  • Pour défendre 255, il suffit de regarder le cas extrême d'une image en noir et blanc. Avec un seul bit, 0 correspond au noir et 1 au blanc
    Il paraît assez clair que 0 doit être mappé sur 0,0 et 1 sur 1,0. C'est du noir et blanc, pas du gris clair (0,25) et du gris foncé (0,75). Autrement dit, une image noir et blanc se normalise par 1 et non par 2
    Avec 2 bits, on a en général 0=noir, 1=gris clair, 2=gris foncé, 3=blanc, donc il est naturel de mapper cela sur 0,0, 0,33, 0,66, 1,0. Le noir doit être noir, le blanc doit être blanc, et l'espacement doit rester uniforme, donc on normalise par 3
    En prolongeant cette logique jusqu'à 8 bits, on en arrive à une normalisation par 255. Même si la différence devient très faible en 8 bits, le noir doit rester 0,0 et le blanc 1,0
    L'autre méthode, qui consiste à normaliser par 256 en 8 bits, fait varier la plage de sortie selon le nombre de bits. Avec 1 bit, on obtient [0,25, 0,75], avec 2 bits [0,125, 0,875], etc. En général, on veut que l'augmentation du nombre de bits ajoute de la nuance, pas qu'elle change le contraste

  • C'était vraiment un texte qui fait réfléchir, et qui m'a amené à reconsidérer certaines hypothèses personnelles
    D'un point de vue de génie électrique, j'ai du mal à adhérer à la présentation des « deux types de quantificateurs » dans l'article. C'est rigoureux mathématiquement, mais ce n'est pas une explication fondée sur les systèmes réels
    Un ADC comporte toujours intrinsèquement une incertitude de quantification de ±1/2 LSB. La caractéristique de transfert correspond toujours à un échantillonnage mid-tread, et je n'ai au moins jamais vu de contre-exemple. Cela vaut aussi bien pour un ADC bipolaire qu'un ADC unipolaire
    Le code le plus bas correspond à la tension négative de référence, et le code le plus haut à la tension positive de référence. Le graphe de la caractéristique de transfert montre que, comme dans l'article, les intervalles extrêmes ont en réalité une largeur de 1/2 LSB
    Dans un système unipolaire, on ne peut pas représenter exactement les tensions intermédiaires ; autrement dit, on retombe sur le problème du gris. Dans un système bipolaire, 0V correspond à la valeur N/2 du mid-tread, mais cela ne signifie pas pour autant qu'il existe « 256 intervalles »
    C'est pourquoi je continuerai à utiliser (VREF+ - VREF-) * k / (2^N - 1). Autrement dit, je suis d'accord avec la normalisation par 255. Au fond, c'est le même problème que l'erreur du piquet de clôture : il y a N valeurs, mais N-1 intervalles. S'il y a moins d'intervalles que de valeurs, il faut partager un intervalle entre deux valeurs, d'où l'apparition d'intervalles de 1/2 LSB aux extrémités

    • Toute documentation ADC que j'ai lue indique qu'on ne peut pas représenter le plein échelle positif. Par exemple, avec un ADC 8 bits ±1V, -128 signifie -1V, et +127 signifie 127/128=0,99219V
      La transition de 126 à 127 se produit à 1,5 LSB du plein intervalle positif. Une différence de 1 LSB signifie une différence de 1/128=0,00781V, et non de 2/255=0,00784V
      Mais en pratique, si ce qui compte est la tension et l'incertitude, cette différence a le plus souvent peu d'importance. Il existe un biais sur la tension de référence ainsi que des erreurs de linéarité. 1 LSB ne correspond exactement ni à 1/128 ni à 2/255, et il faut alors des paramètres de calibration
  • Cela ressemble à la différence, en calcul scientifique, entre des échantillons centrés sur les nœuds et des échantillons centrés sur les cellules, vue en une dimension. Il faut décider si la valeur se trouve au centre d'un intervalle (ou d'un triangle/tétraèdre) ou sur la frontière de l'intervalle (ou aux sommets du triangle/tétraèdre)
    En calcul scientifique, commencer à traiter des données sans savoir comment il faut les interpréter n'a aucun sens. En traitement du signal audio aussi, si l'on ne reçoit qu'un flux d'entiers, il faut savoir quelle intention de représentation portent ces entiers, par exemple un encodage mu-law ou linéaire, avant de pouvoir calculer quoi que ce soit sur le signal d'origine. On espère que les métadonnées attachées aux valeurs fourniront cette réponse
    Mais avec des valeurs de pixels sur 8 bits, en l'absence de métadonnées de format de fichier capables de transmettre correctement l'intention de représentation, on se retrouve à la dérive, sans réponse unique. Comme le dit l'auteur, on ne peut pas reprocher à quelqu'un de choisir ce qui donne le meilleur résultat pour son usage, mais on peut rappeler que des bits sans contexte perdent leur sens

    • Cela me fait penser à la valeur de normalisation utilisée pour la quantification des images satellite Sentinel-2 level-2 de l'ESA
      C'est à peu près ceci : le nombre numérique DN=0 reste réservé à la valeur « NO_DATA », et lorsque DN est dans l'intervalle [1; 1;215-1], la valeur de réflectance L2A SR est donnée par L2A_SRi = (L2A_DNi + BOA_ADD_OFFSETi) / QUANTIFICATION_VALUE
      https://sentiwiki.copernicus.eu/web/s2-products
  • Il y a ici une erreur qui consiste à supposer qu’il y a 256 niveaux de 0 à 255. En réalité, il y a 256 valeurs représentables sur 8 bits, et 255 intervalles entre 0 (noir) et 255 (blanc pur)
    Donc diviser par 255 n’a rien de problématique. Bien sûr, 128 n’est pas exactement un gris à mi-chemin, et les valeurs 8 bits quantifiées de 0 à 255 se trouvent presque toujours en sRGB, pas dans un espace perceptif linéaire
    On retrouve une confusion similaire dans les API modernes quand on traite les positions d’échantillonnage, parce que la position est spécifiée comme une coordonnée et non comme le centre du pixel

    • L’API BeOS se base sur le centre du pixel. Plus personne ne s’en soucie aujourd’hui, certes
  • D’un point de vue algébrique, la réponse est claire : f(x) -> [0, 255]
    Si f(n * 0) == n * f(0) n’est pas vrai, il se passe des choses étranges. Par exemple, si f(x) -> [0, 255], alors f(0) + f(0) + f(0) = 0 + 0 + 0 = 0 = f(0)
    En revanche, si f(x) -> [0.5/8, 7.5/8], alors f(0) + f(0) + f(0) = 0.5/8 + 0.5/8 + 0.5/8 = 1.5/8 != f(0)
    Si on choisit la seconde option, on ne peut pas s’attendre à ce qu’un calcul fait côté x et le même calcul fait côté f(x) coïncident. Autrement dit, la correspondance algébrique est rompue

  • J’ai envie de soutenir la solution du +0.5. D’abord, je n’aime pas ces intervalles de demi-taille aux bords, ensuite la représentation basée sur 255 correspond en général à une image SDR, pas à du HDR
    Les valeurs RGB représentent une luminance relative à un certain état d’adaptation, et dans une scène de jour, le « 0 » n’est pas une « luminance 0 ». C’est plutôt environ 0,001 fois le point le plus lumineux, et cela correspond quand même à des millions de photons, donc bien plus que zéro
    D’une certaine manière, l’œil perçoit le contraste sur une échelle glissante, et il n’existe pas de zéro absolu dans le système. Par exemple, les systèmes de diffusion ont historiquement utilisé 16~235 comme plage de luminance SDR. Je pense que le raisonnement selon lequel « il faut absolument avoir 0 » introduit un biais, et que dans la plupart des cas 0 n’est pas nécessaire

    • En tant que personne ayant beaucoup pratiqué le traitement d’image et le rendu pour la VFX, on dirait que cela oublie qu’il y a ensuite une conversion d’espace colorimétrique. Dans l’ancien SDR, on convertissait vers le Rec.709 linéaire du sRGB, et dans les formats récents vers des gamuts plus larges. La compression de la plage dynamique se produit donc après le chargement
      En outre, une bonne partie des workflows de traitement d’image et de compositing supposent, à tort ou à raison, que 0 signifie 0. On considère donc souvent qu’en 8 bits, 0u correspond à 0.0f et 255 à 1.0f. Si une valeur 0 dans un masque ou un alpha devient légèrement supérieure à 0.0, du code quelque part appliquera un seuil dur à 0.0 pour masquer d’autres opérations, ce qui créera des artefacts. À l’inverse, si 255 en alpha n’est plus exactement 1.0f, alors après prémultiplication l’objet devient très légèrement transparent
      Le même problème peut se produire quand 254 devient 1.0f dans un masque à cause du +0.5
    • L’article se concentre sur le RGB, mais le même problème de quantification existe pour tous les types de signaux mappés entre une représentation discrète et une représentation continue
      L’essentiel n’est pas de savoir si l’on représente 0 photon, mais de maximiser l’information stockée dans 1 octet. Idéalement, il ne faut pas sous-utiliser la valeur d’octet 0, ni ajouter un biais aux données qui auraient dû tomber dans le 0e bucket. Même dans un espace colorimétrique allant de lumineux à très lumineux, chaque valeur d’octet devrait représenter une portion de même taille de la plage de luminance
    • Le fait que les systèmes de diffusion aient historiquement utilisé 16~235 comme plage de luminance SDR est justement le problème. Malheureusement, même le HDMI « moderne » subit encore cette étrange convention, et si l’écran et la source ne se mettent pas d’accord, l’image paraît délavée ou les noirs sont bouchés
    • Les deux solutions ajoutent 0.5. La différence est simplement l’endroit du processus où cela se produit
    • Idée intéressante, mais cela donne l’impression que le monde vacille. Du point de vue d’un programme de traitement, l’ancien noir (0.0) et l’ancien blanc (1.0) deviennent un gris très sombre et un gris très clair
  • Si une règle va jusqu’à 12 pouces, il faut normaliser par la longueur L, pas par 13 qui est le nombre de points sur la règle

    • Cette analogie me perturbe. Je ne sais pas si la « règle » est une règle de 255 pouces avec 256 points marqués de 0 à 255, ou une règle de 256 pouces avec 256 intervalles d’un pouce, auquel cas L = 256×1
    • Si ce qu’on veut réellement compter, ce sont des poteaux de clôture, alors l’erreur des poteaux de clôture n’est pas une erreur
    • C’est vrai, mais >> 8 est bien plus rapide
    • Qui a décidé que les nombres représentaient des points ? Ils peuvent aussi représenter les intervalles entre les points
    • C’est moi qui suis idiot ? 0 ne commence-t-il pas au point de départ ?
  • C’était un article agréable à lire sur un sujet auquel je n’avais pas pensé depuis longtemps. Cela m’a rappelé des moments en développement de jeux où la logique du jeu utilisait des calculs en virgule flottante, mais où le pixel art devait être dessiné sur des coordonnées entières
    À plusieurs endroits, j’essayais d’utiliser quelque chose de proche du +0.5 pour que cela paraisse moins bizarre. C’était particulièrement le cas avec une caméra en mouvement, et il fallait aussi quantifier la caméra
    L’article de Jonathan Blow de 2002 lié plus bas [1] était aussi intéressant. La visualisation du premier article aide beaucoup quand on veut creuser davantage
    [1] https://web.archive.org/web/20240706043551/https://number-no...

 
GN⁺ 2026-06-02
Avis sur Lobste.rs
  • C’est moche, mais correct : la bonne valeur est 255
    Si ce n’est pas intuitif, on peut regarder le cas dégénéré sur 2 bits. Quand les seules valeurs entières possibles sont 0, 1, 2 et 3, si on calcule toutes les conversions entier → virgule flottante, on obtient 0.0, 0.33..., 0.66..., 1.0 pour éviter des comportements bizarres où le noir/blanc ne seraient pas vraiment noir/blanc, ou où les intervalles seraient manifestement irréguliers
    Donc la conversion inverse consiste à multiplier par 3, pas par 4 (2^2)
    • Le début est juste, mais ça n’implique pas pour autant que « la conversion inverse doit multiplier par 3 et non par 4 »
      La conversion inverse nécessite une quantification (arrondi), et c’est précisément là que la symétrie se casse
      Si on crée un gradient réel uniforme dans l’intervalle 0..=1 puis qu’on le quantifie en 0, 1, 2, 3, on voit qu’avec ×3 le résultat n’est pas uniforme. Avec round() après ×3, 1 et 2 sont surreprésentés ; avec floor ou ceil après ×3, 0 ou 3 deviennent des singularités, ce qui donne l’impression que le gradient n’utilise que 3 couleurs sur 4
      La logique /3 et ×3 semble correcte quand on fait un aller-retour sur des nombres exacts, mais les valeurs intermédiaires dépendent fortement du choix d’arrondi, et cela devient important dès qu’on commence à traiter les données
      La seule façon d’obtenir des proportions entières uniformes est de multiplier par (4-ε) puis de tronquer, ce qui revient à ×4, floor() et clamp(). Ça ressemble à une erreur bizarre de 1 ou de ε, mais intuitivement c’est la solution la plus satisfaisante visuellement
  • Le titre m’a beaucoup embrouillé. Je ne sais pas si c’était volontaire, mais au fond ça ressemble plutôt à « est-ce que 0..1 correspond à [0..255.0] ou à [0.5..255.5] ? »
    Pour moi, la réponse a toujours été « évidemment » [0.0..255.0], mais apparemment ce n’est pas évident pour tout le monde
    L’article dit aussi que les intervalles « extrêmes » n’ont que la moitié de la capacité des autres, mais je ne pense pas que ce cadrage soit juste
    S’il n’existe pas de valeurs en dehors de [0..1], le fait que ces intervalles paraissent plus étroits est un artefact de rendu. Ils sont juste rendus plus étroits parce qu’on a découpé les buckets en sachant qu’il n’existe pas de valeurs hors plage
    À l’inverse, s’il existe des valeurs en dehors de [0..1], alors cette plage est infinie. L’article reconnaît le second point, mais pas le premier
    Dès qu’on accepte le premier, le comportement correct semble clair, mais le simple fait que cet article existe montre aussi que ce n’est pas une question objectivement « claire » :D
    • Si 0…255.0 est vraiment si évident, alors quelle plage de valeurs flottantes doit revenir à l’entier 0, et quelle plage doit revenir à l’entier 255 ?
      Si 0..<1 va vers l’entier 0 et que 254>..255.0 va vers l’entier 255, alors 128 se fait manger. On voudra probablement que 127.5..128.5 aille vers 128, mais alors où doivent aller ces moitiés ?
      Si on décale un peu tout l’ensemble pour faire tomber juste 128, alors 0..0.99609375 est mappé vers l’entier 0
  • L’approche standard semble aussi venir du fait que les gens appellent naturellement round()
    Comme cette manière de faire paraît assez naturelle, elle est sans doute devenue la norme par simplicité
  • Je me demande si l’approche opposée à ce qu’on cherchait à obtenir avec 256 pourrait aussi être utile. Autrement dit, envoyer 0.0 vers 0, 1.0 vers 255, et mapper toutes les autres valeurs flottantes entre 1 et 254
    uint8_t output = 0.0f >= result
                     ? 0
                     : 1.0f <= result
                     ? 255
                     : 1 + 253*result;
    
    Ce serait bien que, même pendant le traitement, le noir reste noir et le blanc reste blanc
    • Avec cette méthode, 0 et 255 reçoivent une part plus grande que les autres nombres dans l’intervalle unitaire. Environ 0,8 %, soit 255/253
  • La première image s’affiche de façon cassée chez moi
    • Auteur de l’article ici. Vous voulez dire que le fichier image est corrompu ? Je l’ai bien compressé avec pngcrush. Ou bien vous voulez dire que le contenu de l’image lui-même a un problème ?