diff --git a/.gitignore b/.gitignore index 40e8897..14048c0 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,6 @@ models/** *.tif* *.zip *.tfevents.* +pjsr/ +safe/ +updates/ \ No newline at end of file diff --git a/astrodenoise/applayout.py b/astrodenoise/applayout.py index c793af0..242466e 100644 --- a/astrodenoise/applayout.py +++ b/astrodenoise/applayout.py @@ -296,7 +296,7 @@ def save(self, path): self.dismiss_popup() filepath = Path(path) - #AstroDeNoiseApp() + get_app(App.get_running_app()).lastpath = str(filepath.parent) if self.fits_headers is None: @@ -317,14 +317,19 @@ def save(self, path): else: result_tosave = np.array(self.currentimage.get_data('pre')[0]) + # Clip to range [0,1] before save + result_tosave = np.clip(result_tosave, 0, 1) + try: extension = filepath.suffix.lower() if extension in supported_save_formats_fits: - result_forsave = np.moveaxis(np.transpose(result_tosave),1,2) - write_fits(filepath,result_forsave,headers=self.fits_headers) + write_fits( + filepath, + np.moveaxis(np.transpose(result_tosave),1,2), + headers=self.fits_headers) elif extension in supported_save_formats_tiff: - result_tosave = (result_tosave - np.min(result_tosave)) / (np.max(result_tosave) - np.min(result_tosave)) + # Scale to unit16 for tiff result_tosave = (result_tosave * np.iinfo(np.uint16).max).astype(np.uint16) imsave(filepath,data=result_tosave) else: @@ -459,7 +464,11 @@ def denoise(self, data, C=-2.8,B=0.25): self.update_progress(0) expand_low_actual = 0.5 - (self.expand_low/2) - normalizer = STFNormalizer(C=C,B=B,expand_low=expand_low_actual,do_after=False) if self.normalize_enabled else NoNormalizer(expand_low=expand_low_actual) + # Strength shifts pixel values to right of histogram by up to 0.5 + #Strength=1 => Image + 0 + #Strength=0 => Image + 0.5 + normalizer = STFNormalizer(C=C,B=B,expand_low=expand_low_actual,do_after=False) if self.normalize_enabled else NoNormalizer(expand_low=expand_low_actual,do_after=False) + if self.denoise_enabled: with tf.device(f"/{self.selected_device}:0"): @@ -478,7 +487,7 @@ def denoise(self, data, C=-2.8,B=0.25): result = normalizer.before(np.moveaxis(np.transpose(data),0,1),'YX') self.update_progress(1) - return result + return result - expand_low_actual @mainthread def update_progress(self,progress): @@ -526,8 +535,9 @@ def get_label_data(self): } def get_texture(self, result): - image = (result - np.min(result)) / (np.max(result) - np.min(result)) - image = (image * 255).astype('uint8') + + result = np.clip(result, 0, 1) + image = (result * 255).astype('uint8') colorfmt='rgb' if image.shape[2] == 1: diff --git a/astrodenoise/cli.py b/astrodenoise/cli.py index 7613c46..9333036 100644 --- a/astrodenoise/cli.py +++ b/astrodenoise/cli.py @@ -1,7 +1,9 @@ import os +import sys import argparse import tensorflow as tf from tifffile import imread +from xisf import XISF import numpy as npp from pathlib import Path from os.path import join as path_join @@ -11,6 +13,13 @@ from astrodeep.utils.fits import read_fits, write_fits from astrodenoise.version import modelversion +def get_exepath(): + if getattr(sys, "frozen", False): + datadir = os.path.dirname(sys.executable) + else: + datadir = Path(os.path.dirname(__file__)).parent.as_posix() + return datadir + def cli(): parser = argparse.ArgumentParser() @@ -18,13 +27,13 @@ def cli(): parser.add_argument('input', type=str, nargs=1, help='Input image path, either tif or debayered fits file with data stored as 32bit float.') parser.add_argument('--model','-m', type=str, default=modelversion, help='Alternative model name to use for de-noising.') parser.add_argument('--models_folder', type=str, default='models', help='Alternative models folder root path.') - parser.add_argument('--tiles','-t', type=int, default=0, help='Use number of tiling slices when de-noising, useful for large images and limited memory.') + parser.add_argument('--tiles','-t', type=int, default=3, help='Use number of tiling slices when de-noising, useful for large images and limited memory.') parser.add_argument('--overwrite','-o', action='store_true', help='Allow overwrite of existing output file. Default: False when not specified.') parser.add_argument('--device','-d', choices=['GPU','CPU'], default='CPU', help='Optional select processing to target CPU or GCP. Default: CPU') parser.add_argument('--normalize','-n', action='store_true', help='Enable STFNormalization before de-noising. Default: False when not specified.') parser.add_argument('--norm-C', type=float, default=-2.8, help='C parameter for STF Normalization. Default: -2.8') - parser.add_argument('--norm-B', type=float, default=0.25, help='B parameter for STF Normalization, Higher B results in stronger stretch providing the ability target de-noising more effectively. . Default: 0.25, Range: 0 < B < 1') - parser.add_argument('--norm-restore', action='store_true', help='Restores output image to original data range after processing. Default: False when not specified.') + parser.add_argument('--norm-B', type=float, default=0.25, help='B parameter for STF Normalization, Higher B results in stronger stretch providing the ability target de-noising more effectively. . Default: 0.25, Range: 0 < B < 1') + parser.add_argument('--strength', type=float, default=0.5, help='The denoise strength applied. Default: 0.5') args = parser.parse_args() @@ -35,7 +44,9 @@ def predict(path,model): if path.suffix in ['.fit','.fits']: data, headers = read_fits(path) elif path.suffix in ['.tif','.tiff']: - data, headers = npp.moveaxis(imread(path),-1,0), None + data, headers = npp.moveaxis(imread(path),-1,0), None + elif path.suffix in ['.xisf']: + data, headers = npp.moveaxis(XISF(path).read_image(0),-1,0), None else: print("Skipping unsupported format. Allowed formats: .tiff/.tif/.fits/.fit") return @@ -46,33 +57,38 @@ def predict(path,model): if data.ndim == 2: data = data[npp.newaxis,...] - print("Processing file:",path) - print("Image Dimensions:",data.shape) + print(f"Processing file:{path}\n") + print(f"Image Dimensions: {data.shape}\n") - n_tiles = None if args.tiles == 0 else (args.tiles,args.tiles) + n_tiles = None if args.tiles == 0 else (args.tiles, args.tiles) if n_tiles is not None: print("Processing with tilling:",n_tiles) - output_denoised = [] + axes = 'YX' - normalizer = STFNormalizer(C=args.norm_C,B=args.norm_B,do_after=args.norm_restore) if args.normalize is True else NoNormalizer() - print("Using Normalization:",normalizer.params) + expand_low_actual = 0.5 - (args.strength/2) + normalizer = STFNormalizer(C=args.norm_C,B=args.norm_B,expand_low=expand_low_actual,do_after=True) if args.normalize is True else NoNormalizer(expand_low=expand_low_actual,do_after=True) + print(f"Using Normalization: {normalizer.params}\n") + print(f"Using Strength: {args.strength}\n") + output_denoised = [] for c in data: output_denoised.append( - model.predict(c, axes, normalizer=normalizer,resizer=PadAndCropResizer(), n_tiles=n_tiles) + model.predict(c, axes, normalizer=normalizer, resizer=PadAndCropResizer(), n_tiles=n_tiles) ) + output_denoised_arr = npp.asarray(output_denoised) + # Clip to [0,1] range + output = output_denoised_arr.clip(0,1) + output_file_name = path.stem + f"_denoised.fits" - output_path = path_join(path.parent, 'denoised') - Path(output_path).mkdir(exist_ok=True) - output_file_path = path_join(output_path, output_file_name) - write_fits(output_file_path, output_denoised, headers, args.overwrite) - print("Output file saved:", output_file_path) + output_file_path = path_join(path.parent, output_file_name) + write_fits(output_file_path, output, headers, args.overwrite) + print("Output file saved:", output_file_path) print("Loading model:", args.model) - model = CARE(config=None, name=args.model, basedir=args.models_folder) + model = CARE(config=None, name=args.model, basedir=Path(get_exepath()).joinpath(args.models_folder).as_posix()) file_or_path = args.input[0] if os.path.isfile(file_or_path): diff --git a/astrodenoise/imagelayout.py b/astrodenoise/imagelayout.py index bcdffd3..23b53ed 100644 --- a/astrodenoise/imagelayout.py +++ b/astrodenoise/imagelayout.py @@ -2,7 +2,7 @@ from kivy.properties import ObjectProperty, NumericProperty from kivy.graphics.texture import Texture from kivy.uix.label import Label - +from kivy.input.motionevent import MotionEvent class ImageViewLayout(AnchorLayout): region_x = NumericProperty(0) region_y = NumericProperty(0) @@ -52,124 +52,33 @@ def on_touch_down(self, touch): if self.imageout is None: return True - + if touch.is_mouse_scrolling: - if touch.button == 'scrolldown': - if self.scale < 10: - prev_w = self.imageout.width / self.scale - prev_h = self.imageout.height / self.scale - self.scale *= 1.1 - self.region_w = self.imageout.width / self.scale - self.region_h = self.imageout.height / self.scale - self.region_x += (prev_w-self.region_w) // 2 - self.region_y += (prev_h-self.region_h) // 2 - elif touch.button == 'scrollup': - if self.scale > 1: - prev_w = self.imageout.width / self.scale - prev_h = self.imageout.height / self.scale - self.scale /= 1.1 - self.region_w = self.imageout.width / self.scale - self.region_h = self.imageout.height / self.scale - - if (self.region_w > self.imageout.width) or (self.region_h > self.imageout.height): - self.region_w = self.imageout.width - self.region_h = self.imageout.height - - new_x = self.region_x + (prev_w-self.region_w) // 2 - new_y = self.region_y + (prev_h-self.region_h) // 2 - - if (new_x + self.region_w) > self.imageout.width: - self.region_x = self.imageout.width - self.region_w - elif new_x < 0: - self.region_x = 0 - else: - self.region_x += (prev_w-self.region_w) // 2 - - if (new_y + self.region_h) > self.imageout.height: - self.region_y = self.imageout.height - self.region_h - elif new_y < 0: - self.region_y = 0 - else: - self.region_y += (prev_h-self.region_h) // 2 - else: - self.scale = 1 - - self.displayimage.texture = self.imageout.get_region(self.region_x, self.region_y, self.region_w, self.region_h) - - else: - touch.grab(self) - - if touch.button == 'right' \ + self.render_scroll_zoom(touch) + elif touch.button == 'right' \ and self.imageorig is not None: self.setlabels() self.showlabels(True) - + self.render_slider_preview(touch) + touch.grab(self) + elif touch.button == 'left': + touch.grab(self) + return True def on_touch_move(self, touch): if not self.collide_point(*touch.pos): return super().on_touch_move(touch) - if self.imageout is None: + if self.imageout is None \ + or touch.grab_current is not self: return True - if touch.grab_current is self \ - and touch.button == 'left': - - imx, imy = self.displayimage.get_norm_image_size() - deltax = -touch.dx * (self.region_w/imx) - deltay = -touch.dy * (self.region_h/imy) - - new_x = self.region_x + deltax - new_y = self.region_y + deltay - if (new_x >= 0) and (new_x + self.region_w <= self.imageout.width): - self.region_x += deltax - - if (new_y >= 0) and (new_y + self.region_h <= self.imageout.height): - self.region_y += deltay - - self.displayimage.texture = self.imageout.get_region(self.region_x, self.region_y, self.region_w, self.region_h) - - #https://stackoverflow.com/questions/74543030/get-location-of-pixel-upon-click-in-kivy - if touch.grab_current is self \ - and touch.button == 'right' \ + if touch.button == 'left': + self.render_pan(touch) + elif touch.button == 'right' \ and self.imageorig is not None: - #touch.sync_with_dispatch = True - childImageNormImageSize_x = self.displayimage.norm_image_size[0] - #childImageNormImageSize_y = childImage.norm_image_size[1] - lr_space = (self.width - childImageNormImageSize_x) / 2 # empty space in Image widget left and right of actual image - #tb_space = (self.height - childImageNormImageSize_y) / 2 # empty space in Image widget above and below actual image - - pixel_x = touch.x - lr_space - self.x # x coordinate of touch measured from lower left of actual image - #pixel_y = touch.y - tb_space - self.y # y coordinate of touch measured from lower left of actual image - - if pixel_x > 0 and pixel_x < childImageNormImageSize_x: - #clicked inside image, coords: pixel_x, pixel_y - - image_x = int(pixel_x * self.region_w / childImageNormImageSize_x) - #image_y = pixel_y * self.region_h / childImageNormImageSize_y - - if image_x > 0 and image_x < self.region_w: - mixtexture = Texture.create(size=(self.region_w, self.region_h), colorfmt=self.imageout.colorfmt) - - mixtexture.blit_buffer( - self.imageout.get_region(self.region_x + image_x, self.region_y, self.region_w - image_x, self.region_h).pixels, - pos=(image_x, 0), - size=(self.region_w - image_x, self.region_h), - bufferfmt='ubyte', - colorfmt='rgba') - - mixtexture.blit_buffer( - self.imageorig.get_region(self.region_x, self.region_y, image_x, self.region_h).pixels, - pos=(0,0), - size=(image_x, self.region_h), - bufferfmt='ubyte', - colorfmt='rgba') - - mixtexture.flip_vertical() - mixtexture.mag_filter = 'linear' - mixtexture.min_filter = 'linear' - self.displayimage.texture = mixtexture + self.render_slider_preview(touch) return True @@ -185,3 +94,101 @@ def on_touch_up(self, touch): self.displayimage.texture = self.imageout.get_region(self.region_x, self.region_y, self.region_w, self.region_h) return True + + def render_pan(self, touch): + imx, imy = self.displayimage.get_norm_image_size() + deltax = -1 * touch.dx * (self.region_w/imx) + deltay = -1 * touch.dy * (self.region_h/imy) + + new_x = self.region_x + deltax + new_y = self.region_y + deltay + if (new_x >= 0) and (new_x + self.region_w <= self.imageout.width): + self.region_x += deltax + + if (new_y >= 0) and (new_y + self.region_h <= self.imageout.height): + self.region_y += deltay + + self.displayimage.texture = self.imageout.get_region(self.region_x, self.region_y, self.region_w, self.region_h) + + def render_scroll_zoom(self, touch: MotionEvent): + + if touch.button == 'scrolldown': + if self.scale < 10: + prev_w = self.imageout.width / self.scale + prev_h = self.imageout.height / self.scale + self.scale *= 1.1 + self.region_w = self.imageout.width / self.scale + self.region_h = self.imageout.height / self.scale + self.region_x += (prev_w-self.region_w) // 2 + self.region_y += (prev_h-self.region_h) // 2 + elif touch.button == 'scrollup': + if self.scale > 1: + prev_w = self.imageout.width / self.scale + prev_h = self.imageout.height / self.scale + self.scale /= 1.1 + self.region_w = self.imageout.width / self.scale + self.region_h = self.imageout.height / self.scale + + if (self.region_w > self.imageout.width) or (self.region_h > self.imageout.height): + self.region_w = self.imageout.width + self.region_h = self.imageout.height + + new_x = self.region_x + (prev_w-self.region_w) // 2 + new_y = self.region_y + (prev_h-self.region_h) // 2 + + if (new_x + self.region_w) > self.imageout.width: + self.region_x = self.imageout.width - self.region_w + elif new_x < 0: + self.region_x = 0 + else: + self.region_x += (prev_w-self.region_w) // 2 + + if (new_y + self.region_h) > self.imageout.height: + self.region_y = self.imageout.height - self.region_h + elif new_y < 0: + self.region_y = 0 + else: + self.region_y += (prev_h-self.region_h) // 2 + else: + self.scale = 1 + + self.displayimage.texture = self.imageout.get_region(self.region_x, self.region_y, self.region_w, self.region_h) + + def render_slider_preview(self, touch: MotionEvent): + #https://stackoverflow.com/questions/74543030/get-location-of-pixel-upon-click-in-kivy + + childImageNormImageSize_x = self.displayimage.norm_image_size[0] + #childImageNormImageSize_y = childImage.norm_image_size[1] + lr_space = (self.width - childImageNormImageSize_x) / 2 # empty space in Image widget left and right of actual image + #tb_space = (self.height - childImageNormImageSize_y) / 2 # empty space in Image widget above and below actual image + + pixel_x = touch.x - lr_space - self.x # x coordinate of touch measured from lower left of actual image + #pixel_y = touch.y - tb_space - self.y # y coordinate of touch measured from lower left of actual image + + if pixel_x > 0 and pixel_x < childImageNormImageSize_x: + #clicked inside image, coords: pixel_x, pixel_y + + image_x = int(pixel_x * self.region_w / childImageNormImageSize_x) + #image_y = pixel_y * self.region_h / childImageNormImageSize_y + + if image_x <= 0 or image_x > self.region_w - 1: + return + + mixtexture = Texture.create(size=(self.region_w, self.region_h), colorfmt=self.imageout.colorfmt) + mixtexture.blit_buffer( + self.imageout.get_region(self.region_x + image_x, self.region_y, self.region_w - image_x, self.region_h).pixels, + pos=(image_x, 0), + size=(self.region_w - image_x, self.region_h), + bufferfmt='ubyte', + colorfmt='rgba') + mixtexture.blit_buffer( + self.imageorig.get_region(self.region_x, self.region_y, image_x, self.region_h).pixels, + pos=(0,0), + size=(image_x, self.region_h), + bufferfmt='ubyte', + colorfmt='rgba') + mixtexture.flip_vertical() + mixtexture.mag_filter = 'linear' + mixtexture.min_filter = 'linear' + self.displayimage.texture = mixtexture + diff --git a/astrodenoise/version.py b/astrodenoise/version.py index 2c0a5a4..ece9371 100644 --- a/astrodenoise/version.py +++ b/astrodenoise/version.py @@ -1,2 +1,2 @@ -version = '0.5.2' +version = '0.5.8' modelversion = 'dist/v0.4.0-01' \ No newline at end of file diff --git a/csbdeep/data/prepare.py b/csbdeep/data/prepare.py index cf949cb..5400c66 100644 --- a/csbdeep/data/prepare.py +++ b/csbdeep/data/prepare.py @@ -85,7 +85,9 @@ def before(self, x, axes): def after(self, mean, scale, axes): self.do_after or _raise(ValueError()) - return mean, scale + + x = mean - self.expand_low + return x, scale @property def do_after(self): @@ -196,16 +198,13 @@ def before(self, x, axes): _x, self.m, self.c = STFPreProcessor.stf(x, self.C, self.B, axis) return _x + self.expand_low - def norm(self,data): - return (data - np.min(data)) / (np.max(data) - np.min(data)) - def after(self, mean, scale, axes): - self.do_after or _raise(ValueError()) + self.do_after or _raise(ValueError()) - # Mean requires normalising to [0,1] range else produces harsh clipping - mean = self.norm(mean) - - x_ = STFPreProcessor.rev_stf(mean,self.m,self.c) + x_ = mean - self.expand_low + # Clip mean to [0,1] range + x_ = np.clip(x_,0,1) + x_ = STFPreProcessor.rev_stf(x_,self.m,self.c) return x_, scale diff --git a/pixinsight/AstroDN.js b/pixinsight/AstroDN.js new file mode 100644 index 0000000..85b034b --- /dev/null +++ b/pixinsight/AstroDN.js @@ -0,0 +1,42 @@ +#feature-id AstroDenoise : Toolbox > AstroDenoise +#feature-info A script to run AstroDenoise from within PixInsight. + +#include "AstroDNCLI.js" +#include "AstroDNDialog.js" + +function main() { + console.hide(); + + if (Parameters.isViewTarget) { + var targetView = Parameters.targetView + } + else { + var targetView = ImageWindow.activeWindow.currentView; + } + + if ( !targetView || !targetView.id ) { + let mb = new MessageBox( + "
Select the image for denoising.
"; + this.imageViewSelectLabel.textAlignment = TextAlign_Left | TextAlign_VertCenter; + + this.imageViewSelector = new ViewList(this); + this.imageViewSelector.maxWidth = FORMWIDTH; + this.imageViewSelector.getMainViews(); + + with (this.imageViewSelectorFrame) { + sizer = new HorizontalSizer(); + + with (sizer) { + margin = 6; + add(this.imageViewSelectLabel); + addSpacing(8); + add(this.imageViewSelector); + adjustToContents(); + } + } + + this.imageViewSelector.onViewSelected = function (view) { + cli.targetView = view; + }; + + this.modelSelectionFrame = new Frame(this); + this.modelSelectionLabel = new Label(this); + this.modelSelectionLabel.text = "Model:"; + this.modelSelectionLabel.tooltip = "The selected AI denoise model.
"; + this.modelSelectionLabel.textAlignment = TextAlign_Left | TextAlign_VertCenter; + + this.modelSelectionList = new ComboBox(this); + this.modelSelectionList.addItem("dist/v0.3.0-01"); + this.modelSelectionList.addItem("dist/v0.4.0-01"); + this.modelSelectionList.addItem("dist/v0.4.0-02"); + this.modelSelectionList.addItem("dist/v0.5.0-01"); + + with (this.modelSelectionFrame) { + sizer = new HorizontalSizer(); + + with (sizer) { + margin = 6; + + add(this.modelSelectionLabel); + addSpacing(8); + add(this.modelSelectionList); + adjustToContents(); + } + } + + this.modelSelectionList.currentItem = this.modelSelectionList.findItem(astrodnParameters.model); + + this.modelSelectionList.onItemSelected = function (index) { + astrodnParameters.model = this.itemText(index); + } + + // Strength + this.strengthSlider = new NumericControl(this); + this.strengthSlider.label.text = "Strength:"; + this.strengthSlider.toolTip = "Increase or decrease the denoise strength.
"; + this.strengthSlider.setRange(0.0, 1.0); + this.strengthSlider.slider.setRange(0.0, 1000.0); + this.strengthSlider.setPrecision(3); + this.strengthSlider.setReal(true); + this.strengthSlider.setValue(astrodnParameters.strength); + + this.strengthSlider.onValueUpdated = function (t) { + astrodnParameters.strength = t; + } + + this.resetStrengthButton = new ToolButton(this); + this.resetStrengthButton.icon = this.scaledResource(":/icons/clear-inverted.png"); + this.resetStrengthButton.setScaledFixedSize(24, 24); + this.resetStrengthButton.toolTip = "Reset denoising strength.
"; + this.resetStrengthButton.onClick = () => { + astrodnParameters.strength = 0.5; + this.strengthSlider.setValue(0.5); + } + + this.strengthControl = new HorizontalSizer(); + this.strengthControl.maxWidth = FORMWIDTH; + this.strengthControl.margin = 6; + this.strengthControl.add(this.strengthSlider); + this.strengthControl.add(this.resetStrengthButton); + + //////////////////////// + + this.stfCSlider = new NumericControl(this); + this.stfCSlider.label.text = "STF Low Clipping:"; + this.stfCSlider.toolTip = "STF Stretch C (Low Clipping)
"; + this.stfCSlider.setRange(-4.0, 0.0); + this.stfCSlider.slider.setRange(0.0, 1000.0); + this.stfCSlider.setPrecision(3); + this.stfCSlider.setReal(true); + this.stfCSlider.setValue(astrodnParameters.stfC); + + this.stfCSlider.onValueUpdated = function (t) { + astrodnParameters.stfC = t; + } + + this.stfCResetButton = new ToolButton(this); + this.stfCResetButton.icon = this.scaledResource(":/icons/clear-inverted.png"); + this.stfCResetButton.setScaledFixedSize(24, 24); + this.stfCResetButton.toolTip = "Reset denoising strength.
"; + this.stfCResetButton.onClick = () => { + astrodnParameters.stfC = -2.8; + this.stfCSlider.setValue(-2.8); + } + + this.stfCControl = new Control( this ); + this.stfCControl.sizer = new HorizontalSizer(); + this.stfCControl.sizer.maxWidth = FORMWIDTH; + this.stfCControl.sizer.margin = 6; + this.stfCControl.sizer.add(this.stfCSlider); + this.stfCControl.sizer.add(this.stfCResetButton); + this.stfCControl.enabled = astrodnParameters.isstf(); + + /////////////////////// + + this.stfBSlider = new NumericControl(this); + this.stfBSlider.label.text = "STF Strength:"; + this.stfBSlider.toolTip = "STF Stretch B (Strength)
"; + this.stfBSlider.setRange(0.0, 1.0); + this.stfBSlider.slider.setRange(0.0, 1000.0); + this.stfBSlider.setPrecision(3); + this.stfBSlider.setReal(true); + this.stfBSlider.setValue(astrodnParameters.stfB); + + this.stfBSlider.onValueUpdated = function (t) { + astrodnParameters.stfB = t; + } + + this.stfBResetButton = new ToolButton(this); + this.stfBResetButton.icon = this.scaledResource(":/icons/clear-inverted.png"); + this.stfBResetButton.setScaledFixedSize(24, 24); + this.stfBResetButton.toolTip = "Reset denoising strength.
"; + this.stfBResetButton.onClick = () => { + astrodnParameters.stfB = 0.25; + this.stfBSlider.setValue(0.25); + } + + this.stfBControl = new Control( this ); + this.stfBControl.sizer = new HorizontalSizer(); + this.stfBControl.sizer.maxWidth = FORMWIDTH; + this.stfBControl.sizer.margin = 6; + this.stfBControl.sizer.add(this.stfBSlider); + this.stfBControl.sizer.add(this.stfBResetButton); + this.stfBControl.enabled = astrodnParameters.isstf(); + + //////////////////////// + // Process with STF + this.processSTF = new Frame; + this.processSTF.sizer = new HorizontalSizer; + this.processSTF.sizer.margin = 6; + this.processSTF.sizer.spacing = 6; + + this.processSTF.sizer.addStretch(); + + this.processSTFCheckbox = new CheckBox(this); + this.processSTFCheckbox.text = "Pre-process with STF"; + this.processSTFCheckbox.checked = astrodnParameters.stf; + this.processSTFCheckbox.toolTip = "For linear images, pre-process the image to denoise with STF stretch. The denoise process is best executed on non-linear images.
"; + this.processSTF.sizer.add(this.processSTFCheckbox); + + this.processSTFCheckbox.onCheck = function (checked) { + astrodnParameters.stf = checked; + this.dialog.stfCControl.enabled = astrodnParameters.isstf(); + this.dialog.stfBControl.enabled = astrodnParameters.isstf(); + } + + // Replace target view + this.replaceTargetFrame = new Frame; + this.replaceTargetFrame.sizer = new HorizontalSizer; + this.replaceTargetFrame.sizer.margin = 6; + this.replaceTargetFrame.sizer.spacing = 6; + + this.replaceTargetFrame.sizer.addStretch(); + + this.replaceTargetCheckbox = new CheckBox(this); + this.replaceTargetCheckbox.text = "Replace the target view"; + this.replaceTargetCheckbox.checked = astrodnParameters.replaceTarget; + this.replaceTargetCheckbox.toolTip = "Replaces the target view with the processed image, if checked. Otherwise, a new image will be created.
"; + this.replaceTargetFrame.sizer.add(this.replaceTargetCheckbox); + + this.replaceTargetCheckbox.onCheck = function (checked) { + astrodnParameters.replaceTarget = checked; + } + + this.buttonFrame = new Frame; + + this.buttonFrame.sizer = new HorizontalSizer; + this.buttonFrame.sizer.margin = 6; + this.buttonFrame.sizer.spacing = 6; + + this.newInstanceButton = new ToolButton(this); + this.newInstanceButton.icon = this.scaledResource(":/process-interface/new-instance.png"); + this.newInstanceButton.setScaledFixedSize(24, 24); + this.newInstanceButton.onMousePress = () => { + astrodnParameters.saveToInstance(); + Console.hide(); + this.newInstance(); + } + + this.ok_Button = new ToolButton(this); + this.ok_Button.icon = this.scaledResource(":/process-interface/execute.png"); + this.ok_Button.setScaledFixedSize(24, 24); + this.ok_Button.toolTip = "Execute.
"; + this.ok_Button.onClick = () => { + astrodnParameters.saveToFile(); + this.ok(); + cli.process(); + }; + + this.cancel_Button = new ToolButton(this); + this.cancel_Button.icon = this.scaledResource(":/process-interface/cancel.png"); + this.cancel_Button.setScaledFixedSize(24, 24); + this.cancel_Button.toolTip = "Close this dialog with no changes.
"; + this.cancel_Button.onClick = () => { + this.cancel(); + }; + + this.help_Button = new ToolButton(this); + this.help_Button.icon = this.scaledResource(":/process-interface/browse-documentation.png"); + this.help_Button.setScaledFixedSize(24, 24); + this.help_Button.toolTip = "Shows the script documentation.
"; + this.help_Button.onClick = () => { + Dialog.browseScriptDocumentation("AstroDN"); + }; + + this.reset_Button = new ToolButton(this); + this.reset_Button.icon = this.scaledResource(":/process-interface/reset.png"); + this.reset_Button.setScaledFixedSize(24, 24); + this.reset_Button.toolTip = "Resets all settings to their defaults.
"; + this.reset_Button.onClick = () => { + astrodnParameters.reset(); + this.dialog.modelSelectionList.currentItem = this.dialog.modelSelectionList.findItem(astrodnParameters.model); + this.dialog.strengthSlider.setValue(astrodnParameters.strength); + this.processSTFCheckbox.checked = astrodnParameters.stf; + this.dialog.stfCSlider.setValue(astrodnParameters.stfC); + this.dialog.stfBSlider.setValue(astrodnParameters.stfB); + this.dialog.replaceTargetCheckbox.checked = astrodnParameters.replaceTarget; + } + + this.buttonFrame.sizer.add(this.newInstanceButton); + this.buttonFrame.sizer.addSpacing(8); + this.buttonFrame.sizer.add(this.ok_Button); + this.buttonFrame.sizer.addSpacing(8); + this.buttonFrame.sizer.add(this.cancel_Button); + this.buttonFrame.sizer.addSpacing(32); + this.buttonFrame.sizer.add(this.help_Button); + this.buttonFrame.sizer.addSpacing(16); + this.buttonFrame.sizer.add(this.reset_Button); + + this.sizer = new VerticalSizer; + this.sizer.margin = 8; + + this.sizer.add(this.helpLabel); + this.sizer.addSpacing(8); + + this.sizer.add(this.imageViewSelectorFrame); + this.sizer.addSpacing(4); + this.sizer.add(this.modelSelectionFrame); + this.sizer.add(this.strengthControl); + this.sizer.addSpacing(4); + this.sizer.add(this.processSTF); + this.sizer.add(this.stfCControl); + this.sizer.add(this.stfBControl); + this.sizer.addSpacing(4); + this.sizer.add(this.replaceTargetFrame); + this.sizer.addSpacing(16); + + this.sizer.add(this.buttonFrame); + + if (cli.targetView !== undefined) { + this.imageViewSelector.currentView = cli.targetView; + } +} + +AstroDNDialog.prototype = new Dialog \ No newline at end of file