PixInsight Forum

Software Development => New Scripts and Modules => Topic started by: Juan Conejero on 2010 July 30 04:02:27

Title: New Script: Batch Automatic STF Stretch
Post by: Juan Conejero on 2010 July 30 04:02:27
Hi all,

In response to many user requests, here is a simple script that applies automatic STFs to all open images (main views only). Tired of clicking the "A" button? Just open your raw images and run the script. Automatic screen stretch made easy!

At the beginning of the script you can find three macro definitions: SHADOWS_CLIP, TARGET_BKG and RGB_LINK, whose meanings are explained in code comments. These define the script's operating parameters. Their values are the default values applied by the standard ScreenTransferFunction tool, so you normally won't need to change them.

Just a word of caution. If your machine is on the slow side and you have a lot of images open, please be patient as the script can't be interrupted. However this shouldn't cause any practical problems unless you open really many images, say 100 or so.

Enjoy!

EDIT (2013 August 12): Two years ago I discovered that the MTF function is nicely invertible. An MTF function with midtones balance parameter m is given by:

(http://pixinsight.com/doc/tools/HistogramTransformation/images/eqn_0002.svg)

The above equation can be derived from the Bulirsch-Stoer algorithm (see for example R. Bulirsch, J. Stoer et al., Introduction to Numerical Analysis, Third Edition, Springer, 2010, sect. 2.2) for evaluation of the diagonal rational function, given the three fixed data points of the MTF function.

The inverse problem to solve in this script is as follows: Given input and output background values b0 and b1, respectively, find the required midtones balance mb for the MTF function such that:

b1 = MTF( b0, mb )

It can be shown that the answer is:

mb = MTF( b1, b0 )

Before discovering this nice property, we were using a binary search algorithm to find the value of mb. I have updated the script below accordingly (sorry for not having done this before, but I overlooked this post). The original binary search routine is left commented for "historical reasons" :)

Code: [Select]
/*
 * AutoSTF
 *
 * A simple script to apply automatic screen transfer functions (STF) to all
 * open images.
 *
 * Copyright (C) 2010-2013, Pleiades Astrophoto S.L.
 * Written by Juan Conejero (PTeam)
 */

#feature-id    Utilities > AutoSTF

#feature-info  A script that applies automatic screen transfer functions (STF) \
   to all open images.<br/>\
   <br/>\
   Written by Juan Conejero (PTeam)<br/>\
   Copyright (C) 2010-2013 Pleiades Astrophoto

/*
 * Default STF Parameters
 */

// Shadows clipping point measured in sigma units from the main histogram peak.
#define SHADOWS_CLIP -1.25

// Target background in the [0,1] range.
#define TARGET_BKG    0.25

// Whether to apply the same STF to all channels, or treat each channel separately.
#define RGB_LINK      true

/*
 * Find a midtones balance value that transforms v1 into v0 through a midtones
 * transfer function (MTF), within the specified tolerance eps.
 *
 * ### This routine is no longer used - See the following forum thread for more
 * information:
 *    http://pixinsight.com/forum/index.php?topic=2116
 */
 /*
function findMidtonesBalance( v0, v1, eps )
{
   if ( v1 <= 0 )
      return 0;

   if ( v1 >= 1 )
      return 1;

   v0 = Math.range( v0, 0.0, 1.0 );

   if ( eps )
      eps = Math.max( 1.0e-15, eps );
   else
      eps = 5.0e-05;

   var m0, m1;
   if ( v1 < v0 )
   {
      m0 = 0;
      m1 = 0.5;
   }
   else
   {
      m0 = 0.5;
      m1 = 1;
   }

   for ( ;; )
   {
      var m = (m0 + m1)/2;
      var v = Math.mtf( m, v1 );

      if ( Math.abs( v - v0 ) < eps )
         return m;

      if ( v < v0 )
         m1 = m;
      else
         m0 = m;
   }
}
 */

/*
 * STF Auto Stretch routine
 */
function ApplyAutoSTF( view, shadowsClipping, targetBackground, rgbLinked )
{
   var stf = new ScreenTransferFunction;

   var n = view.image.isColor ? 3 : 1;

   if ( rgbLinked )
   {
      /*
       * Try to find how many channels look as channels of an inverted image.
       * We know a channel has been inverted because the main histogram peak is
       * located over the right-hand half of the histogram. Seems simplistic
       * but this is consistent with astronomical images.
       */
      var invertedChannels = 0;
      for ( var c = 0; c < n; ++c )
      {
         view.image.selectedChannel = c;
         if ( view.image.median() > 0.5 )
            ++invertedChannels;
      }
      view.image.resetSelections();

      if ( invertedChannels < n )
      {
         // Noninverted image

         var c0 = 0;
         var m = 0;
         for ( var c = 0; c < n; ++c )
         {
            view.image.selectedChannel = c;
            var median = view.image.median();
            var avgDev = view.image.avgDev();
            c0 += median + shadowsClipping*avgDev;
            m  += median;
         }
         view.image.resetSelections();
         c0 = Math.range( c0/n, 0.0, 1.0 );
         m = Math.mtf( targetBackground, m/n - c0 );

         stf.STF = [ // c0, c1, m, r0, r1
                     [c0, 1, m, 0, 1],
                     [c0, 1, m, 0, 1],
                     [c0, 1, m, 0, 1],
                     [0, 1, 0.5, 0, 1] ];
      }
      else
      {
         // Inverted image

         var c1 = 0;
         var m = 0;
         for ( var c = 0; c < n; ++c )
         {
            view.image.selectedChannel = c;
            var median = view.image.median();
            var avgDev = view.image.avgDev();
            m  += median;
            c1 += median - shadowsClipping*avgDev;
         }
         view.image.resetSelections();
         c1 = Math.range( c1/n, 0.0, 1.0 );
         m = 1 - Math.mtf( targetBackground, c1 - m/n );

         stf.STF = [ // c0, c1, m, r0, r1
                     [0, c1, m, 0, 1],
                     [0, c1, m, 0, 1],
                     [0, c1, m, 0, 1],
                     [0, 1, 0.5, 0, 1] ];
      }
   }
   else
   {
      var A = [ // c0, c1, m, r0, r1
               [0, 1, 0.5, 0, 1],
               [0, 1, 0.5, 0, 1],
               [0, 1, 0.5, 0, 1],
               [0, 1, 0.5, 0, 1] ];

      for ( var c = 0; c < n; ++c )
      {
         view.image.selectedChannel = c;
         var median = view.image.median();
         var avgDev = view.image.avgDev();

         if ( median < 0.5 )
         {
            // Noninverted channel
            var c0 = Math.range( median + shadowsClipping*avgDev, 0.0, 1.0 );
            var m  = Math.mtf( targetBackground, median - c0 );
            A[c] = [c0, 1, m, 0, 1];
         }
         else
         {
            // Inverted channel
            var c1 = Math.range( median - shadowsClipping*avgDev, 0.0, 1.0 );
            var m  = 1 - Math.mtf( targetBackground, c1 - median() );
            A[c] = [0, c1, m, 0, 1];
         }
      }

      stf.STF = A;

      view.image.resetSelections();
   }

   console.writeln( "<end><cbr/><br/><b>", view.fullId, "</b>:" );
   for ( var c = 0; c < n; ++c )
   {
      console.writeln( "channel #", c );
      console.writeln( format( "c0 = %.6f", stf.STF[c][0] ) );
      console.writeln( format( "m  = %.6f", stf.STF[c][2] ) );
      console.writeln( format( "c1 = %.6f", stf.STF[c][1] ) );
   }

   stf.executeOn( view );

   console.writeln( "<end><cbr/><br/>" );
}

function main()
{
   console.show();
   var images = ImageWindow.windows;
   for ( var i in images )
   {
      processEvents();
      ApplyAutoSTF( images[i].mainView, SHADOWS_CLIP, TARGET_BKG, RGB_LINK );
      processEvents();
   }
}

main();
Title: Re: New Script: Batch Automatic STF Stretch
Post by: Jack Harvey on 2010 July 30 06:34:05
Great  Thanks, this is handy
Title: Re: New Script: Batch Automatic STF Stretch
Post by: Nocturnal on 2010 August 01 08:48:54
Interesting, the script seems to repeat the A functionality rather than reuse it from the STF process. You can't instantiate an STF object and call the appropriate method to do an automatic stretch?
Title: Re: New Script: Batch Automatic STF Stretch
Post by: Jordi Gallego on 2010 August 02 11:43:17
Thanks Juan, it is useful ;)
Title: Re: New Script: Batch Automatic STF Stretch
Post by: FunTomas on 2013 August 07 13:54:21
I want to process a set of images (>300). I modified this script to apply HistogramTransformation (like Mike Schuster, but his script is no longer awailable for download).
When I try to run this script on opened image, everything is O.K.
When I run this modified script on ImageContainer, it fails. I made icon instance of script on desktop, and then drag and drop triangle icon from ImageContainer.

I prepared small script for testing what am I  doing wrong.

Code: [Select]
function main() {
   var av = ImageWindow.activeWindow;
   console.writeln("activeWindow ... ", av);
   if (av) {
      var img = av.mainView.image;
      console.writeln("isColor ... ", img.isColor);
      console.writeln("width x height ... ", img.width, " x ", img.height);
   }
}

Running this script on image container, img is undefined. Please help, if anybody know.
Title: Re: New Script: Batch Automatic STF Stretch
Post by: Juan Conejero on 2013 August 12 01:40:07
Hi Tomas,

Try this:

Code: [Select]
function main() {
   var av = ImageWindow.activeWindow;
   console.writeln("activeWindow ... ", av);
   if (!av.isNull) {
      var img = av.mainView.image;
      console.writeln("isColor ... ", img.isColor);
      console.writeln("width x height ... ", img.width, " x ", img.height);
   }
}

main();

You have to use the ImageWindow.isNull property to check if a window is valid.
Title: Re: New Script: Batch Automatic STF Stretch
Post by: Juan Conejero on 2013 August 12 04:41:21
By the way, I have updated the original AutoSTF script that I published in this thread. See the first thread post for more information.