Author Topic: Faster version of MaskedStretch script  (Read 11785 times)

Offline Andres.Pozo

  • PTeam Member
  • PixInsight Padawan
  • ****
  • Posts: 927
Faster version of MaskedStretch script
« on: 2010 March 10 04:24:26 »
Hi,

I have been using for the last months a modified version of the MaskedStretch script and I think that it could be useful for someone. The original version by David Serrano works wonderfully, however, it is a bit slow. I have made some changes that can reduce a lot the computation time.

The main change is how to calculate the value of the MTF that it is applied in each step. The algorithm used by David is a binary search that applies N times the MTF to image varying the MTF parameter until the desired MTF is found. This would be very slow so the version by David uses an approximation that consists in applying the MTF to a reduced version of the image. This is the reason that the median of the resulting image is not the target median, although usually near enough.

In my version I use a different approximation. Instead applying the MTF to a thumbnail of the image, I apply the MTF only to the median value. Applying the MTF to a value is much faster than applying it to an image. The result is a MTF parameter very similar (although not the same) to that found by David's algorithm.

The result of the changes is that an stretch that in my computer takes 74.3 seconds with the version 0.8, only takes 25.8 with the version 0.9.

If anyone wants to try the script, this is the code:
Code: [Select]
/*
   masked-stretch.js v0.9

   Iteratively stretch histogram with an appropriate luminance mask each time.

   Copyright (C) 2007 David Serrano, 2010 Andres del Pozo

   This program is free software: you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by the
   Free Software Foundation, version 3 of the License.

   This program is distributed in the hope that it will be useful, but WITHOUT
   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
   more details.

   You should have received a copy of the GNU General Public License along with
   this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/*
    Special thanks go to (in chronological order):
    - Carlos Sonnenstein, Carlos Milovic and Juan Conejero for their invaluable
      contributions (ideas, knowledge, code and probably more).
    - My english teacher for telling me that a contribution can't be valuable ;)
      (refer to v0.4 if you don't get this).

    Changelog:
    0.9:   (By AdP) Several speed optimizations. It uses an approximation for
           calculating the median of the image after applying N times an MTF.
           Instead of using a thumbnail, the MTF is applied to the original
           median.
    0.8:   Now supports mask blurring with a convolution.
           User interface reorganization.
           Changed default parameters.
    0.7.1: Corrected a scoping bug spotted by Iñaki Lizaso.
    0.7:   Now masks can be blurred by removing wavelet layers with the à trous
           algorithm (dyadic scaling sequence).
           Bug fix: mask shadows clipping wasn't working at all.
           Better settings management, following the aberration-spotter scheme.
           Removed settings compatibility with v0.4.
    0.6.1: Got rid of the confirmation dialog when closing a temporary image.
    0.6:   User can now specify the amount of shadows that will be clipped in
           each mask.
    0.5.1: Adapted Settings.lastReadOK calls to latest PixInsight (build 329).
    0.5:   Code for calculating mtf completely rewritten (again). Now, instead
           of using a binary search like in 0.4, it does linear interpolations.
           New option to delete the settings saved by the script.
           Sets up an RGB working space that gives all three color components
           the same importance when calculating the luminance.
    0.4.1: Better Settings handling. Still reads v0.4 settings.
    0.4:   Code for calculating mtf completely rewritten. Now works over a
           small copy of the image, with real HT's and real masks.
           Increased maximum number of iters to 200. Users always want more ;).
           More code reorganization. Now I even like it :).
    0.3.1: Fixed a couple of issues with settings. Should work as expected now.
    0.3:   Now the code is more OO. Hope it's well structured.
           Uses the new Math.mtf() built-in function. Checks if iter == 0.
           Initial user interface (mostly copy & paste).
    0.2:   Some code reorganization.
           Now deduces MTF from given values: iterations and target median.
    0.1.1: Bug fix: masked was not being inverted.
    0.1:   Initial version.
*/

// TODO: restore the image's original RGBWS.
// TODO: couldn't manage to disable some GUI elements depending on which
//       RadioButton is pressed.

#define VERSION "0.9"
#define E 1e-4            // epsilon
#define SET_PREFIX "MST"
#define BLUR_METHOD_NONE 0
#define BLUR_METHOD_ATW  1
#define BLUR_METHOD_CONV 2

#include <pjsr/ColorSpace.jsh>
#include <pjsr/DataType.jsh>
#include <pjsr/FrameStyle.jsh>
#include <pjsr/Interpolation.jsh>
#include <pjsr/NumericControl.jsh>
#include <pjsr/ResizeMode.jsh>
#include <pjsr/SampleType.jsh>
#include <pjsr/Sizer.jsh>
#include <pjsr/UndoFlag.jsh>

#feature-id Utilities > MaskedStretch_0.9
#feature-info  At the time of stretching the histogram of an image, \
   it is better to do it several times changing the mask used every \
   time &mdash; a long, boring task that is perfect for script automation. \
   This script does precisely that.<br/>\
   <br/>\
   Copyright &copy; 2007 David Serrano, &copy; 2010 Andres del Pozo
//#feature-icon masked-stretch.xpm

function err_dialog() {   // bad name, I know
    this.__base__ = Dialog;
    this.__base__();

    this.l = new Label (this);
    with (this.l) {
        margin = 15;
        wordWrapping = true;
        minWidth = 500;
        text = "Unable to calculate MTF value, probably because the " +
            "median desired is too far from the current one. Increase " +
            "the number of iterations.";
    }

    this.b = new PushButton (this);
    this.b.text = " OK ";
    this.b.onClick = function() { this.dialog.ok(); };

    this.hsizer = new HorizontalSizer;
    with (this.hsizer) {
        margin = 6;
        spacing = 6;
        addStretch();
        add (this.b);
        addStretch();
    }

    this.sizer = new VerticalSizer;
    with (this.sizer) {
        margin = 6;
        spacing = 6;
        add (this.l);
        addSpacing (4);
        add (this.hsizer);
    }

    this.windowTitle = "Error";
    this.adjustToContents();
    this.setFixedSize();
}
err_dialog.prototype = new Dialog;
var err_dl = new err_dialog;

function mst_user_data() {
    this.iter = 15;
    this.target_median = 0.08;
    this.shadows_clipping = 0;   // mask shadows clipping
    this.blur_method = BLUR_METHOD_NONE;
    this.atw_layers = 2;         // numberOfLayers to remove to masks
    this.atw_scaling = 0;        // scaling function
    this.conv_pixels = 7;
    this.delete_settings = false;
}
var user_data = new mst_user_data;

function mst_engine() {
    var stats = new ImageStatistics;
    var win;    // image window we'll modify
    var hist = new HistogramTransformation;
    var shadows_clipping = new HistogramTransformation;
    var mtf;

    this.atw_scaling_function = function (name, kernel) {
        this.name = name;
        this.kernel = kernel;
    }

    this.atw_scaling_functions = new Array (
        new this.atw_scaling_function ("3x3 Linear Interpolation", [
            1/16, 1/8, 1/16,
            1/8,  1/4, 1/8,
            1/16, 1/8, 1/16
        ]),
        new this.atw_scaling_function ("3x3 Gaussian", [
            1/25, 1/5, 1/25,
            1/5,  1,   1/5,
            1/24, 1/5, 1/25
        ]),
        new this.atw_scaling_function ("5x5 B3 Spline", [
            1/256, 1/64, 3/128, 1/64, 1/256,
            1/64,  1/16, 3/32,  1/16, 1/64,
            3/128, 3/32, 9/64,  3/32, 3/128,
            1/64,  1/16, 3/32,  1/16, 1/64,
            1/256, 1/64, 3/128, 1/64, 1/256
        ]),
        new this.atw_scaling_function ("5x5 Gaussian", [
            0.00723,0.0459, 0.085,  0.0459, 0.00723,
            0.0459, 0.29155,0.53995,0.29155,0.0459,
            0.085,  0.53995,1,      0.53995,0.085,
            0.0459, 0.29155,0.53995,0.29155,0.0459,
            0.00723,0.0459, 0.085,  0.0459, 0.00723
        ])
    );

    // Contributed by Juan Conejero. Will be removed when the same functionality
    // makes its way to the PCL.
    this.gaussian_filter = function (size, shape) {
        const epsilon = 0.01;        // maximum truncation error
        size = Math.max (3, size|1); // ensure odd dimension >= 3

        // The filter array
        var h = new Array;

        // Standard deviation of the filter distribution
        var sigma = (size >> 1) / Math.pow (-shape * Math.ln (epsilon), 1/shape);
        var rk = shape * Math.pow (sigma, shape);

        // Optimized code
        for (var i = 0, n2 = size >> 1, y = -n2; y <= n2; ++y) {
            if (y > 0) {
                for (var x = 0, yy = y+y; x < size; ++x, ++i) {
                    h[i] = h[i - yy*size];
                }
            } else {
                for (var x = -n2; x <= n2; ++x, ++i) {
                    h[i] = (x > 0) ?
                        h[i - (x+x)] :
                        Math.exp (-Math.pow (Math.sqrt (x*x + y*y), shape) / rk);
                }
            }
        }
        return h;
    }

    this.set_win = function (w) {
        if (w.isNull)
            throw Error ("Invalid/No image window.");

        this.win = w;
    }

    this.get_luma = function (view) {
        var luma=new Image;
        view.image.extractLuminance (luma);
        return luma;
    }

    // obtains luma, then its median.
    this.get_median = function (view) {
        var l = this.get_luma (view);
        stats.generate (l);
        return stats.median;
    };

    this.modify_mask = function (view) {
        // let's clip the shadows a bit
        if (user_data.shadows_clipping) {
            shadows_clipping.executeOn (view, false);
        }

        // do atw
        if (user_data.blur_method == BLUR_METHOD_ATW) {
            var atw = view.image.ATW (
                this.atw_scaling_functions[user_data.atw_scaling].kernel,
                user_data.atw_layers
            );
            with (view) {
                beginProcess (UndoFlag_NoSwapFile);
                image.transfer (atw[user_data.atw_layers]);  // residual layer
                endProcess();
            }
        }

        // convolution
        if (user_data.blur_method == BLUR_METHOD_CONV) {
            with (view) {
                beginProcess (UndoFlag_NoSwapFile);
                if (user_data.conv_pixels <= 15) {
                    image.convolve (
                        this.gaussian_filter (user_data.conv_pixels, 2)
                    );
                } else {
                    image.convolveFFT (
                        this.gaussian_filter (user_data.conv_pixels, 2)
                    );
                }
                endProcess();
            }
        }
    }

    this.stretch = function (win) {
        var v = win.mainView;

        hist.H = [
            // shadows, midtones, highlights, ext_shadows, ext_hilights
            [0, 0.5,      1, 0, 1], // R
            [0, 0.5,      1, 0, 1], // G
            [0, 0.5,      1, 0, 1], // B
            [0, this.mtf, 1, 0, 1], // RGB
            [0, 0.5,      1, 0, 1], // Alpha
        ];

        // window that will hold the luminance for masking
        var luma_w = new ImageWindow (
            500, 500, 1, v.image.bitsPerSample,
            v.image.sampleType == SampleType_Real,
            false, "MST_luma"
        );

        for (var i = 0; i < user_data.iter; i++) {
            var luma = this.get_luma (v);

            // transfer image to window, modify and apply as mask
            with (luma_w.mainView) {
                beginProcess (UndoFlag_NoSwapFile);
                image.transfer (luma);  // luma is no longer an Image after this
                endProcess();
            }

            this.modify_mask (luma_w.mainView);

            //luma_w.show();
            win.maskVisible = false;
            win.maskInverted = true;
            win.mask = luma_w;

            // stretch
            hist.executeOn (win.mainView, true);

            // clean up
            luma_w.removeMaskReferences();  // automatically removes mask from win
            gc();
        }

        luma_w.undoAll();
        luma_w.purge();
        luma_w.close();
        luma_w = null;
        gc();
    }

    // simulateStretch applies the MTF to the median. The result is an
    // approximation to applying the MTF to the image and then calculting the median
    this.simulateStretch = function (win) {
        var median = this.get_median(win.mainView);
        for (var i = 0; i < user_data.iter; i++)
            median=Math.mtf(this.mtf,median);
        console.writeln(format("MTF=%.5f median=%.5f", this.mtf, median));
        return median;
    }

    this._do_calc_mtf = function (w) {
        console.writeln("Calculating the MTF value:");
   
        var om = this.get_median (w.mainView);  // original median
        var tm = user_data.target_median;
        var it = user_data.iter;

        // if users don't try this, Murphy will do
        if (Math.abs (om - tm) < E) {
            this.mtf = 0.5;
            return;
        }

        var mtf1; var median1;
        var mtf2; var median2;
        var temp_median;

        if (om < tm) {    // going to lighten the image
            mtf1 = 0.20;
            this.mtf = mtf1;
            median1=this.simulateStretch(w);
            // did we have a lucky strike?
            if (Math.abs (median1 - tm) < E)
                return;  // this.mtf is already set
            if (tm > median1) {
                // user wants a big stretch, we won't handle it
                this.mtf = -1;
            }

            mtf2 = 0.50; median2 = om;
        } else {
            mtf1 = 0.50; median1 = om;

            mtf2 = 0.70;
            this.mtf = mtf2;
            median2=this.simulateStretch(w);

            // did we have a lucky strike?
            if (Math.abs (median2 - tm) < E)
                return;  // this.mtf is already set
            if (tm < median2) {
                // user wants a big stretch, we won't handle it
                this.mtf = -1;
            }
        }

        // we're not setting this.mtf to -1 in the following loop, but it
        // can have this value from the previous checks made. So this is
        // actually "if (-1 != this.mtf) { while (1) {  blah blah; } }"
        // with just one level of indentation
        while (-1 != this.mtf) {
            // heart of the algorithm
            this.mtf = (1 * (tm-median2) / (median1-median2)) * (mtf1-mtf2) + mtf2;
            temp_median=this.simulateStretch(w);

            if (Math.abs (temp_median - tm) < E)
                break;  // this.mtf is already set

            // USERS KEEP ON DEMANDING PERL!! ;)
            // (($temp_median > $tm) ? ($mtf1, $median1) : ($mtf2, $median2)) =
            //     ($this_mtf, $temp_median);
            if (temp_median > tm) {     // image too light
                mtf1 = this.mtf;
                median1 = temp_median;
            } else {
                mtf2 = this.mtf;
                median2 = temp_median;
            }
        }

        gc();
    }

    this.calc_mtf = function() {
        var v = this.win.mainView;
        var thumb_h = 300;
        var thumb_w= v.image.width*thumb_h/v.image.height;

        // apply a better RGB working space
        var rgbws = new RGBWorkingSpace;
        with (rgbws) {
            channels = [  // Y, x, y
                [1.000000, 0.648431, 0.330856],
                [1.000000, 0.321152, 0.597871],
                [1.000000, 0.155886, 0.066044]
            ];
            gamma = 2.20;
            sRGBGamma = true;
            applyGlobalRGBWS = false;
            executeOn (v);   // TODO: how to revert this?
        }

        // build HT object for the mask shadows clipping
        shadows_clipping.H = [
            // shadows, midtones, highlights, ext_shadows, ext_hilights
            [0,                          0.5, 1, 0, 1], // R
            [0,                          0.5, 1, 0, 1], // G
            [0,                          0.5, 1, 0, 1], // B
            [user_data.shadows_clipping, 0.5, 1, 0, 1], // RGB
            [0,                          0.5, 1, 0, 1], // Alpha
        ];

        this._do_calc_mtf (this.win);

        if (-1 == this.mtf) {
            err_dlg.execute();
            return false;
        }

        return true;
    }

    this.work = function() {
        this.stretch (this.win);
    }
}
var engine = new mst_engine;

function mst_dialog() {
    this.__base__ = Dialog;
    this.__base__();

    this.label_width = this.font.width ("Wavelets scaling function:");
    this.edit_width = this.font.width ( "0000000" );

    // help label
    this.helpLabel = new Label (this);
    with (this.helpLabel) {
        frameStyle = FrameStyle_Box;
        margin = 4;
        wordWrapping = true;
        useRichText = true;
        text = "<p><b>MaskedStretch v"+VERSION+"</b> &mdash; A " +
            "script to perform a histogram stretch of an image via several " +
            "small luminance-masked histogram transformations. This is good for " +
            "preventing small stars from growing without control.</p>" +
            "<p>Copyright &copy; 2007 David Serrano, &copy; 2010 Andres del Pozo</p>" +
            "<p>Choose the number of iterations and the median of the desired " +
            "image (typically in the range 0.05 - 0.10). Then click 'Ok' " +
            "and wait for a new image to be created. A smaller copy of the "+
            "image will show the progress of the script.</p>" +
            "<p>You can specify an optional number of <i>&agrave; trous</i> wavelet " +
            " layers to be removed from the mask in order to blur it. Alternatively, " +
            "it can be blurred using a convolution too.</p>" +
            "<p>This program can't do aggressive changes with few iterations. " +
            "An error message will appear if you try to do so.</p>";
    }

    // iterations
    this.iter_NC = new NumericControl (this);
    with (this.iter_NC) {
        label.text = "Iterations:";
        label.minWidth = this.label_width + 6+1; // align with labels inside GroupBox below
        setRange (1, 200);
        slider.setRange (0, 199);
        //slider.minWidth = 150;
        setPrecision (0);
        setValue (user_data.iter);
        edit.setFixedWidth( this.edit_width );
        toolTip = "Number of histogram transformations."
        onValueUpdated = function (value) { user_data.iter = value; };
    }

    // median
    this.median_NC = new NumericControl (this);
    with (this.median_NC) {
        label.text = "Target median:";
        label.minWidth = this.label_width + 6+1; // align with labels inside GroupBox below
        setRange (0.001, 0.2);
        slider.setRange (0, 1000);
        slider.minWidth = 400;
        setPrecision (3);
        setValue (user_data.target_median);
        edit.setFixedWidth( this.edit_width );
        toolTip = "Desired median of the resulting image.";
        onValueUpdated = function (value) { user_data.target_median = value; };
    }

    // mask shadows clipping
    this.shadows_clipping_NC = new NumericControl (this);
    with (this.shadows_clipping_NC) {
        label.text = "Shadows clipping:";
        label.minWidth = this.label_width;
        setRange (0, 0.4);
        slider.setRange (0, 401);
        //slider.minWidth = 400;
        setPrecision (3);
        setValue (user_data.shadows_clipping);
        edit.setFixedWidth( this.edit_width );
        toolTip = "Shadows clipping to perform on each luminance mask";
        onValueUpdated = function (value) { user_data.shadows_clipping = value; };
    }

    // mask blur method: label
    this.blur_method_LBL = new Label (this);
    with (this.blur_method_LBL) {
        text = "Blur method:";
        textAlignment = TextAlign_Right | TextAlign_VertCenter;
        minWidth = this.label_width;
    }

    // mask blur method: radio buttons
    this.blur_method_NONE_R = new RadioButton (this);
    with (this.blur_method_NONE_R) {
        text = "None";
        if (user_data.blur_method == BLUR_METHOD_NONE) { checked = true; }
        onCheck = function(checked) {
            if (checked) {
                user_data.blur_method = BLUR_METHOD_NONE;
            } else {
            }
        };
    }
    this.blur_method_ATW_R = new RadioButton (this);
    with (this.blur_method_ATW_R) {
        text = "À trous wavelets";
        if (user_data.blur_method == BLUR_METHOD_ATW) { checked = true; }
        onCheck = function(checked) {
            if (checked) {
                user_data.blur_method = BLUR_METHOD_ATW;
            } else {
            }
        };
    }
    this.blur_method_CONV_R = new RadioButton (this);
    with (this.blur_method_CONV_R) {
        text = "Convolution";
        if (user_data.blur_method == BLUR_METHOD_CONV) { checked = true; }
        onCheck = function(checked) {
            if (checked) {
                user_data.blur_method = BLUR_METHOD_CONV;
            } else {
            }
        };
    }

    // mask blur method: sizer
    this.blur_method_HS = new HorizontalSizer (this);
    with (this.blur_method_HS) {
        spacing = 16;
        add (this.blur_method_LBL);
        add (this.blur_method_NONE_R);
        add (this.blur_method_ATW_R);
        add (this.blur_method_CONV_R);
        addStretch();
    }

    // mask atw layers to remove
    this.atw_layers_NC = new NumericControl (this);
    with (this.atw_layers_NC) {
        label.text = "Wavelet layers to remove:";
        label.minWidth = this.label_width;
        setRange (1, 5);
        slider.setRange (0, 5);
        //slider.minWidth = 400;
        setPrecision (0);
        setValue (user_data.atw_layers);
        edit.setFixedWidth( this.edit_width );
        toolTip = "<p>&Agrave; trous wavelet layers to remove from masks</p>";
        onValueUpdated = function (value) { user_data.atw_layers = value; };
    }

    // mask atw scaling function: label
    this.atw_scaling_LBL = new Label (this);
    with (this.atw_scaling_LBL) {
        text = "Wavelet scaling function:";
        textAlignment = TextAlign_Right | TextAlign_VertCenter;
        minWidth = this.label_width;
        toolTip = "Scaling function used to calculate layers";
    }

    // mask atw scaling function: combobox
    this.atw_scaling_CB = new ComboBox (this);
    with (this.atw_scaling_CB) {
        for (var i = 0; i < engine.atw_scaling_functions.length; i++) {
            addItem (engine.atw_scaling_functions[i].name);
        }
        currentItem = user_data.atw_scaling;
        toolTip = "Scaling function used to calculate layers";
        onItemSelected = function (index) { user_data.atw_scaling = index; };
    }

    // mask atw scaling function: sizer
    this.atw_scaling_HS = new HorizontalSizer (this);
    with (this.atw_scaling_HS) {
        spacing = 6;
        add (this.atw_scaling_LBL);
        add (this.atw_scaling_CB, 100);
    }

    // mask convolution kernel size
    this.conv_pixels_NC = new NumericControl (this);
    with (this.conv_pixels_NC) {
        label.text = "Convolution kernel size:";
        label.minWidth = this.label_width;
        setRange (3, 31);
        slider.setRange (0, 14);
        setPrecision (0);
        setValue (user_data.conv_pixels);
        edit.setFixedWidth( this.edit_width );
        toolTip = "Kernel size for the convolution, in pixels";
        onValueUpdated = function (value) { user_data.conv_pixels = value; };
    }

    // mask sizer
    this.mask_VS = new VerticalSizer (this);
    with (this.mask_VS) {
        margin = 6;
        spacing = 6;
        add (this.shadows_clipping_NC);
        addSpacing (4);
        add (this.blur_method_HS);
        addSpacing (4);
        add (this.atw_layers_NC);
        add (this.atw_scaling_HS);
        addSpacing (4);
        add (this.conv_pixels_NC);
    }

    // mask groupbox
    this.mask_GB = new GroupBox (this);
    with (this.mask_GB) {
        title = "Mask";
        sizer = this.mask_VS;
    }

    // delete settings checkbox
    this.ds_CB = new CheckBox (this);
    with (this.ds_CB) {
        text = "Delete settings and exit";
        checked = user_data.delete_settings;
        toolTip = "Check to remove the stored settings and exit the program.";
        onCheck = function (checked) { user_data.delete_settings = checked; };
    }

    // buttons
    this.ok_Button = new PushButton (this);
    this.ok_Button.text = " OK ";
    this.ok_Button.onClick = function() { this.dialog.ok(); };

    this.cancel_Button = new PushButton (this);
    this.cancel_Button.text = " Cancel ";
    this.cancel_Button.onClick = function() { this.dialog.cancel(); };

    this.buttons_Sizer = new HorizontalSizer;
    this.buttons_Sizer.spacing = 4;
    this.buttons_Sizer.addStretch();
    this.buttons_Sizer.add (this.ok_Button);
    this.buttons_Sizer.add (this.cancel_Button);

    // pack everything
    this.sizer = new VerticalSizer;
    with (this.sizer) {
        margin = 6;
        spacing = 6;
        add (this.helpLabel);
        addSpacing (4);
        add (this.iter_NC);
        add (this.median_NC);
        add (this.mask_GB);
        addSpacing (4);
        add (this.ds_CB);
        addSpacing (4);
        add (this.buttons_Sizer);
    }

    this.windowTitle = "MaskedStretch v" + VERSION;
    this.adjustToContents();
    this.setFixedSize();
}
mst_dialog.prototype = new Dialog;

function mst_settings() {
    var conf = new Array (
        // important: use the same name as the user_data variables
        new Array ("iter",             DataType_UInt8),
        new Array ("target_median",    DataType_Float),
        new Array ("shadows_clipping", DataType_Float),
        new Array ("blur_method",      DataType_UInt8),
        new Array ("atw_layers",       DataType_UInt8),
        new Array ("atw_scaling",      DataType_UInt8),
        new Array ("conv_pixels",      DataType_UInt8)
    );

    this.load = function() {
        var temp;

        for (var i in conf) {
            var str  = conf[i][0];
            var type = conf[i][1];

            temp = Settings.read (SET_PREFIX + "/" + str, type);
            if (Settings.lastReadOK) {
                user_data[str] = temp;
            } else {
                console.writeln (format (
                    "Warning: couldn't read setting '%s/%s'", SET_PREFIX, str
                ));
            }
        }
    }

    this.save = function() {
        for (var i in conf) {
            var str  = conf[i][0];
            var type = conf[i][1];

            Settings.write (SET_PREFIX + "/" + str, type, user_data[str]);
        }
    }

    this.del = function() {
        Settings.remove (SET_PREFIX);
    }
}
var mst_set = new mst_settings;

function main() {
    console.hide();

    engine.set_win (ImageWindow.activeWindow);

    mst_set.load();
    var dialog = new mst_dialog;   // has to be done after settings are loaded
    if (!dialog.execute())
        return;
    if (user_data.delete_settings) {
        mst_set.del();
        console.show();
        return;
    } else {
        mst_set.save();
    }

    console.abortEnabled = true;

    var startts = new Date;
    if (engine.calc_mtf()) {
        console.writeln (format ("Using MTF of %6.04f.", engine.mtf));
        engine.work();
    } else {
        console.writeln ("Image not modified (but its RGBWS *was* modified).");
    }
    var endts = new Date;
    console.writeln (format ("<br><b>MaskedStretch</b>: %.2f s",
        (endts.getTime() - startts.getTime()) / 1000));

    console.show();
}

main();

Offline Jordi Gallego

  • PixInsight Addict
  • ***
  • Posts: 279
Re: Faster version of MaskedStretch script
« Reply #1 on: 2010 March 16 10:49:18 »

Hi Andrés,

I can confirm that your version is considerably faster (On my system, even more that what you have found). It also brings final median value closer to target value than version 0.8.
Thank you very much for the improvement!! ;)

Jordi



Jordi Gallego
www.astrophoto.es

Offline Silvercup

  • PixInsight Addict
  • ***
  • Posts: 187
Re: Faster version of MaskedStretch script
« Reply #2 on: 2010 March 16 15:33:16 »
Hi Andres:

Very fast, 25 iterations in 29 seconds.

Thanks. Silvercup

Offline avastro

  • PixInsight Addict
  • ***
  • Posts: 181
    • http://astrosurf.com/avastro/
Re: Faster version of MaskedStretch script
« Reply #3 on: 2010 March 18 02:42:36 »
Hi Andres
Thank you for improving the maskedstretch scrip, I use it regularly in my process and your version is definitively more speedy than version 0.8.
V0.9
130 iterations = 114.3 s
V0.8
130 iterations = 452.9 s
Not bad  :D

Antoine
Antoine
Lentin Observatory
http://www.astrosurf.com/avastro/

Offline Harry page

  • PTeam Member
  • PixInsight Jedi Knight
  • *****
  • Posts: 1458
    • http://www.harrysastroshed.com
Re: Faster version of MaskedStretch script
« Reply #4 on: 2010 March 18 12:13:17 »
Hi

Ok I am stupid , how do I load this script into PI   ???


Harry
Harry Page

Offline Andres.Pozo

  • PTeam Member
  • PixInsight Padawan
  • ****
  • Posts: 927
Re: Faster version of MaskedStretch script
« Reply #5 on: 2010 March 18 12:47:11 »
Hi

Ok I am stupid , how do I load this script into PI   ???


Harry
It is easy:
1 - Save the code to a .js file (i.e. MaskedStretch_0_9.js)
2 - Enter PixInsight
3 - Select the menu entry "Script | Feature Scripts..."
4 - Press the button "Add" and select the directory where you saved the script.

Now, the script can be used from the menu "Script | Utilities | MaskedStretch_0.9".

Offline Harry page

  • PTeam Member
  • PixInsight Jedi Knight
  • *****
  • Posts: 1458
    • http://www.harrysastroshed.com
Re: Faster version of MaskedStretch script
« Reply #6 on: 2010 March 18 13:01:47 »
Ok

Still dumb  :'(

I highlight the script and it will not save , can copy it , what do write or save this in  ???

Harry
Harry Page

Offline Silvercup

  • PixInsight Addict
  • ***
  • Posts: 187
Re: Faster version of MaskedStretch script
« Reply #7 on: 2010 March 18 14:20:09 »
Hi:

Copy it and paste in notepad, then save as script_name.js

Silvercup

Offline Harry page

  • PTeam Member
  • PixInsight Jedi Knight
  • *****
  • Posts: 1458
    • http://www.harrysastroshed.com
Re: Faster version of MaskedStretch script
« Reply #8 on: 2010 March 19 00:44:47 »
Hi

Thanks for un dumbing me  O:)

Harry
Harry Page

Offline Riccardo A. Ballerini

  • Newcomer
  • Posts: 37
Re: Faster version of MaskedStretch script
« Reply #9 on: 2013 September 30 13:38:08 »
Hi :-)
I stumbled over this old topic and gave this interesting script a try.

The console returns this error :-(


FastMaskedStretch.js, line 74: invalid macro identifier: BLUR_METHOD_ATWÂ

Is there an easy fix?

regards

Offline Andres.Pozo

  • PTeam Member
  • PixInsight Padawan
  • ****
  • Posts: 927
Re: Faster version of MaskedStretch script
« Reply #10 on: 2013 September 30 15:13:42 »
This a very old thread. You shouldn't use the script in it. Please use the script distributed with the latest version of PixInsight (MaskedStretch v0.9.1).

Since Juan plans to write a process for doing the same as this script I will not continue improving it:
http://pixinsight.com/forum/index.php?topic=4560.msg31733#msg31733

Offline Riccardo A. Ballerini

  • Newcomer
  • Posts: 37
Re: Faster version of MaskedStretch script
« Reply #11 on: 2013 October 01 03:43:00 »
Thank you for replying :-)

best