Skip to content

Setup and Parameter Guide for astro-color-stretch 

Horsehead Nebula with reflection nebula NGC2023

Setup guide for astro-color-stretch Version 1.2
Updated April 2, 2026

The following parameters are in the user modifiable variables section of the Python program code. They are described below in functional detail with recommendations for setting.

File Functions:

dirpath – String specifying the OS path location of the input file as well as where the output files will be written.

infile – String specifying the name of the input file (filename.ext) to be processed. File types that can be processed are tif, jpg, and png. Supported data types for these file formats are uint8, uint16, and float32.

Unsigned 8-bit (uint8) files are scaled up to 16 bits (0-65535) for processing. Uint8 is, however, the least desirable data type for processing since this only scales up the limited 8-bit per channel information. Some color shift, especially in compressed jpg files, is likely. Tif files encoded in uint16 or float32 are the best choice. Stacked output files like those from Deep Sky Stacker (DSS) are normalized [0-1] float32 and are scaled to 0-65535.  All image arrays are processed using float64 math.

ftype – Output file type specifier:  0 – tif, 1 – jpg, 2 – png

write_full – Boolean, set to True to include the input parameter settings in the output file name. False writes a shortened version (infile-color-stretched.ext). Multiple runs do not overwrite previous versions. A -1, -2, -3, etc. suffix is added to successive identical file names.

plot_hist – Boolean specifying whether to plot the input file histogram to a file in the specified path, dirpath.

Image Stretching and Curves:

stretch_type – Determines the type of stretch to perform. 0 – no stretch, 1 – root-power stretch, 2 – asinh stretch, 3 – log stretch.

Root-Power-Stretching:
The root-power-stretch is done according to the following:

output = (input)x     where x = 1 / root-power     (2)

rootiter – Set to 1 for a single pass stretch using the value specified in rootpower. Set to 2 for an added second pass using the value specified in rootpower2

rootpower – Value for a single pass root-power stretch or for the first pass of two.

rootpower2 – Value used for the second pass root-power stretch.


Asinh Stretching:
The asinh stretch option is done according to the following:

output = asinh(input / Kf) / asinh(Kf)     (3)

asinhiter – Set to 1 for a single pass stretch using the value specified in K1. Set to 2 to add a second pass using the value specified in K2.

K1 – Kf value used for a single pass asinh stretch or for the first pass of two.

K2 – Kf Value used for the second pass asinh stretch.


Log Stretching:
The log stretch option is done according to the following:

output = log(+ kf  *  input) / log(+ kf)    (4)

logiter – Set to 1 for a single pass stretch using the value specified in logK1. Set to 2 to add a second pass using the value specified in logK2.

logK1 – kf value used for a single pass log stretch or for the first pass of two.

logK2 – Kf Value used for the second pass log stretch.

Figure 1 below shows output vs. input plots of the root-power, asinh, and log stretch types for representative values of both single and two pass stretches.

Output vs Input plots of Root Power, asinh, and log image stretch function
Figure 1 - Root-Power, asinh, and log Stretch Curves

The best choice of stretch function depends both on the characteristics of the image data and on aesthetic preference. Root-power, asinh, and logarithmic stretches all belong to the same general class of monotonic nonlinear transforms used to compress image dynamic range. Their response curves are broadly similar as each increases the visibility of faint signal while preventing bright regions from saturating too rapidly.

With appropriate parameterization, the practical differences between these stretch methods are often minimal. In many cases—particularly when a two-pass stretch is employed—the resulting images from each stretch method can be made nearly indistinguishable. However, the transforms are not identical, differing primarily in their gain transfer behavior at low input levels. Depending on the image, this can be the dominant factor in single-pass stretches as applied to the low-intensity data. Experimentation with single or two-passes for the various methods is usually the best approach.

Choosing the amount of stretch with any method is largely dependent on the quality of the input image data. Images with good SNR can work well with root-powers or Kf values (for asinh and log) of 100 or more. Much higher values in a single pass stretch start to have a diminishing effect. Consider a two pass stretch if more stretch is needed and can be tolerated. A two-pass stretch with lower first pass root-power values of 6-10 or Kf values of 30-50 followed by a second pass of either in the 2-5 range is a good place to start. A higher first pass value followed by a lower second pass value typically works best.

On brighter images or those with a lot of sky glow, consider starting with a single pass stretch using much smaller root-powers or Kf values and working up from there. Such images may not tolerate a lot of stretching at all. Images with large gradients or vignetting should be fixed. See options for vignetting and gradient correction below.

s-curves:
scurveSet for the type of curve to apply:  0 – no curves 1 – scurve1  2 – a sequence of scurve1, scurve2
3 – a sequence of scurve1, scurve2, scurve1  4 – a sequence of scurve1, scurve2, scurve1, scurve2.

The input vs. output plots of scurve 1 and scurve 2 are shown in figure 2 below. They are based on the same curve equations in rnc-color-stretch. Curve 1 crosses from below linear in the lower input range, which will have the effect of adding contrast to the lower mid-tones. Curve 2 will brighten the overall image with slightly more bias on the upper brighter parts of the image .

Choosing the right curve or curve sequence (if any) depends on the image data, amount of initial RTP/asinh stretch, and personal preference. I usually go with the more conservative curve 1 on my first run. Curve options 2, 3, and 4 progressively add more stretch if desired or tolerated.  It can take some experimentation to find the right combination of initial RTP/asinh stretch and curve sequence.

Figure 2 - Linear and s-curves

Color Corrrection:

color_correction_type  – 0 – No Correction, 1 – Ratio Method, 2 – HSV Method.

colorenhance – Color enhancement factor. Values greater than 1 add saturation, less than 1 decreases it for either color correction method. Range is 0.0 – 2.0.

gamma – Adds a signal dependent saturation curve in the HSV method. Higher gamma levels add greater saturation to brighter levels, lower levels desaturate. Set to 1.0 for no effect. Range is 0.1 – 10.0.

The ratio-based color correction method restores color by comparing the channel ratios in the original sky-zeroed image with those in the stretched image. It basically adjusts the stretched image so that its color ratios more closely match those of the original data. The correction strength is signal dependent and is reconstructed according to which color channel is dominant at each pixel, ensuring stable color recovery while preserving the stretched intensity. This method generally performs better for nebulae, particularly where significant color information exists at low signal levels. 

The HSV color correction method is simpler and more computationally efficient. It works by converting both the original sky-zeroed image and the stretched image to HSV (Hue–Saturation–Value). Color restoration is performed by replacing the stretched image’s hue and saturation channels with those from the original image, while retaining the stretched value (V) channel. This preserves the stretched nonlinear brightness. This method often produces better results for galaxies with bright features as it tends to preserve the appearance of high-signal color and fine galactic structural detail. 

Color Adjustment and White Balance:

HSV Adjustment:
HSVadjust – Optional additional adjustment of Hue, Saturation, Value, and Vibrance after color correction. Set to True to do additional HSV adjustments according to the parameter values below. These optional HSV adjustments can also be done in a post-processor. However, I have sometimes found them to be convenient for quickly comparing slightly different hue, saturation, or vibrance levels on multiple runs.

hue_adjust – Adjusts hue from -120 to +120 degrees (0 to 360 degrees according on the standard HSV model). Zero degrees is no hue adjust.

sat_adjust – Adjusts saturation. Has a similar but stronger effect as colorenhance in colorcorrection. Set between 0.0  and 2.0 to reduce or increase saturation. No adjustment is 1.0. 

val_adjust – Set between 0.1 and 2.0 to darken or lighten. A value of 1.0 is no adjustment.

vib_adjust – Adjusts vibrance. Set between -1.0 and 1.0. Vibrance differs from saturation as it selectively boosts/cuts colors that are less saturated while protecting colors that are already strong. Straight saturation by comparison increases or decreases the intensity of all colors equally. Set to 0.0 for no vibrance adjust.

White Balance Adjustment:
White balance can also be easily adjusted in post processing. However, these adjustments can be used to ‘center’ up a white balance, giving the post-processor more range on either side for adjustment.

wb_mode – Method for adjusting white balance:
0 – None: No white balance adjustment is performed.

1 – gray-world method: Corrects general color cast (too blue or too warm) by scaling to an average gray across all
channels and scaling to adjust each channel so that its mean matches the gray average. This method generally works well for images of galaxies but not so well for more saturated, colorful objects like nebulae. 

2 – temp/tint adjustment: Adjusts white balance based on specified temp and tint factors.
temp –  > 1 warms the image, values < 1 cools the image (range 0.8 to 1.2)
tint –  > 1 more magenta,  < 1 more green (range 0.7 to 1.3)

Corrections:

Chromatic Aberration Correction:
ca_correct – Set to True to perform chromatic aberration correction. The effect is slight in order to avoid excessive color shifts and artifacts. The amount and efficacy will vary depending on the image.

Vignetting Correction:
vn_correct – Radial Vignette Correction enabled when set to True. Radial vignette correction corrects brightness falloff toward the edges using a least squares fit of intensity vs. radius. It assumes falloff is radially symmetric and smoothly varying.

vn_strength – Controls the amount of vignetting correction (range 0 to 100), representing 0% to 100% correction.

Gradient Correction:
lg_correct – Linear Gradient Correction enabled when set to True. Linear gradient correction corrects for linear gradients across the frame using a least squares fit. Gradients that can be fixed are horizontal linear, vertical linear, diagonal linear, and planar tilt (brighter in one corner, darker in the opposite). 

lg_strength – Controls the amount of gradient correction (range 0 to 100), representing 0% to 100% correction. 

Vignette correction and linear gradient correction can be applied independently or in combination, depending on the characteristics of the image. In some cases, either correction may be unnecessary or may even produce negative effects. Experimentation is needed to find what is optimal.

Vignette correction is most effective for images that are un-cropped or only lightly cropped. Without correction, stretched images often appear with a bright center and darker corners, making it difficult to establish a consistent post-stretch zero sky background level. Heavily cropped images typically exhibit little to no vignetting and likely will not benefit from this correction.

Linear gradient correction is recommended for images captured under conditions with significant sky glow or light pollution. These often exhibit a brightness gradient across the frame, typically from one edge to the other. Gradient correction can effectively mitigate this effect.

For both vignette and gradient corrections, a small number of pixel values in some images may exceed the 0–65,535 range. When this occurs, the software prints a warning to the output stream showing the full maximum and minimum values and the number of pixels outside this range. Any out-of-range values are clipped to fit within the 0–65,535 range. In my experience, this approach does not produce visible artifacts in the final output images. Reducing the correction strengths may also help minimize clipping. Note: The default clip method can be changed to scaling by setting mode = “scale” in the argument line of def  fix_out_of_bounds if desired. 

Note too that both corrections are computationally intensive and will increase processing time. For reference, a full-size uncorrected image typically processes in 30–40 seconds on a MacBook M4 Pro. Applying vignette correction increases this to just over one minute.

Star Size Reduction –
star_reduction – set to True to perform a star reduction on the final output image. This method uses a Difference of Gaussians (DoG) to detect stars , filters out small isolated pixels, followed by masking to shrink star halo sizes.

reduction_strength – Controls the amount of star size reduction (range 0.0 to 1.0). Specifying 0 represents no star size reduction.

Richardson-Lucy Deconvolution:

Optional Richardson-Lucy deconvolution implemented using the Python skimage module.

rl_deconvolve – Set to True to perform a Richardson-Lucy Deconvolution.

rl_iterations – Number of iterations used for Richardson–Lucy (RL) deconvolution. Range: 1–40.

Increasing the number of iterations generally enhances fine structural detail by progressively sharpening features blurred by the point spread function (PSF) – see below. The tradeoff, as with most sharpening methods, is potential degradation of image quality due to noise amplification and the formation of ringing artifacts

With very few iterations (e.g., one or two), the sharpening effect is usually minimal because the correction applied to the current image estimate is small. In practice, the optimal value depends on the image and its noise level. A reasonable starting point is rl_iterations = 8, which can be increased gradually until noise amplification or artifacts become objectionable. More iterations increase run time.

psf_sigma – Standard deviation of the Point Spread Function (PSF) in pixels. Range: 0.1 – 3.0.

Estimating the PSF (σ) can be approached using a number of methods. Two fairly straightforward methods are outlined below. 

Direct Measurement Full Width at Half Minimum (FWHM) Method
1. Pick a bright, unsaturated star in your image.
2. Fit a Gaussian profile to its brightness.
3. Measure the FWHM in pixels.
4. Convert FWHM to σ using:

σ = FWHM / 2√(2 ln(2))  ≈  FWHM / 2.355          (5)

Estimation from Sensor, Optics, and Seeing
This method uses a calculation based on plate scale and an estimate of expected seeing.

Calculate Plate Scale (sensor/lens angular resolution corresponding to one pixel):

Plate scale (arcsec/pixel) = 206.265 * pixel size (µm) / focal length (mm)     (6)

Estimate Atmospheric Seeing:
   Excellent site: ~0.5–1.0 arcsec
   Average site: ~1.5–2.5 arcsec (most likely for good, dark, skies)
   Poor site: >3 arcsec

The expected approximate FWHM in pixels is then:

FHWMseeing (pixels) = Seeing (arcsec) / Plate scale (arcsec/pixel)      (7)

Convert to σ using equation (5) above:

σ  FWHM / 2.355      (8)

 
For example the σ for a setup with a 600mm lens, sensor pixel pitch of 4.35μ, and an estimated 2 arc-sec seeing can be calculated using equations 6, 7, and 8 as follows:

Plate scale: 206.265 * 4.35/600  1.5 arcsec/pixel

FHWMseeing  = 2 arcsec / 1.5 arcsec/pixel  1.3 pixels

σ = FHWMseeing / 2.355 ≈ 0.6 pixels 

This method assumes FWHM is dominated by atmospheric turbulence (seeing). Measured FWHM, however, also includes the RMS sum of other independent sources according to:

FHWMtotal ≈ sqrt(FHWM2seeing + FHWM2optics + FHWM2tracking )   (9)

Where:
FHWMseeing   – Term for atmospheric turbulence.
FHWMoptics  – Diffraction, focus errors, and optical aberrations.
FHWMtracking – Mount drift, periodic error, or wind shake.

If diffraction, focus errors, optical performance, and mount issues are not negligible compared to atmospheric seeing, they will RMS add to FHWMtotal according to equation 9.

Selecting an initial PSF for Richardson–Lucy deconvolution is less about determining a precise value than establishing a reasonable starting estimate. In practice, psf_sigma = 0.8 and rl_iterations = 15 provide a good baseline for most long focal length astrophotography images. These parameters are interdependent: broadly speaking, a smaller psf_sigma requires fewer iterations, while a larger psf_sigma requires more. Optimal values and combinations are ultimately best determined empirically.

Internally, the program sets the kernel size to the nearest odd integer greater than or equal to The PSF kernel used in the calculations is a modified Moffat profile. Note: The PSF function can be changed to a simple straight Gaussian by setting the boolean in the def richardson_lucy_deconvolution arguments to False if desired.

Any noise reduction (recommended) should be done last after deconvolution in post for the final image.

Sky Level Factors:

skylevelfactor  Sky level relative to histogram peak. Default of 6% (0.06) seems to work well in most cases. Lowering can help improve zero sky color shift at the risk of not finding zero sky levels. Higher levels might be needed when zero sky levels are not found.

zeroskyred, zeroskygreen, zeroskyblue – Zero sky levels for respective rgb channels (0 – 25000). These values are set to 4096 as default, which works well in most cases. However, for dark sky levels that exhibit color shift from not finding a good black point, these parameters can be adjusted. Using the box_method described below can help in finding a dark sky region to optimize black point.

ofname_skyzero – Set to True to include the zero-zky levels in the output file name.

setmin – Set to true to perform a set minimum in the rgb data. This brings up the base level using scaling to assure there are no really dark pixels.

setminr, setming, setminb – Minimum values used for respective rgb channels. Defaults for each channel are set to 4096.

rgbskyzero_method – Method for determining image area for setting zero sky levels.
0 – full:  Use histograms of the entire image area for setting the zero sky levels.
1 – auto: Auto-scan for darkest area specified by window size: win_width x win_height for zero sky levels.
2 – manual:  Use user specified window coordinates, ulx, uly, and win_width x win_height for zero sky levels.

ulx, uly – Upper left x and y coordinates for manual selection of window for zero sky levels.

win_width,  win_height  Width and height of window in pixels for auto-scan and manual rgbskyzero_method.

win_frac – Set from 1 – 10. Window steps for sliding window defined by win_width x win_height across and down the image during auto-scan for the darkest region.

Use of the rgbskyzero_method:
In most images the results from using the full image area or a restricted dark area will be virtually identical. In cases where there is significant light pollution or a background bias, stretching to bring out faint details can exaggerate zero sky errors set by the estimated channel zero sky values. This will usually manifest in the dark areas as color shifts. Just running using a restricted dark sky area alone may help but will likely require additional individual adjustment of the zero sky parameters (zeroskyred, zeroskygreen, zeroskyblue) to fully improve dark area color shifts.

When either auto or manual rgbskyzero_methods  are selected, the program will generate a file showing the input file with an outline around the selected isolated area as well as a file of just the isolated area. The image of the selected dark region can then be stretched for easier identification of dark region color shifts. Program astro-color-stretch can be used to do the stretch (with parameter set back to rgbskyzero_method = 0) or it can be simply hand stretched in a post processor. The zero sky parameters can then be iteratively adjusted to find a better black point. Since astro-color-stretch runs very fast (< 1s for small dark region images), multiple runs can be done quickly to determine the optimal setting.

Histograms of the original input file as well as those of the zero sky subtracted full image and dark sky area will also be written to the selected path.

Choosing a win_width and win_height depends on the image. Too small an area compared to the full image results in a pixelated image with insufficient information. Too large, and the area will likely pick up more sky glow or gradient and negatively influence finding a good black point. By observing the general shape and size of the area that might be darkest in a given image is the best guide in determining the best window size and aspect ratio. I usually start with something at least in the neighborhood of 700 pixels on a side for a full-frame 45.6 MP image.

For the auto-scan, a win_frac  = 1 steps the full window across image without any overlap. This is the fastest and least accurate scan but works well in images with larger dark areas relative to the window size. Setting win_frac  = 10  increments the window scan by 1/10 of the window height and width. This is more accurate for finding the darkest area but takes a little longer. In practice this still scans quickly for even large 45.6MP full frame images.

Running astro-color-stretch

Program astro-color-stretch is a single Python file and is available in the astrophotography software downloads page. It is provided here free and open source according to the license agreement in the downloads page. Since this program is an adaptation of Roger Clark’s original rnc-color-stretch program, all previous copyright and license terms in his original license also apply.

The program was developed using PyCharm. I highly recommend this IDE to load and run astro-color-stretch for first time and even experienced users. It is reliable and easy to use. In order to run astro-color-stretch, a number of imports are required. Modules os, math, sys, and time are built-in so they should already be included in the current version of Python. CV2NumPy, scipy, skimage, and matplotlib, however, must be installed if not already done so by the user. This can be done either with pip install via a console window or from within PyCharm by going to PyCharm “settings…”. Under your project name, find “Python Interpreter” and then click the “+” (install) symbol. This will pull up a window where you can search for CV2 and NumPy to install. There are a number of internet resources that can help with these installations or debug any issues.

As I have not yet written a GUI or implemented an input file method for parameter input, the program parameters must be modified directly in the “user modifiable variables” section near the top of the program code. Comments in the code and the information provided above should easily guide the user. Beyond that, no programming knowledge of Python is required to run astro-color-stretch.

I have only run astro-color-stretch on Mac OS but it should run fine in Windows provided that the dirpath variable is set for the correct Windows path syntax. I am working on a Win 11 VMware virtual machine but have so far found it to be far too unstable to load and verify astro-color-stretch in Windows. Any feedback on issues or modifications needed with running this code on Windows are welcome via an email or in the comments.

Download
astro-color-stretch-1.2 can be downloaded here on the Astrophotography Software Page.

Additional Astrophotography Resources
Main Astrophotography Page