Starnett++ and Siril

By Fred Denjean

This tutorial aims to present a python script allowing to simply interface Starnet++ and Siril. This tool meets the needs of users to be able to manipulate images from Starnet++ with Siril and especially Pixel Math.

Why this script?

Images from Siril pre-processing are in standard FITS/32bit format. However, the images usable by Starnet++ must be in TIFF/16bit format. They must therefore be converted. Similarly, Starnet++ output images are in TIFF/16bit. If you want to benefit from Siril’s calculation precision, you have to convert them to FITS/32bit. This format is also the only one supported by Siril’s PixelMath tool.

Prerequisites

The goal is to use here a script written in python (that everyone can adapt), and based on the Siril ecosystem. For this, you will first need:

The python script must then be placed in this directory, just next to the executable starnet++.exe.

The script place

The script place

What does this script?

  • It transforms the input files in FITS/32 into TIFF/16bit file(s)
  • It launches Starnet++
  • It re-transforms the result file into FITS/32
  • It updates the FILTER field (FITS header) of the generated files to make them compatible with PixelMath

The file(s) only need to be “dragged” onto the Python script as shown in the video.

The input files

  • They must be of the FIT/FITS type.
  • The best is to use files whose histogram has already been stretched.
  • It can be a mono file (R, G or B) from a split command or Ha and/or OIII files from an extract command.
  • It can be an RGB file (3 layers therefore).
Example of input files set

Example of input files set

The output files

At the output of the script, 2 possibilities:

  • If the input file was mono, only the myfile_s.fits file is generated. _s for “starless” …
  • If the input file was in RGB, in addition to the myfile_s.fits file, the myfile_stars.fits file (the star mask) is also generated.
Example of the corresponding output files

Example of the corresponding output files

What about PixelMath?

The PixelMath tool recently introduced in Siril makes it possible to manage the images to be handled thanks to a sorting facilitated by the FILTER field of the FITS header.

This python script remains in this perspective and updates this FILTER field. Thereby:

  • the starless file, whose name is of the type myfile_s.fits, also have a field of the type FILTER = CLS_sls (if CLS was the original value…).
  • the star mask, whose name is of type myfile_stars.fits, also have a field of type FILTER = CLS_str (if CLS was the original value…).

Let’s see an example

Here it is a mix of mono images (R.fits, G.fits, B.fits) and an RGB image (result2-CLS.fits) made with a CLS filter (and therefore FILTER=CLS).

These 4 images are dragged simultaneously onto the script, and after several minutes of calculation, we see the resulting files.

Then, by opening Siril and PixelMath, one can realize the efficiency of sorting images according to the updated FILTER values. This effect is seen on mono images, then on RGB images.

The python script

Just copy the code below and save to a file named AutoStarnetFITS.py (for example).

import subprocess
import sys
import os
from PIL import Image
from pysiril.siril import Siril
from pysiril.addons import Addons
from pysiril.wrapper import Wrapper
import pathlib
from astropy.io import fits


def Run(*args, trailer= '_s', stride= '256', keep_open=True ):   
  
    #preparing pysiril
    workdir = os.getcwd()
    print('Starting PySiril')
    app = Siril()
    AO = Addons(app)
    cmd = Wrapper(app)
    app.tr.Configure(False,False,True,True)
    app.MuteSiril(True)
    app.Open()
    print('Starting Siril')
        
    for file_in in args:
        print('\nFile to process :', str(file_in))
        #Get attributes (name and extension) of the input file
        filename, inputfile_extension=os.path.splitext(file_in)
        
        if inputfile_extension!='.fits' and inputfile_extension!='.fit': 
            print ('ABORTED: Not a FIT/FITS file')
            return
    
        #Get the FILTER value of the FITS header
        hdul = fits.open(file_in, mode='update')
        try:
            fltr0=hdul[0].header['FILTER']        
        except Exception as e :
            print("\n******* " +  str(e) + "in "+ str(file_in))
            print("*******  Creating the missing field... " + "\n" )
            hdr= hdul[0].header
            hdr.insert('DATE', ('FILTER', '   '))
            hdul.flush()
            fltr0=hdul[0].header['FILTER']
        print("FILTER = " +  str(fltr0) + "\n" )
        hdul.close(output_verify='fix')
        
        #Set pysiril
        app.Execute('setext {:s}'.format(inputfile_extension))
        app.Execute('cd {:s}'.format(workdir))

        # Open the original input 32b FITS file
        # convert it into 16b and save as TIF file with the same name
        cmd.load(filename)          
        cmd.set16bits
        cmd.savetif(filename)

        #Define the 16b version of the initial file, used as input for Starnet++
        filename_tif = filename + '.tif'   
    
        #Get attributes (name and extension) of the last file
        fileroot, file_extension=os.path.splitext(filename_tif)
    
        #Define the output file of Starnet++
        outputfile_extension=".tif"
        outputfilename=filename+trailer+outputfile_extension
        
        
        #String Compatibility test
        if ' ' in filename_tif:
            print ('FAILURE: No space allowed in file name or file path')
            return

            
        #Define image mode
        im = Image.open(filename_tif)
        imode=im.mode
        im.close()
        need_stars = True
        if imode=='I;16': 
            need_stars = False
        
##Few results of "mode" method according to file format input
##          color   Mono
##fits 32     F       F
##fits 16     F       F
##fits 16u    F       F
##fits 8      F       F
##Tif 32      /       F
##Tif 16      RGB     I;16  (the only file format unsable by Starnet)
##Tif 8       RGB     L


        #Compatibility test
        if ((imode=='I;16' or imode=='RGB') and file_extension=='.tif') :
            #set argument for Starnet++
            args= "starnet++.exe " + filename_tif +" "+ outputfilename + " " + stride
            print ('Starnet++ is running... ')
            subprocess.call(args, shell=True)
        else: 
            print ('Not a TIF/16bit file')
            return
    
        #Convert the result starless file into a 32b version 
        starless_filename = fileroot + '_s'
        cmd.load(starless_filename)             #Load bla_s.tif
        cmd.set32bits
        cmd.fmul('1.0')
        cmd.save(starless_filename)             #Save bla_s.fits
    
        #Creation of the Stars.fits
        if need_stars == True:
            folder,_=os.path.split(starless_filename)
            #stars=folder+"\\" + fileroot + "_stars.fits"
            stars=filename+"_stars"
            cmd.load(file_in)                 #Load bla.FITS, stars+neb
            cmd.isub(starless_filename)             #substract starless version
            cmd.save(stars)                         #save stars.FITS

        #Deletion of TIF temp files  
        tokill=starless_filename + '.tif'         
        os.remove(tokill)                       #Remove bla_s.tif  
        os.remove(filename_tif)                 #Remove bla.tif 
    
        ########################################
        ########################################
        #Set the FILTER value in the FITS header
        # At first, for the Starless file
        sl_name=starless_filename+'.fits'
        #print ('starless file2: ', sl_name)
        hdul = fits.open(sl_name, mode='update')
    
        try:
            fltr=hdul[0].header['FILTER']        
        except Exception as e :
            print("******* " +  str(e) + "in "+ str(sl_name))
            print("******* " +  " Creating the missing field... ")
            hdr= hdul[0].header
            hdr.insert('DATE', ('FILTER', ' '))
            hdul.flush()
            fltr=fltr0
    
        hdr = hdul[0].header
        newfltr= str(fltr)+'_sls' 
        hdr['FILTER'] = newfltr
        hdul.close(output_verify='fix')
    
        ########################################    
        # ...and then for the Stars file    
        if need_stars == True:
            stars=stars+'.fits'
            #print ('stars file2: ', stars)
            hdul = fits.open(stars, mode='update')
    
            try:
                fltr=hdul[0].header['FILTER']        
            except Exception as e :
                print("******* " +  str(e) + "in "+ str(stars))
                print("******* " +  " Creating the missing field... ")
                hdr= hdul[0].header
                hdr.insert('DATE', ('FILTER', ' '))
                hdul.flush()
                fltr=fltr0
      
            hdr = hdul[0].header
            newfltr= str(fltr)+'_str' 
            hdr['FILTER'] = newfltr
            hdul.close(output_verify='fix')
        ########################################  
   
    app.Close()
    del app
    print ('Stop')

if __name__ == "__main__":
    Run(*sys.argv[1:])

It’s up to you now!!!