Anthony suggested simplifying the sigmoidal-contrast enhancement code
to the following function:
sigmoidal(x) = 1/(1+exp(contrast*(midpoint-x)))
scaled by:
( sigmoidal(x)-sigmoidal(0) )/( sigmoidal(1)-sigmoidal(0) )
arguing:
>
"The function is simpler, applies the midpoint more logically and the
'contrast; value is equivelent to the original 'beta' value of the
original paper, and is completely independant in meaning from the value
of 'midpoint' as it should be."
This seems persuasive, but left open the derivation of
sigmoidal-contrast reduction. This email implements Anthony's
formulation, provides the equivalent solution for contrast reduction,
links to screenshots of gnuplot contrast curve examples, provides the
gnuplot info to try it yourself, and offers my attempt at c-code for
ImageMagick that implements it all.
Have a look at
http://www.pbase.com/lmarso/inbox
for screen captures of gnuplot examples applying contrast enhancement
and reduction with the formulas that follow.
Sigmoidal contrast *reduction* on an equivalent basis to Anthony's
formulation is:
sigmoidal_inverse(x)=midpoint-log((1-xi(x))/xi(x))/contrast
where xi(x) scales the *input* to a range appropriate to match
Anthony's contrast enhancement formulation.
xi(x)=min+x*(max-min)
where
min=(1/(1+exp(midpoint*contrast)))
and
max=(1/(1+exp(contrast*(midpoint-1))))
Cut and paste the following into gnuplot to generate your own examples:
set grid
set xtics .1
set ytics .1
midpoint=0.5
contrast=10
sigmoidal(x) = 1/(1+exp(contrast*(midpoint-x)))
sigmoidal_scaled(x)=(sigmoidal(x)-sigmoidal(0) )/( sigmoidal(1)-sigmoidal(0))
sigmoidal_inverse(x)=midpoint-log((1-x)/x)/contrast
min=(1/(1+exp(midpoint*contrast)))
max=(1/(1+exp(contrast*(midpoint-1))))
xi(x)=min+x*(max-min)
plot [0:1] x,sigmoidal_scaled(x),0.5,sigmoidal_inverse(xi(x))
***NOTE, if you change the midpoint and contrast, you must rerun the
formulas that follow to generate the proper graphs***
Here is my attempt at reworking magick/enhance.c at line 1759. For
both contrast enhancement and reduction, it embeds the scaling
long-hand in the calculation.
for (i=0; i <= (long) MaxMap; i++)
{
if (sharpen != MagickFalse)
{
sigmoidal_map[i]=(MagickRealType) ScaleMapToQuantum(MaxMap*
((1/(1+exp(contrast*(midpoint/MaxRGB-(double) i/MaxMap))))-
(1/(1+exp(contrast*(midpoint/MaxRGB)))))/((1/(1+exp(contrast*
(midpoint/MaxRGB-1))))-(1/(1+exp(contrast*(midpoint/MaxRGB))))));
continue;
}
sigmoidal_map[i]=(MagickRealType) ScaleMapToQuantum(MaxMap*
(midpoint/MaxRGB-log((1-(1/(1+exp(midpoint/MaxRGB*contrast))+((double)
i/MaxMap)*
((1/(1+exp(contrast*(midpoint/MaxRGB-1))))-(1/(1+exp(midpoint/MaxRGB*contrast))))))/
(1/(1+exp(midpoint/MaxRGB*contrast))+((double)
i/MaxMap)*((1/(1+exp(contrast*
(midpoint/MaxRGB-1))))-(1/(1+exp(midpoint/MaxRGB*contrast))))))/contrast));
}
Seems to work.
Best regards.