Author Topic: Script methods for get/set pixel values by coords?  (Read 446 times)

Offline TinySpeck

  • Member
  • *
  • Posts: 65
    • View Profile
Script methods for get/set pixel values by coords?
« on: 2019 February 14 11:39:52 »
I'm having a hard time finding useful information on the methods for the PCL classes.  I'm looking for methods which read and write the pixel data at a given (x, y) coordinate in an Image.  I've been scanning the available script source code for clues, and the closest I can find is Image.interpolate(), which seems way overkill.

Help...?
Gerrit

Offline oldwexi

  • PixInsight Guru
  • ****
  • Posts: 623
    • View Profile
    • Astronomy Pages G.W.
Re: Script methods for get/set pixel values by coords?
« Reply #1 on: 2019 February 14 13:10:35 »
Gerrit!
PixelMath offers with the Functions:
"pixel($T,x,y)"  and
"x()"
"y() "
what you are looking for.
(check in PixelMath these functions for explanations.)

So, you can either do what you are asking for with PixelMath only,  or
as i did,  you can imbedd in your script the same expression and call from there PixelMath.

If you load the script "Blend" and look at lines 75 to 77 there i wrote an example how to create a starmask at a selectable starborder at increasing decreasing brightness threshold levels).

Accessing x an y pixels without PixelMath - others should have the solution -  i use the simplicity of PixelMath
for these kind of needs.
Gerald

Offline TinySpeck

  • Member
  • *
  • Posts: 65
    • View Profile
Re: Script methods for get/set pixel values by coords?
« Reply #2 on: 2019 February 14 13:27:08 »
Thank you, Gerald.  I use the PixelMath expressions too, and they're so easy compared to trying to use a script.   :(

I would be fine with embedding PixelMath in a script and will be happy to steal your code for that.    ;D  I found your Blend script and will dig into it.

If anyone can help with the way to do this directly in a script (and ideally where to find this kind of information), I'd be very grateful.
Gerrit

Offline TinySpeck

  • Member
  • *
  • Posts: 65
    • View Profile
Re: Script methods for get/set pixel values by coords?
« Reply #3 on: 2019 February 15 08:53:49 »
Unfortunately PixelMath won't do what I'm trying.  It seems to create an image output buffer as it is processing, so if you change a pixel on one loop through the image the next loop won't see the change.  The change is written to the output buffer and the next loop reads the input image pixel.  It works this way whether you tell it to create a new output image or not.

So I need to get a script going where I can read/write to the same image.  But for the life of me I can't find how to read/write pixels at an arbitrary (x,y) coordinate using the PCL classes.  That Image.interpolate() method only works for one channel, and as I said it seems like way too much for simple data access. 

How do you read/write a complete RGB pixel at a given image coordinate?
Gerrit

Offline Juan Conejero

  • PTeam Member
  • PixInsight Jedi Grand Master
  • ********
  • Posts: 6941
    • View Profile
    • http://pixinsight.com/
Re: Script methods for get/set pixel values by coords?
« Reply #4 on: 2019 February 15 11:22:08 »
Hi Gerrit,

Here we go. I'm not really sure if you refer to PCL (C++) or to the JavaScript runtime. Anyway, I'll tell you how to do this in both programming environments.

In JavaScript, you have to use the Image object. Here is a brief working example:

Code: [Select]
function outputPixelValue( image, x, y, c )
{
   console.writeln( format( "<end><cbr>The pixel at x=%d y=%d c=%d has a value of %.7f",
                            x, y, c, image.sample( x, y, c ) ) );
}

// The curret image window.
var window = ImageWindow.activeWindow;
if ( window.isNull )
   throw new Error( "I need an image to play!" );

// The image in the window's current view.
var image = window.currentView.image;

// Open the Process Console window.
console.show();

// Read some pixels.
outputPixelValue( image, 123, 456, 0 );
outputPixelValue( image, 300, 400, 1 );
outputPixelValue( image, 400, 500, 2 );

Copy the above code and paste it on a new JavaScript source document in Script Editor. Save the .js file wherever you want, open an image, and run it (on Linux and Windows you can press F9, Ctrl+R on macOS). The example assumes that the current image is an RGB color image, since it reads the three first channels (the c argument passed to outputPixelValue()).

As you see, the Image.sample() method does exactly what you want. There are other (faster) ways to read pixel values, but this is the basic technique.

Now in C++ with the PCL library:

Code: [Select]
#include <pcl/ImageWindow.h>
#include <pcl/Console.h>

void OutputPixelValue( const ImageVariant& image, int x, int y, int c )
{
   Console().WriteLn( String().Format( "<end><cbr>The pixel at x=%d y=%d c=%d has a value of %.7f",
                              x, y, c, image( x, y, c ) ) );
}

void OutputSomePixels( const View& view )
{
   AutoViewLock lock( view );
   ImageVariant image = view.Image();
   OutputPixelValue( image, 123, 456, 0 );
   OutputPixelValue( image, 300, 400, 1 );
   OutputPixelValue( image, 400, 500, 2 );
}

// ... somewhere from a function...
ImageWindow window = ImageWindow::ActiveWindow();
if ( window.IsNull() )
   throw Error( "I need an image to play!" );

OutputSomePixels( window.CurrentView() );

Again, there are much faster (by orders of magnitude) ways to access pixel data in C++/PCL, but this would be a basic example. The OutputPixelValue() function makes use of the following operator member function of ImageVariant:

   double pcl::ImageVariant::operator() ( int x, int y, int channel = 0 ) const

which returns a pixel value in the normalized [0,1] range, just as Image.sample() in JavaScript. Both methods return a floating point pixel value irrespective of the actual pixel data type of the image; PCL and the JS runtime handle all of the involved complexities internally.

If you want to know how to modify pixels, not just how to read them, let me know. As I've said, these are absolutely basic examples, but also a good starting point to get the grasp of PixInsight development in JavaScript and C++. For PCL, the reference documentation is the main source of rigorous information:

https://pixinsight.com/developer/pcl/doc/html/

For JavaScript we still don't have a complete documentation like PCL's (I am working on it), but you can use the Object Explorer window to obtain complete information on the available objects, their properties and methods. Note also that core JavaScript objects are normally very similar to their C++ counterparts (as these examples clearly show). Finally, a large amount of PixInsight source code is open source, available at our official GitLab repositories (also still on GitHub for a limited time), including the complete PCL library, utility programs and many official modules.

Let me know if this is what you were after.
Juan Conejero
PixInsight Development Team
http://pixinsight.com/

Offline TinySpeck

  • Member
  • *
  • Posts: 65
    • View Profile
Re: Script methods for get/set pixel values by coords?
« Reply #5 on: 2019 February 15 11:53:34 »
Wonderful, Juan, thank you!  I AM confused about PCL vs JavaScript -- it's JavaScript I'm trying to write, so your first example will get me going.  Just a couple further questions:

>> I do need to modify pixels, so I will gratefully accept your offer of more details there.
>> Is there a method for getting the whole pixel RGB vector, or should I stick with three outputPixelValue() calls?
>> You mention
As you see, the Image.sample() method does exactly what you want. There are other (faster) ways to read pixel values, but this is the basic technique.
but I don't see Image.sample() in the JavaScript code.  I'm a total newbie at Java and the PixInsight classes, so I apologize if I'm missing something obvious.  Can you explain what Image.sample() has to do with this?

Again, many thanks!
Gerrit

Offline Juan Conejero

  • PTeam Member
  • PixInsight Jedi Grand Master
  • ********
  • Posts: 6941
    • View Profile
    • http://pixinsight.com/
Re: Script methods for get/set pixel values by coords?
« Reply #6 on: 2019 February 15 12:20:38 »
PCL stands for PixInsight Class Library. It is a collection of object definitions and programming tools designed specifically to write PixInsight modules in the C++ programming language, which is the language used to write almost all of PixInsight and its modules.

The PixInsight JavaScript Runtime, or PJSR for short, is an integrated JavaScript compiler and programming environment built in the PixInsight core application. This means that you can use it by just writing code and executing it; you don't need external tools as happens with C++/PCL.

Open the Object Explorer window and look at the tree on the left side. It exposes all of the predefined JavaScript objects that are available directly in PJSR. If you extend the right panel of Object Explorer and select one of these core objects (such as Image for example), you'll get a complete list of all properties and methods provided by the object in question.

Modifying pixels is a bit more complicated than just reading them. This is because when you are about to modify an image, PixInsight wants to know that you are going to do that before you actually do that. This is necessary to keep everything synchronized, to put it simplified. Look at this snippet:

Code: [Select]
// The curret image window.
var window = ImageWindow.activeWindow;
if ( window.isNull )
   throw new Error( "I need an image to play!" );

// The current view.
var view = window.currentView;

// Set one pixel orange.
view.beginProcess();
view.image.setSample( 1.0, 123, 456, 0 );
view.image.setSample( 0.5, 123, 456, 1 );
view.image.setSample( 0.0, 123, 456, 2 );
view.endProcess();

Again, this is a working example that you can copy and run in PixInsight. It will set the pixel at coordinates x=123 y=456 to orange color. The View.beginProcess() method tells PixInsight that you are going to modify the image in the view. As it is being called here, PixInsight will save a swap file to disk, so the action can be undo/redo normally. If you don't want to generate a swap file, you can change the script this way:

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

// The curret image window.
var window = ImageWindow.activeWindow;
if ( window.isNull )
   throw new Error( "I need an image to play!" );

// The current view.
var view = window.currentView;

// Set one pixel orange.
view.beginProcess(  UndoFlag_NoSwapFile );
view.image.setSample( 1.0, 123, 456, 0 );
view.image.setSample( 0.5, 123, 456, 1 );
view.image.setSample( 0.0, 123, 456, 2 );
view.endProcess();

But be careful because any changes you do to an image with UndoFlag_NoSwapFile cannot be undone.

As for speed, take it easy for now :) Better learn the basics well and make tests to get your feet wet. Unless you have to modify hundreds of thousands of pixels many times, Image.sample() and Image.setSample() are pretty fast.
Juan Conejero
PixInsight Development Team
http://pixinsight.com/

Offline TinySpeck

  • Member
  • *
  • Posts: 65
    • View Profile
Re: Script methods for get/set pixel values by coords?
« Reply #7 on: 2019 February 15 13:21:28 »
This is so good of you to explain all this, Juan, I really appreciate it.  In a forum like this, hopefully others will benefit too.

Ah -- I see where image.sample() is!  It's built into the console.writeln() call in your first example.  So it's sample() for reading and setSample() for writing, both methods of the Image class, and I need to do three of each for the R, G, B channels.

I agree about starting slowly!  The script I'm working only modifies a few dozen pixels in an image, so speed shouldn't be critical.
Gerrit