7
votes

Interpolation bilinéaire - DirectX vs. GDI +

J'ai une application C # pour laquelle j'ai écrit le code GDI + qui utilise le rendu bitmap / texturebrush pour présenter des images 2D, qui peuvent avoir diverses fonctions de traitement d'image appliquées. Ce code est un nouveau chemin dans une application qui imite le code DX9 existant et partage une bibliothèque commune pour effectuer toutes les opérations de vecteur et de matrice (E.G. ViewToworld / WorldToView). Mon lit d'essai est composé d'images de sortie DX9 que je comparais à la sortie du nouveau code GDI +.

Un simple boîtier de test qui rend une fenêtre de visualisation qui correspond aux dimensions bitmap (c.-à-d. Aucun zoom ou pan) est match pixel-parfait (pas de diffèmement binaire) - mais dès que l'image est zoomée UP (magnifié), je reçois des différences très mineures dans 5 à 10% des pixels. La magnitude de la différence est de 1 (occasionnellement 2) / 256. Je soupçonne que cela est dû aux différences d'interpolation.

Question : Pour une projection DX9 ortho (et un espace mondial d'identité), avec une caméra perpendiculaire et centrée sur un quad texturé, est-il raisonnable d'attendre directx.direct3d.texturefilter.lineear pour générer une sortie identique à un rectangle / polygone rempli de texture GDI + texture lors de l'utilisation du system.drawing.drawing2d.interpolationmode.bilinéaire réglage?

pour cet étui (grossissement), le code DX9 utilise ceci (Minfilter, MiPfilter Set De même):
périphérique.setsamPllerstate (0, samplerstagestates.magfilter, (int) texturefilter.linear);

et le chemin GDI + utilise: g.interpolationmode = interpolationmode.bilinéaire;

Je pensais que "une interpolation bilinéaire" était une définition de filtre assez spécifique, mais j'ai remarqué qu'il y avait une autre option dans GDI + pour "HighqualityBilinéear" (que j'ai essayé, sans différence - ce qui a du sens compte tenu de la description de la description de "Ajout du préfiltering pour la rétrécissement")

Question de suivi: Est-il raisonnable d'attendre une correspondance de sortie Pixel-Perfect entre DirectX et GDI + (en supposant que toutes les coordonnées externes passées sont égales)? Sinon, pourquoi pas?

Clarification: Les images que j'utilises sont en gris opaques (r = g = b, a = 1) en utilisant le format32bppargb.

Enfin, il existe un certain nombre d'autres API que j'aurais pu utiliser (Direct2D, WPF, GDI, etc.) - et cette question s'applique généralement à la comparaison des images de sortie interpolées «équivalentes» à deux de ces deux. Merci!


17 commentaires

Je ne comprends pas. Comment pouvez-vous vous attendre à des valeurs pixelles-parfaites lorsque vous utilisez l'interpolation? Le point d'interpolation est de modifier des valeurs de pixel. Si votre point est que DX et GDI + n'étangez pas de la même manière: non, ils ne le font pas. Code différent.


Code différent certainement, mais comme la définition de l'interpolation bilinéaire est un algorithme simple ( en.wikipedia.org/wiki/ Bilinear_interpolation ) Pour trouver les contributions pondérées des pixels adjacents, pourquoi devraient-ils générer des résultats différents? Les résultats doivent être équivalents (au sein d'une erreur d'arrondi à virgule flottante, au moins) si elles utilisent la même formule. Je recherche des connaissances spécifiques sur la manière dont les formules peuvent différer entre DX9 et GDI +.


Parce que ce type de code est optimisé pour être rapide avant d'être précis. Vous devez appeler Microsoft Support si vous souhaitez trouver quelqu'un avec les connaissances spécifiques. Je sais seulement que l'interpolation de GDI + est un peu bruyante, profitant de l'œil humain ne pouvant pas observer de petites différences afin de gagner de la vitesse. Cela compte vraiment il y a 10 ans.


Bon point - Publié également sur les forums MSDN. J'espère qu'ils garderaient n'importe quel terme d'erreur bien en dessous de <1/512 à GDI +, mais peut-être pas.


Je n'ai pas eu une très bonne réponse de MSDN ( social.msdn.microsoft.com/forums/en-us/winforms/thread/.../a>), alors commençant une généraliste ici.


@holtavolt, la réponse de @john Nicholas fait ressortir un bon point. Les composants de couleur GDI (RGBA) sont mis en œuvre dans des octets de 8 bits. DX implémente les composants de couleur en tant que flotteurs (0,0-1.0). Lors de la conversion d'une valeur de flotteur en arrière à 8 bits d'octets pour l'affichage final à l'écran, il peut facilement y avoir une différence d'arrondi à 1 bits. L'utilisation de flotteurs pour interpoler est beaucoup plus précise que l'interpolation d'octets, mais tout dépend de quelle manière l'arrondi va. E.G., l'arrondi bancaire standard tourne de 0,5 à la hausse si pair si impair (ou vice versa). Bytes Interpolation (GDI) Toujours autour de la même direction.


Je spécifie les données RVB (c'est en niveaux de gris) de la même manière pour les deux API: gdi + = pixelformat.format32bppargb, dx9 = format.x8r8g8b8 - Sauf si vous voulez dire que DX convertit à l'intérieur de mes 8 bits / canal pour flotter en interne? L'arrondissement du banquier expliquerait effectivement beaucoup si c'est le cas.


@holtavolt, c'est correct. Tu l'as eu. DX représente tout le tout comme des flotteurs. X8R8G8B8 est juste la profondeur de couleur Afficher . Il prend en charge les affichages avec différentes profondeurs de couleur. Il convertit simplement une couleur (avec des composants flottants) en une couleur INT du nombre de bits requis. Je sais que définitivement depuis que je voulais écrire des programmes D3D auparavant.


A du sens pour moi - merci pour l'info. Je vais essayer votre idée re. Pilote de référence - Si tel est le cas (c'est-à-dire où GDI + et DX Diverge), je devrais voir une différence aussi.


@holtavolt, attendant avec impatience vos résultats ... juste totalement curieux! :-) Juste une note de suivi à l'arrondissement des banquiers - il est rond au plus proche même entier et est la méthode d'arrondi de l'IEEE par défaut.


@holtavolt, avez-vous eu la chance de faire le pilote de référence?


Pas encore - certaines autres priorités ont-elles augmenté, mais signaleront mes conclusions. J'ai également découvert sur l'outil PIX, que j'utiliserai pour voir si je peux trouver plus de détails sur les étapes internes DX: msdn.microsoft.com/en-us/library/ee417062 (v = vs.85) .aspx


@Stephen - Comme vous l'avez prédit, le pilote de référence vs. HW Sentier n'est pas un pixel-parfait aussi.


@holtavolt, vous aurez probablement besoin de définir le chemin HW exactement identique à celui du rendu de référence. Il est étonnamment difficile à faire. Il existe certaines choses comme la FSAA ou multisampling qu'un GPU peut faire par défaut (contrôlé via une application de panneau) que le rendeur de référence ne fera pas. En outre, certains panneaux de commande GPU remplacent les paramètres d'application sur les modes d'interpolation. Donc, si un côté a la FSAA et de l'autre côté, vous n'aurez pas aussi de pixel-perfection, bien que dans ce cas, vous trouverez des différences beaucoup plus importantes qu'un peu.


@Stephen - La différence est une valeur unique - voici l'histogramme de la différence via ImageMagik: Différences de composition de Testorigin512-mdx.png avec testorigin512-mdxref.png 245803: (0, 0, 0, 0) # 000000000000 noir 16341: (257, 257, 257, 0) # 010101010101 RVB (1,1,1) . (Il s'agit simplement d'un bitmap de niveaux de gris) basé sur ce résultat, l'entrée ici et de MS propre Docs Re. Validation des pilotes HW vs. Le pilote de référence à l'aide de PIX, je suis maintenant convaincu que les différences de mise en œuvre et le comportement d'arrondi à cela.


@holtavolt, intéressant. Vous comparez DX (avec GPU) contre DX avec Renderer de référence? Etrange, puisque # 010101010101 devrait être RVB (5,5,5), pas RVB (1,1,1) ...


@Stephen - correct sur le comparateur. Je crois que le # 0101/257 ==> RVB (1, ...) est un problème de représentation imagemagick, car ce n'est qu'un PNG de 24 ppp.


3 Réponses :


6
votes

DirectX exécute principalement dans le GPU et DX9 peut être des shaders. GDI + fonctionne sur des algorithmes complètement différents. Je ne pense pas qu'il soit raisonnable de s'attendre à ce que les deux puissent trouver exactement les sorties correspondantes de pixels.

J'attendrais que DX9 ait une meilleure qualité que GDI +, qui est une amélioration de l'étape sur l'ancien GDI, mais pas beaucoup. GDI + est bien compris pour avoir des problèmes avec des lignes anti-aliasing et la préservation de la qualité dans la mise à l'échelle de l'image (ce qui semble être votre problème). Afin d'avoir quelque chose de similaire en qualité que le traitement de la texture GPU de la dernière génération, vous devrez passer aux graphiques WPF. Cela donne une qualité similaire à DX.

WPF utilise également le GPU (si disponible) et redevient le rendu logiciel (s'il n'y a pas de GPU), donc la sortie entre le GPU et le rendu logiciel sont raisonnablement proches.

Edit: Bien que cela ait été cueilli comme la réponse, ce n'est qu'une tentative précoce d'expliquer et ne répond pas vraiment à la vraie raison. Le lecteur est renvoyé à des discussions énoncées dans les commentaires à la question et aux réponses à la place.


8 commentaires

Même réponse que j'ai envoyée à Hans s'applique ici. Pouvez-vous indiquer des problèmes spécifiques avec les problèmes d'échographie d'images GDI + qui expliquerait ceci (par exemple un défaut de document dans leur mode bilinéaire?) - Je ne trouve rien. Le code DX9 n'utilise pas de shaders et je suis sûr que je vais voir les mêmes résultats à l'aide du chemin de référence (logiciel), mais je vais essayer cela.


Bounty est toujours ouverte - quelqu'un connaît spécifiquement pourquoi DX9 génère un résultat différent de Bilinéar Interp que GDI +? Le pointeur à la source ou à une référence définitive obtient la prime.


@holtavolt, avez-vous essayé avec le rendu de référence logiciel dans DX9? Je suppose que si vous utilisez le filtrage du matériel GPU, cela peut dépendre des optimisations effectuées par le matériel GPU - E.G. améliorer les performances dans des endroits où personne n'est susceptible de remarquer.


Juste hors de curiosité, pouvez-vous poster les images qui montrent les différences?


DirectX, au moins en 9, n'utilise pas de mèches pour gérer beaucoup. Il fonctionne sur le GPU au lieu de la CPU, et le GPU est libre de gérer le filtrage de la manière la plus optimale pour son architecture.


@Stephen - Je vais essayer d'obtenir des échantillons d'images ensemble, je peux poster et utiliser le rendu de référence, n'avait pas pensé à cela.


Les sommets GDI sont spécifiés comme précision de flotteur. Toutes les informations que je peux trouver re. D3D indique qu'il utilise également un type presque IEEE-754 de type à flotteur (ceci est 10, pas 9 - peut-être que c'est un changement) -> msdn.microsoft.com/en-us/library/ccc308050 (v = vs.85) .aspx


@holtavolt, je ne parle pas de sommets. Je parle de Représentation des couleurs . AFAIK, GDI utilise une représentation de couleur 24 bits ou 32 bits, tandis que DX utilise 4xFloats. Interpolérer les couleurs avec des flotteurs vs. Int's vous donnera la diffuise 1 bits Diff.



2
votes

Pourquoi faites-vous l'hypothèse qu'ils utilisent la même formule?

même s'ils utilisent la même formule et que vous acceptez que la mise en œuvre soit différente, vous vous attendez à ce que la sortie soit la même?

À la fin de la journée, le code est conçu pour fonctionner avec la perception ne peut pas être mathématiquement précis. Bien que vous puissiez obtenir cela avec Cuda si vous voulez.

Plutôt que d'être surpris que vous obtenez différents résultats, je serais très surpris si vous avez des matchs parfaits pixels.

La façon dont ils représentent la couleur est différente ... Je sais pour un fait nvidia utilise un flotteur (peut-être doubler) pour représenter la couleur WHERAS GDI utilise INT Je crois.

http://fr.wikipedia.org/wiki/gpgpu

dans DX9 Shader 2.0 apparaît, ce qui est lorsque la mise en oeuvre de la couleur est commutée d'Int à 24 et 32 ​​bits.

Essayez de comparer le rendu ATI / AMD au rendu NVIDIA et vous pouvez clairement voir que la couleur est très différente. Je l'ai remarqué pour la première fois dans la quote-part 2 ... La différence entre les 2 cartes était stupéfiante - bien sûr, cela est dû à un grand nombre de choses, dont la moindre est la mise en œuvre de leur Bilinier Interp.

Edit: Les informations sur la manière dont la spécification a été apportée après avoir répondu. Quoi qu'il en soit, je pense que les types de données utilisés pour la stocker sont différents, quelle que soit la façon dont vous le spécifiez. De plus, la mise en œuvre du flotteur est likeley pour être différente. Je me trompe peut-être mais je suis sûr que c # implémente différemment au compilateur C que Nvidia utilise. (et cela suppose que GDI + ne convertit pas simplement le flotteur en l'équivalent ....)

Même si je me trompe à ce sujet, je tiens étroitement qu'il serait exceptionnel pour attendre 2 différentes implémentations d'un algorithme identique. Ils sont optimisés pour la vitesse, par conséquent, la différence d'optomisation traduira directement une différence de qualité d'image, car cette vitesse proviendra d'une approche différente de la coupe des coins / une approximation.


11 commentaires

+1 pour mettre en place le problème de la représentation de couleur flottante / octet. La différence d'un bit peut être simplement arrondir des différences.


J'ai ajouté un point de clarification - ce sont des images en niveaux de gris (r = g = b)


@John - Les éléments de couleur sont spécifiés de manière identique (GDI + = PixelFormat.Format32BPPPARGB, DX9 = FORMAT.X8R8G8B8)


Eh bien, je suis content d'avoir gaspillé mon temps à essayer de vous trouver des raisons concrètes de ne pas obtenir la réponse.


@holtavolt, c'est vrai. @John Nicholas était celui qui a proposé le problème de la représentation des couleurs - qui, à ce moment-là, semble la raison la plus probable. Je crois que vous pouvez toujours changer le prix de la Bounty, ce que je pense qu'il mérite.


HMM - Je vois que c'est ainsi en revue (bien que vous ayez beaucoup clarifié). John - Je vais voir si les modérateurs peuvent réaffecter, comme je ne le peux pas.


La réaffectation n'est pas possible. J'ai commencé une autre prime que je vais vous donner, John, une fois que la minuterie d'attribution de primes de 24 heures est terminée.


En fait, c'est moi qui devrait rendre la prime. Gosh, cela devient compliqué :-) Je ne sais pas pourquoi je ne pensais pas à cela auparavant. @holtavolt, lorsque vous avez assigné votre génération à @john Nicholas, je vais commencer une prime et vous l'attribuer. Je ne sais pas si je peux admettre une prime sur une question qui vous est revenue de votre part ...


@holtavolt, pouvez-vous demander aux modérateurs si cela est possible pour moi de commencer une prime sur une question posée par vous, puis récompensez la prime à vous - le propriétaire de la question?


@Stephen - Votre aide était aussi méritée, alors s'il vous plaît gardez-le - merci!


@holtavolt, eh bien si vous insistez ... :-D



0
votes

Il y a deux possibilités pour les différences de remontage. Le premier est évident, lorsque la valeur RVB est calculée en tant que fraction des valeurs de chaque côté. La seconde est plus subtile, lors du calcul du rapport à utiliser lors de la détermination de la fraction entre les deux points.

Je serais effectivement très surpris si deux implémentations différentes de l'algorithme étaient des pixels-for-pixels identiques, il y a tant d'endroits pour une différence de +/- 1 à se produire. Sans obtenir les détails exacts de la mise en œuvre des deux méthodes, il est impossible d'être plus précis que cela.


0 commentaires