Author Topic: script to average two columns  (Read 10602 times)

Offline Nocturnal

  • PixInsight Jedi Council Member
  • *******
  • Posts: 2727
    • http://www.carpephoton.com
script to average two columns
« on: 2008 July 17 20:15:19 »
Hi,

I have a slight problem with my images. One column isn't quite right and somehow calibration doesn't take care of it. The result is a vague colored line in my image. I thought I'd try to write a simple jscript to do this. Something like

function FixColumn(image, col) {
for (y = 0; y < image.height; y++) {
  image.pixel(y, col) = (image.pixel(y, col-2) + image.pixel(y, col+2))/2
}
}

Then call this routine ala the example in one of the tutorials. So my code is simplistic and of course does not work. The Image class doesn't have a pixel property. The example uses the Rect and Point classes but that seems a bit convoluted for such a trivial operation.

Can you dconvolve me please? :)

I might just write this in C++ with fitsio but it seemed like a good little example to get started with PI scripting.

Thanks,

   Sander

PS you call understood of course that I'm fixing a bayer matrix image, hence the -2 and +2 to get pixels of the same color.
Best,

    Sander
---
Edge HD 1100
QHY-8 for imaging, IMG0H mono for guiding, video cameras for occulations
ASI224, QHY5L-IIc
HyperStar3
WO-M110ED+FR-III/TRF-2008
Takahashi EM-400
PIxInsight, DeepSkyStacker, PHD, Nebulosity

Offline Juan Conejero

  • PTeam Member
  • PixInsight Jedi Grand Master
  • ********
  • Posts: 7111
    • http://pixinsight.com/
script to average two columns
« Reply #1 on: 2008 July 18 03:58:05 »
Hi Sander,

This is an ideal problem to be fixed with a script.

The following methods of Image are your friends:

Code: [Select]
Number|Complex Image.sample( Point p[, int c] )
Number|Complex Image.sample( int x, int y[, int c] )


The sample function returns the pixel value at the specified image coordinates. Coordinates can be specified as a Point object or as separate x, y coordinates. The optional c argument selects a particular channel; if it isn't specified, the current channel will be used (which can be set by assigning an integer to the Image.selectedChannel property). If the image is in the grayscale color space, you don't have to care (unless it has alpha channels and you want to work with them).

Coordinates can be out of range, which is legal for reading pixel values (not for storing them). In this case, zero is returned. The channel index c, if specified, must correspond to an existing channel in the image (zero-based).

Number|Complex means that this function can return either a real value or a complex value, depending on the sample data type of the image. You are working with a real-valued image so you'll always receive real numbers.

Take into account that PixInsight works transparently in the normalized real range. This means that you'll always receive values in the [0,1] range, despite the actual data type of the image (integer or floating point).

However, if the image is in floating point, you can receive real values outside the [0,1] range, but this can only happen if your script generates unnormalized values. In other words, PI's JavaScript runtime will honor whatever you store in a floating point image (not in an integer image for obvious reasons).

Code: [Select]
void Image.setSample( Number|Complex v, Point p[, int c] )
void Image.setSample( Number|Complex v, int x, int y[, int c] )


The setSample function sets the specified pixel sample value at given x, y coordinates and optional channel index. As before, the position can be a Point or separate coordinates.

Most of the information given for Image.sample applies. The coordinates must be valid, or a runtime exception will be thrown (you can catch it with a try ... catch construct, if necessary).

With these methods in mind, here is your routine:

Code: [Select]
function FixColumn( image, col )
{
   for ( var y = 0; y < image.height; ++y )
      image.setSample( (image.sample( col-2, y ) + image.sample( col+2, y ))/2, col, y );
}


The var keyword to declare y in the for loop is required. Actually, you can omit it, but then JavaScript will create a global variable and the for loop can be one order of magnitude slower. With var, the scope of y is restricted to the for body and the engine optimizes its usage.

I look forward to see your results. I am *very* interested in promoting PixInsight scripting, and this can be a nice example.

Of course, you can do this also as a module written in C++. JavaScript is indeed slow, but its big advantage is that it's very easy to use: you need no compiler and no additional development tools. And PI's JS engine gives you a lot of powerful predefined routines that run at machine speed. Module development only makes sense for tasks much more complex than these fixes.
Juan Conejero
PixInsight Development Team
http://pixinsight.com/

Offline Nocturnal

  • PixInsight Jedi Council Member
  • *******
  • Posts: 2727
    • http://www.carpephoton.com
script to average two columns
« Reply #2 on: 2008 July 18 07:02:13 »
Hi Juan,

that worked great! The problem is that I am lazy and I don't feel like doing work the computer should do for me :) So when I have 20 lights I want my script to process them automatically. Now I recall the script you wrote to do batch conversion but man that's a arm's length of code just to display a dialog. What I'm thinking of is simpler. Load all the images you want to convert (and only those), then run the script which will modify them all. I suppose it could save the results to a new file or just leave them there for the user to decide. This is what I wrote:

Code: [Select]
/**
 * A script to fix a bad pixel column in the active window
 * Based on the script from the image processing tutorial
 * "NGC 5189 from GeminiObservatory South:
 * Deconvolution and HDRWaveletTransform in PixInsight", by J.Conejero (PTeam).
 */

#include <pjsr/ImageOp.jsh>

function FixColumn( image, col )
{
   for ( var y = 0; y < image.height; ++y )
      image.setSample( (image.sample( col-2, y ) + image.sample( col+2, y ))/2, col, y );
}


function main()
{
   // Get access to the currently active image window.
   //var window = ImageWindow.activeWindow;
   var windows = ImageWindow.windows;

   // Do we have at least one?
   if ( windows.isNull )
      throw Error( "There are no active image windows!" );

   for (var window in windows) {
  with ( window.mainView )
  {
     // Inform the core PixInsightapplication that we are about to modify the
     // image in this view. Without this,we have read-only access to the image.
     beginProcess();

     FixColumn(image, 2359);
     // Done modifying the image.
     endProcess();
  }
   }
}

main();


This doesn't work:

Processing script file: S:/pixinsight/FixColumn.js
*** Error [039]: S:/pixinsight/FixColumn.js, line 28: TypeError: window.mainView has no properties

Apparently I can't iterate over the windows array returned from ImageWindow.windows. I tried added the .isWindow test but that skips all windows.

I know I (and other users) sound like a broken record but don't you think it's time to stop coding and start writing documentation? I mean I'd love to experiment more with this but the way these classes work is let's say esoteric enough that it's not obvious how things work :) There are examples and I looked at those but they're too big to be of much help when you get started. A simple 1 or 2 sentence description of each class/property with links to relevant types would allow us to explore this without having to ask even the most basic question. I realize you're happy to help us here but I'd be much more productive if I could at least attempt to help myself and then come to you when I really can't figure it out.
Best,

    Sander
---
Edge HD 1100
QHY-8 for imaging, IMG0H mono for guiding, video cameras for occulations
ASI224, QHY5L-IIc
HyperStar3
WO-M110ED+FR-III/TRF-2008
Takahashi EM-400
PIxInsight, DeepSkyStacker, PHD, Nebulosity

Offline Nocturnal

  • PixInsight Jedi Council Member
  • *******
  • Posts: 2727
    • http://www.carpephoton.com
script to average two columns
« Reply #3 on: 2008 July 18 07:36:42 »
Hi,

Unfortunately DSS can't stack my images anymore after I run the script and save the file. It appears that PI changes the FITS format which makes it incompatible with the calibration frames. My process:

- load frame
- F9
- save

This should leave the FITS header as it was, correct? Looking at the files it seems you don't keep the BZERO and BSCALE fields. DSS displays the files all black.
Best,

    Sander
---
Edge HD 1100
QHY-8 for imaging, IMG0H mono for guiding, video cameras for occulations
ASI224, QHY5L-IIc
HyperStar3
WO-M110ED+FR-III/TRF-2008
Takahashi EM-400
PIxInsight, DeepSkyStacker, PHD, Nebulosity

Offline Nocturnal

  • PixInsight Jedi Council Member
  • *******
  • Posts: 2727
    • http://www.carpephoton.com
script to average two columns
« Reply #4 on: 2008 July 18 07:53:00 »
I've uploaded a .7z file with an original and 'saved' FITS file after running the script.

http://cid-a93625fef5ca95fb.skydrive.live.com/self.aspx/PixInsight/PIvsDSS.7z
Best,

    Sander
---
Edge HD 1100
QHY-8 for imaging, IMG0H mono for guiding, video cameras for occulations
ASI224, QHY5L-IIc
HyperStar3
WO-M110ED+FR-III/TRF-2008
Takahashi EM-400
PIxInsight, DeepSkyStacker, PHD, Nebulosity

Offline Juan Conejero

  • PTeam Member
  • PixInsight Jedi Grand Master
  • ********
  • Posts: 7111
    • http://pixinsight.com/
script to average two columns
« Reply #5 on: 2008 July 18 08:03:23 »
Hi Sander,

Of course, when one just wants to solve a particular problem, not willing to create a tool to be used by others, creating a graphical interface is an unnecessary pain. This is why there is a Console object in PI's runtime :)

Try this one:

Code: [Select]
#include <pjsr/ImageOp.jsh>

function FixColumn( image, col )
{
   for ( var y = 0; y < image.height; ++y )
      image.setSample( (image.sample( col-2, y ) + image.sample( col+2, y ))/2, col, y );
}

function main()
{
   // Get access to the currently active image window.
   //var window = ImageWindow.activeWindow;

   // Do we have at least one image window?
   if ( ImageWindow.windows.length == 0 )
      throw Error( "There are no image windows!" );

   for ( var i = 0; i < ImageWindow.windows.length; ++i ) // for each image window
      with ( ImageWindow.windows[i].mainView )
      {
         // Inform the core PixInsightapplication that we are about to modify the
         // image in this view. Without this,we have read-only access to the image.
         beginProcess();

         FixColumn( image, 2359 );

         // Done modifying the image.
         endProcess();
      }
}

main();


As you can see in this example, for ( var x in ImageWindow.windows ) won't work. This is a limitation related to the way actual objects living in PI's graphical interface are being made visible to the JS runtime. Basically, for the "in" clause to work, the iterated objects (x in this example) should be native JS objects, which is not the case. ImageWindow.windows is an array of ImageWindow objects, which are just managed aliases of actual image windows. In other words, to instantiate an ImageWindow variable in a meaningful way, the new operator must be used; for example:

Code: [Select]
var window = new ImageWindow;

This happens also with the rest of managed objects that work as virtual aliases of actual objects in the PI platform. As you can see, this is not actually a problem (other than the fact that "for ( var x in ..." is more elegant than "for ( var i = 0; ...") since an array can be iterated with the subscript operator.

Quote
don't you think it's time to stop coding and start writing documentation?


Of course, I agree completely. But the problem is that I am completely alone to carry out the following tasks:

- Development. Keep in mind that we have now 4 different versions (32/64 bits and Linux/Windows). Of course the whole code is 99.9% portable, but each platform has its own idiosyncrasies.

- Authoring documentation, which is not the nicest task I can think of, indeed. This is my worst nightmare and I am conscious that I am not being able to do even a minimally decent work.

- Giving support to the users, mainly by email.

- Taking care of the users on this forum, which is a pleasure.

- Maintenance of the web site. Needless to say that it is way outdated (look at the screenshots and you'll know what I mean).

- Maintenance of the e-commerce system and the licensing system (100% my own php code).

Add to this all of the bureaucratic tasks related to our company (a company is much harder to maintain here in Europe than in the U.S.), although I must say that my wife helps me a lot with this stuff.

I simply cannot do more things. I use to work 12 or more hours a day (the last week have been 14 to 16). It seems that I have bitten more than I can chew, as a user told to me.

All I can say is "I'll try to do that as soon as possible". The problem is that I've said that before, probably more than 100 times, so I'll better shut up :lol:
Juan Conejero
PixInsight Development Team
http://pixinsight.com/

Offline Juan Conejero

  • PTeam Member
  • PixInsight Jedi Grand Master
  • ********
  • Posts: 7111
    • http://pixinsight.com/
script to average two columns
« Reply #6 on: 2008 July 18 08:16:23 »
Hmmm, this sounds very strange. Are you working with floating point images? In that case, the BZERO and BSCALE keywords must not be used at all (as per the standard). This is why PixInsight is not writing these keywords to your FITS files if they store floating point images.

I didn't know that DSS needs these two keywords to interpret floating point FITS images. It shouldn't. This is workaround-able by saving your images in the TIFF format, though.

I'm going to take a look to your files now.
Juan Conejero
PixInsight Development Team
http://pixinsight.com/

Offline Juan Conejero

  • PTeam Member
  • PixInsight Jedi Grand Master
  • ********
  • Posts: 7111
    • http://pixinsight.com/
script to average two columns
« Reply #7 on: 2008 July 18 08:29:34 »
Sander, you can save the fixed files automatically. I have modified the script to do so in TIFF format:

Code: [Select]
#include <pjsr/ImageOp.jsh>

function FixColumn( image, col )
{
   for ( var y = 0; y < image.height; ++y )
      image.setSample( (image.sample( col-2, y ) + image.sample( col+2, y ))/2, col, y );
}

function main()
{
   // Get access to the currently active image window.
   //var window = ImageWindow.activeWindow;

   // Do we have at least one image window?
   if ( ImageWindow.windows.length == 0 )
      throw Error( "There are no image windows!" );

   for ( var i = 0; i < ImageWindow.windows.length; ++i )
      with ( ImageWindow.windows[i] )
      {
         with ( mainView )
         {
            // Inform the core PixInsightapplication that we are about to modify the
            // image in this view. Without this,we have read-only access to the image.
            beginProcess();

            FixColumn( image, 100 );

            // Done modifying the image.
            endProcess();
         }

         // Append "_colFix" to the file name and set the ".tif" extension
         var basePath = File.changeExtension( File.appendToName( filePath, "_colFix" ), ".tif" );
         var path = basePath;

         // Make sure that we are not going to overwrite an existing file
         for ( var n = 1; File.exists( path ); ++n )
            path = File.appendToName( basePath, n.toString() );

         // Save the file. Disable all dialogs and security checks.
         saveAs( path, false, false, false, false );
      }
}

main();


Hope this helps
Juan Conejero
PixInsight Development Team
http://pixinsight.com/

Offline Juan Conejero

  • PTeam Member
  • PixInsight Jedi Grand Master
  • ********
  • Posts: 7111
    • http://pixinsight.com/
script to average two columns
« Reply #8 on: 2008 July 18 09:39:32 »
Hi Sander

I think I know where the problem is: it seems that DSS cannot read *signed* integer FITS images. Your original image is in unsigned 16-bit format. So it needs BZERO=32768 and BSCALE=1 in order to translate unsigned integers to signed integers. This is a standard FITS practice, since as you know FITS cannot store unsigned integer values. The BZERO/BSCALE trick does the following:

actual_pixel_value = (value_stored_in_the_file + BZERO)/BSCALE

So if BZERO=32768 and BSCALE=1, we can store unsigned integers in the range 0 to 65535 as signed values in the range -32768 to +32767.

When saving your fixed files, just select the "16-bit unsigned integer" option on the FITS Options dialog, and all will be fine.

What happens is that the FITS module just tries to save signed integer files by default, since this is the native FITS format. This is the default option because it doesn't need the BZERO/BSCALE trick, which is quite ugly in my opinion. For some reason the module isn't honoring the original signed/unsigned format, as it should. I'll investigate that. You're the resident bug-catcher! :lol:
Juan Conejero
PixInsight Development Team
http://pixinsight.com/

Offline Nocturnal

  • PixInsight Jedi Council Member
  • *******
  • Posts: 2727
    • http://www.carpephoton.com
script to average two columns
« Reply #9 on: 2008 July 18 09:59:28 »
Thanks for all the replies Juan.

I certainly understand your position regarding all the tasks you have ahead of you and I don't envy you at all. I noticed you used Doxygen to create your PCL documentation. It seems this would be the quickest way to get at least rudimentary documentation for the jscript interface prepared. Knowing you there are probably lots of comments in your code already so maybe you're half way there?

As an aside I only noticed yesterday that each process has a nice description when accessed from the process explorer with the extension displayed. Now I've been using PI for a while and so you have to wonder why I never noticed this in the past :) Wouldn't it make sense to add a help button on each process window that shows this help message? I mean pressing F1 with a process container brings up PI online docs. How about showing that text instead?
Best,

    Sander
---
Edge HD 1100
QHY-8 for imaging, IMG0H mono for guiding, video cameras for occulations
ASI224, QHY5L-IIc
HyperStar3
WO-M110ED+FR-III/TRF-2008
Takahashi EM-400
PIxInsight, DeepSkyStacker, PHD, Nebulosity

Offline Nocturnal

  • PixInsight Jedi Council Member
  • *******
  • Posts: 2727
    • http://www.carpephoton.com
script to average two columns
« Reply #10 on: 2008 July 18 10:23:04 »
Hi Juan,

the script ran fine but now the problem is DSS can't deal with monochrome TIFF files as lights. Well, it could if the it was a mono camera but it's OSC so DSS needs to debayer. I have alerted Luc to this thread so let's wait for him to comment.
Best,

    Sander
---
Edge HD 1100
QHY-8 for imaging, IMG0H mono for guiding, video cameras for occulations
ASI224, QHY5L-IIc
HyperStar3
WO-M110ED+FR-III/TRF-2008
Takahashi EM-400
PIxInsight, DeepSkyStacker, PHD, Nebulosity