2009-09-25 8 views
5

Ma boucle interne contient un calcul que le profilage montre être problématique.Formule rapide pour une courbe "à contraste élevé"

L'idée est de prendre un pixel en niveaux de gris x (0 < = x < = 1), et "augmenter son contraste". Mes exigences sont assez lâche, il suffit de ce qui suit:

  • pour x < 0,5, 0 < = f (x) x <
  • pour x> 0,5, x < f (x) = 1 <
  • f (0) = 0
  • f (x) = 1 - f (1 - x), soit il doit être "symétrique"
  • De préférence, la fonction doit être lisse.

Ainsi, le graphique doit ressembler à ceci:

Graph.

J'ai deux implémentations (leurs résultats diffèrent, mais les deux sont): conforme

float cosContrastize(float i) { 
    return .5 - cos(x * pi)/2; 
} 

float mulContrastize(float i) { 
    if (i < .5) return i * i * 2; 
    i = 1 - i; 
    return 1 - i * i * 2; 
} 

Je demande, soit un microoptimization pour l'une de ces mises en œuvre, ou un original, plus rapide de votre formule.

Peut-être que l'un d'entre vous peut même tourner les bits;)

+0

Peut-être pourrions-nous vous aider à mieux nous dire quelle langue vous utilisez (je suppose Java) et quel est le compilateur/temps d'exécution impliqué. –

+0

C# avec un compilateur MS et l'exécution, mais je suis prêt à réécrire l'algo critique en C++ si je trouve que j'ai besoin de ... –

+0

C# avec les noms de méthodes camelCased? :( – Joren

Répondre

5

trivialement vous pouvez simplement seuil, mais j'imagine que cela est trop stupide:

return i < 0.5 ? 0.0 : 1.0; 

Puisque vous parlez de « contraste de plus en plus: » Je suppose que la les valeurs d'entrée sont des valeurs de luminance. Si c'est le cas, et ils sont discrets (peut-être c'est une valeur de 8 bits), vous pouvez utiliser une table de recherche pour le faire assez rapidement.

Votre 'mulContrastize' semble raisonnablement rapide. Une optimisation serait d'utiliser des maths entiers. Disons, encore une fois, que vos valeurs d'entrée pourraient être passées en tant que valeur non signée de 8 bits dans [0..255]. (? Encore une fois, peut-être une hypothèse bien) Vous pouvez faire quelque chose à peu près comme ...

int mulContrastize(int i) { 
    if (i < 128) return (i * i) >> 7; 
    // The shift is really: * 2/256 
    i = 255 - i; 
    return 255 - ((i * i) >> 7); 
+0

Un seuil est trop loin de lisse pour être utile dans mon cas, ce sont des valeurs de luminance, oui, ce ne sont pas des valeurs discrètes - ce sont en fait des flotteurs, pour deux raisons. OpenGL, les textures flottantes sont les plus rapides, et j'ai pris la décision d'utiliser des flottants de 0.0-1.0 pour faciliter mes calculs * et * rapides, mais je n'ai jamais pensé à contrastizing avec une table de recherche. il est plus important que le problème de texture d'OpenGL: l'implémentation que vous avez publiée est bien, mais pas aussi belle qu'une table de recherche, et mon mulContrastize est "raisonnablement rapide", mais pas dans une boucle interne aussi serrée :) –

+0

Btw, vous ne devriez pas t diviser par 255 deux fois, juste une fois. Donc, vous devriez changer de 7. –

+0

Oups, vous avez raison, cela normalise un pas trop loin. Va réparer l'exemple. –

13

Tenez compte des sigmoïde fonctions suivantes (bien en forme traduit à la plage souhaitée):

screenshot


I généré la figure ci-dessus Matlab. Si vous êtes intéressé, voici le code:

x = -3:.01:3; 
plot( x, 2*(x>=0)-1, ... 
     x, erf(x), ... 
     x, tanh(x), ... 
     x, 2*normcdf(x)-1, ... 
     x, 2*(1 ./ (1 + exp(-x)))-1, ... 
     x, 2*((x-min(x))./range(x))-1 ) 
legend({'hard' 'erf' 'tanh' 'normcdf' 'logit' 'linear'}) 
+1

+1. bien joué. –

+1

Le problème principal de l'OP est la vitesse. Comment cela accélère-t-il les choses? – tom10

+0

Merci, au moins je sais qu'ils s'appellent 'sigmoïde' maintenant;) J'ai fait une implémentation facile avec tanh, c'est aussi rapide que le cos. Le reste prendra un peu plus de temps et je pense qu'ils seront plus lents mais nous verrons. –

4

Une interpolation par morceaux peut être rapide et flexible.Il ne nécessite que quelques décisions suivies d'une multiplication et d'une addition, et peut approximer n'importe quelle courbe. Cela évite aussi la densité qui peut être introduite par les tables de recherche (ou le coût supplémentaire dans deux recherches suivi d'une interpolation pour lisser cela), bien que le lut puisse fonctionner parfaitement bien pour votre cas.

alt text http://i35.tinypic.com/fbeu0j.png

Avec seulement quelques segments, vous pouvez obtenir une assez bon match. Ici, il y aura de la couleur gradients, qui sera beaucoup plus difficile à détecter que la densité dans les couleurs absolues. Comme le remarque Eamon Nerbonne dans les commentaires, la segmentation peut être optimisée en «choisissant [vos] points de segmentation basés sur quelque chose comme la dérivée seconde pour maximiser le détail», c'est-à-dire là où la pente change le plus. Clairement, dans mon exemple affiché, avoir trois segments au milieu du segment de cinq segments n'ajoute pas beaucoup plus de détails.

+2

Et si vous êtes vraiment snazzy, vous pouvez choisir vos points de segmentation basés sur quelque chose comme la dérivée seconde pour maximiser les détails (pas de point de différencier les segments dans le segment central, assez droit). –

+0

@Eamon: Merci pour la deuxième idée dérivée. Je savais que j'étais paresseux avec les points centraux, mais j'aime vraiment la généralisation à la dérivée seconde. – tom10

Questions connexes