TL; DR: 2 * 1LSB coupures de tramage triangulaire-pdf dans les boîtiers électroniques à 0 et 1 en raison du serrage. Une solution consiste à lerp à un tramage uniforme 1 bit dans ces edgecases.
J'ajoute une deuxième réponse, car cela s'est avéré un peu plus compliqué que je ne le pensais à l'origine. Il semble que ce problème ait été un "TODO: besoins de serrage?" dans mon code depuis que je suis passé du tramage normalisé au tramage triangulaire ... en 2012. Ça fait du bien de le regarder enfin :) Code complet pour la solution / les images utilisées tout au long du post: https://www.shadertoy.com/view/llXfzS
Tout d'abord, voici le problème que nous examinons, lors de la quantification d'un signal à 3 bits avec un tramage PDF triangulaire 2 * 1LSB:
- essentiellement ce que hotmultimedia a montré.
En augmentant le contraste, l'effet décrit dans la question devient apparent: la sortie ne passe pas en moyenne au noir / blanc dans les boîtiers électroniques (et s'étend en fait bien au-delà de 0/1 avant de le faire).
Regarder un graphique donne un peu plus d'informations:
(les lignes grises marquent 0/1, également en gris le signal que nous essayons d'émettre, la ligne jaune est la moyenne de la sortie tramée / quantifiée, le rouge est l'erreur (moyenne du signal)).
Fait intéressant, non seulement la sortie moyenne n'est pas 0/1 aux limites, mais elle n'est pas non plus linéaire (probablement en raison du pdf triangulaire du bruit). En regardant l'extrémité inférieure, il est logique de comprendre pourquoi la sortie diverge: lorsque le signal tramé commence à inclure des valeurs négatives, le serrage sur la sortie modifie la valeur des parties inférieures tramées de la sortie (c'est-à-dire les valeurs négatives), ce qui augmenter la valeur de la moyenne. Une illustration semble être en ordre (tramage 2LSB uniforme et symétrique, toujours en jaune):
Maintenant, si nous utilisons simplement un tramage normalisé 1LSB, il n'y a aucun problème au niveau des bords-cas, mais bien sûr, nous perdons les belles propriétés du tramage triangulaire (voir par exemple cette présentation ).
Une solution (pragmatique, empirique) (hack) consiste alors à revenir à [-0,5; 0,5 [tramage uniforme pour la casse):
float dithertri = (rnd.x + rnd.y - 1.0); //note: symmetric, triangular dither, [-1;1[
float dithernorm = rnd.x - 0.5; //note: symmetric, uniform dither [-0.5;0.5[
float sizt_lo = clamp( v/(0.5/7.0), 0.0, 1.0 );
float sizt_hi = 1.0 - clamp( (v-6.5/7.0)/(1.0-6.5/7.0), 0.0, 1.0 );
dither = lerp( dithernorm, dithertri, min(sizt_lo, sizt_hi) );
Ce qui corrige les edgecases tout en gardant le tramage triangulaire intact pour la plage restante:
Donc, pour ne pas répondre à votre question: je ne sais pas s'il existe une solution plus solide mathématiquement, et je suis tout aussi désireux de savoir ce que Masters of Past a fait :) Jusque-là, au moins nous avons cet horrible hack pour faire fonctionner notre code.
EDIT
Je devrais probablement couvrir la solution de contournement-suggestion donnée dans la question, sur la compression simple du signal. Étant donné que la moyenne n'est pas linéaire dans les cas particuliers, la simple compression du signal d'entrée ne produit pas un résultat parfait - bien qu'elle fixe les points de terminaison:
Références