[repo] kind = "recipe" [recipe] description = "Generate an 'SHO' (golden/blue Hubble palette) from HaOiii (and optionally SiiOiii) data. Green is the real Sii channel when present, otherwise a synthetic Ha/OIII blend." [[stages]] name = "palette_sho" description = "SHO palette: R=Ha, G=Sii-or-synthetic, B=OIII" tool.name = "siril" #role = "palette" # Channels are selected out of the prior stage outputs by filename suffix # (_Ha / _OIII / _Sii). We load them by absolute path and re-save under canonical # names so Siril PixelMath ($Ha$ / $OIII$) can reference them. # # The green line is a single template expression: if a _Sii channel is present it # expands to `load ` (real Sii on green), otherwise to a `pm` blend of Ha+OIII. script = ''' # Bring the noise-reduced Ha and OIII channels in under canonical names. # Rescale each to 0..1 so all three channels enter rgbcomp on the same # footing (otherwise Ha/OIII dominate green and the result goes magenta). load {[str(p) for p in input["ha"].full_paths if "_Ha" in str(p)][0]} save Ha pm "$Ha$" -rescale 0.0 1.0 save Ha load {[str(p) for p in input["oiii"].full_paths if "_OIII" in str(p)][0]} save OIII pm "$OIII$" -rescale 0.0 1.0 save OIII # Green channel: use the real Sii channel if present, otherwise synthesize one from Ha+OIII {'load ' + [str(p) for p in input["sii"].full_paths if "_Sii" in str(p)][0] if [str(p) for p in input["sii"].full_paths if "_Sii" in str(p)] else 'pm "$Ha$ * ' + str(parameters.ha_weight) + ' + $OIII$ * ' + str(parameters.oiii_weight) + '" -rescale 0.0 1.0'} save SynGreen # Rescale Sii to 0..1 like Ha/OIII above. Without this the raw Sii # background (~5e-4) is ~20x brighter than the rescaled Ha/OIII # backgrounds (~2e-5) and the composite goes bright green. pm "$SynGreen$" -rescale 0.0 1.0 save SynGreen # Merge into an SHO image: R=Ha, G=SynGreen (Sii or synthetic), B=OIII rgbcomp Ha SynGreen OIII -out=results # Remove the residual magenta/purple cast. SCNR (rmgreen) only removes # *green*, so we invert (magenta -> green), run rmgreen, then invert back. pm "1 - $results$" rmgreen save results pm "1 - $results$" save results # Move to the final output location load results save "{output.full_paths[0]}" ''' [[stages.inputs]] kind = "job" name = "ha" # The Ha channel is required. Keep this requirement on its own input block so # min_count applies to this channel rather than to each multiplexed upstream # task (which contains only one channel). after = "denoise" # the role name: whichever denoiser will actually run [[stages.inputs.requires]] kind = "metadata" name = "filter" value = ["HaOiii", "SiiOiii"] [[stages.inputs.requires]] kind = "filename" value = "_Ha" [[stages.inputs.requires]] kind = "min_count" value = 1 [[stages.inputs]] kind = "job" name = "oiii" # OIII is also required, but has its own min_count requirement. after = "denoise" # the role name: whichever denoiser will actually run [[stages.inputs.requires]] kind = "metadata" name = "filter" value = ["HaOiii", "SiiOiii"] [[stages.inputs.requires]] kind = "filename" value = "_OIII" [[stages.inputs.requires]] kind = "min_count" value = 1 [[stages.inputs]] kind = "job" name = "sii" # Sii is optional: without min_count, an absent matching input is represented # by an empty FileInfo and the script synthesizes green instead. after = "denoise" # the role name: whichever denoiser will actually run optional = true [[stages.inputs.requires]] kind = "metadata" name = "filter" value = ["SiiOiii"] [[stages.inputs.requires]] kind = "filename" value = "_Sii" [[stages.outputs]] kind = "processed" name = ["SHO.fits"] [[stages.parameters]] name = "ha_weight" default = 0.4 # Value used if not overriden description = "Ha contribution when synthesizing the green channel (0..1)" [[stages.parameters]] name = "oiii_weight" default = 0.6 # Value used if not overriden description = "OIII contribution when synthesizing the green channel (0..1)"