In an effort to potentially improve upon and parameterize image stretching, I have put together a Pixelmath sequence to stretch images in a natural, but controllable manner, that in my initial testing, is showing to improve upon the multiple use of curves or histogram stretch and is easier to duplicate than "hand drawn" curves. The basis of this stretching are general hyperbolic decline decline/growth functions used in empirical forecasting of natural time series processes such as nuclear decay (exponential), chemical reaction rates (concentrations, catalyst site availability), oil,gas, and water well production decline (Arps (Arps, not Arp) equations), and even epidemiology.

I believe that the general hyperbolic stretch routines are suitable to for stretching (even in extremes) due to the properties that: they are piecewise continuous given natural look to the result, their first derivative is always positive (will always increase the pixel intensity - avoiding multiple stretch artifacts such as backwards stretches) and piecewise continuous (will avoid spike and backwards stretch situation that can arise with curves), will always, are normalized to run between 0 and 1 and will never "clip" data.

In base form, two parameters control the stretch, "D" and "b", with the maximum stretch intensity (dynamic range extension) occuring at 0 intensity (darkest part of image) and continuously reducing to the right hand side of the historgram. In this way, the left side of the histrogram has its contrast between pixels increased while the right hand side has it increased. D is used to control the amount of stretch that is applied in a give application. The equations will accept any value of D>0 (with 0 meaning no stretch/change at all) and 10 being the maximum that I find you can routinely use while controlling the results and <1 used to tweak results and final adjustments. The other parameter "b" can be thought of as the "stretch intensity", or how focused the actual stretch is around a single point. For bigger b, the stretch will be greater focused around a single intensity, while a lower b will spread the stretch around. Mathmatically, a b=0 represents a pure exponential stretch, while 0<b<1 represents a hyperbolic stretch, b=1 is a harmonic stretch, and b>1 is a highly intense, super-hyperbolic stretch. I find keeping b<2 useful, unless the image has very poor dynamic stretch.

Two additional parameters are defined: SP and HP. SP which defaults to 0 specifies the pixel intensity at which the maximum stretch is applied. (The stretch intensity will actually reduce (but stay positive) on either side of the SP input. Use SP>0 where the image is properly exposed (to the left of the histogram, but increases rapidly), and I have found a value less than the median (maybe 0.5x to 0.25x) is best. SP too low will tend to move the histogram to the right, provided excellent contrast to your noise - especially in conjuction with a large D. HP which defaults to 1, is a method to reserve dynamic range for the stars. Stars can easily be overstretched, so it is not uncommon to use a HP value as low as 0.6 or 0.65 to protect high intensity stars. Ideally HP should be set between the values of the brightest nebula and the intensity of the dimmest stars in danger of bloating.

That it, I could talk endlessly on how to do more complicated things with this stretch, but I was interested if anyone wanted to try it out. Unfortunately, I haven't the skills necessary to make an actual script or process out of it - and I'm not sure if there would be any uptake anyways. If you would like to try it, here is the pixelmath statements to copy/paste into pixelmath to generate you own utility. I set pixelmath to create a new image without rescaling.

// RGB Code:

iif(b==0,EC=1,EC=0);

iif(b>0,Ds=D*b,Ds=D);

iif(b>0,bs=b,bs=1);

iif(EC==1,q0=exp(-Ds*SP),q0=(1+Ds*SP)^(-1/bs));

iif(EC==1,qWP=2-exp(-Ds*(HP-SP)),qWP=2-(1+Ds*(HP-SP))^(-1/bs));

iif(EC==1,q1=2-2*exp(-Ds*(HP-SP))+exp(-Ds*(2*HP-SP-1)),q1=2-2*(1+Ds*(HP-SP))^(-1/bs)+(1+Ds*(2*HP-SP-1))^(-1/bs));

iif($T<SP,EC*exp(-Ds*(SP-$T))+(1-EC)*(1+Ds*(SP-$T))^(-1/bs)-q0,iif($T>HP,2-EC*(2*exp(-Ds*(HP-SP))+exp(-Ds*(2*HP-$T-SP)))+(1-EC)*(2*(1+Ds*(HP-SP))^(-1/bs)+(1+Ds*(2*HP-$T-SP))^(-1/bs))-q0,2-EC*exp(-Ds*($T-SP))-(1-EC)*(1+Ds*($T-SP))^(-1/bs)-q0))/(q1-q0);

// Parameters:

D = 2;

b =0.2;

SP =0.00;

HP =1.00;

Rnorm;

q0;

qWP;

q1;

Ds;

bs;

EC;

If there is any interest, I will compile a better explanation of how to use it (with graphs and such), how it can be used to increase the contrast/dynamic on stars, how to normalize linear images to make the most of room between 0 and 1, how you can "duplicate" arcsinh stretch, use inverse images, etc. What I will need help with, is that as a Pixelmath "routine", there is no preview. The best advice is to watch your histogram - how spread is the histogram, and where do the bright stars lie. I open the histogram transformation windows and set the view to each stretching iteration as I make them.

Please let me know if you find this at all useful and want to try it. I will be using it going forward, reserving curves transformations for final tweaks only. (Note, none of my displayed images have made use of this yet, so please don't judge by my results - my problems with stretching are what caused me to create this in the first place).

Regards,

Dave Payne