Author Topic: Generic2DImage<P>::Apply problem with integer images  (Read 4985 times)

Offline NKV

  • PTeam Member
  • PixInsight Guru
  • ****
  • Posts: 677
Hi,
I try to convert BandingReduction script to module version and I have trouble with integer images.

There are simplest code:
Code: [Select]
img.Apply( fixFactor, ImageOp::Add );
img.Apply( fixFactor, ImageOp::Add ) don't work properly if img is integer image and fixFactor < 0.

To avoid the problem I use this code:
Code: [Select]
if ( fixFactor >=0 )
   img.Apply( fixFactor, ImageOp::Add );
else
   img.Apply( -fixFactor, ImageOp::Sub );

Which works fine. Look at PrintScreen with Original, Good and Bad results.

Mathematically ( img add fixFactor )  identical to ( img substract  -fixFactor )
So I not understand why I can't use negative fixFactor , especially if I sure that final result will positive? Is it bug or I doing something wrong?

Complete code:
Code: [Select]
class BandingReductionEngine
{
public:

   template <class P>
   static void Apply( Generic2DImage<P>& img, const BandingReductionInstance& instance )
   {
      ImageStatistics ss;
      ss.DisableVariance();
      ss.EnableRejection();

      Rect lineRect( img.Width(), 1 );
      for ( int c = 0; c < img.NumberOfNominalChannels(); c++  )
      {
         img.SelectChannel( c );
         ss.SetRejectionLimits( 0, 1 );
         ss << img;                           //   Generate MainImage Statistics
         double avgDev = ss.AvgDev();
         double median = ss.Median();

         double rejectionHigh = median + instance.sigmaFactor * 1.4826 * avgDev;
         if ( instance.highlightProtect )
            ss.SetRejectionLimits( 0, rejectionHigh );

         for ( int row = 0; row < img.Height(); row++ )
         {
            lineRect.MoveTo( 0, row );
            img.SelectRectangle( lineRect );
            ss << img;
            double fixFactor = median - ss.Median();
            //if ( fixFactor >=0 )
               img.Apply( fixFactor, ImageOp::Add );
            //else
            //   img.Apply( -fixFactor, ImageOp::Sub );
         }
         img.ResetSelection();
      }
      img.ResetSelections();
   }
};

Offline georg.viehoever

  • PTeam Member
  • PixInsight Jedi Master
  • ******
  • Posts: 2132
Re: Generic2DImage<P>::Apply problem with integer images
« Reply #1 on: 2012 May 13 13:34:13 »
Just a guess what happens:

- PI only considers double values between 0 and 1. This is equivalent to the full range of unsigned(!) integers (0...2^bits-1). PI does not have a concept of negative values, neither for double sample nor for int samples.
- Apply converts the double to an unsigned int in http://pixinsight.com/developer/pcl/doc/html/Image_8h_source.html#l01431 . It used toSample() to convert the double to unsigned (!) int.
- ToSample(double) is defined as http://pixinsight.com/developer/pcl/doc/html/PixelTraits_8h_source.html#l02723, which does not handle negative values properly (strictly speaking they are out of range...)

Georg
« Last Edit: 2012 May 13 14:53:25 by georg.viehoever »
Georg (6 inch Newton, unmodified Canon EOS40D+80D, unguided EQ5 mount)

Offline Juan Conejero

  • PTeam Member
  • PixInsight Jedi Grand Master
  • ********
  • Posts: 7111
    • http://pixinsight.com/
Re: Generic2DImage<P>::Apply problem with integer images
« Reply #2 on: 2012 May 13 15:40:07 »
Assume that img is an unsigned integer image; for example:

UInt16Image img;

When the Apply member function is called with a negative literal value, such as:

img.Apply( -1, ImageOp::Add );

PCL automatically converts -1 into UInt16Image::sample_type, which is uint16 (a PCL alias for unsigned short). So what you are adding in this case is uint16(-1) = 0xFFFF. This is a limitation of current PCL versions, which will be overcome in PCL 2.0.

To avoid this problem, specify the literal value as a floating point number. For example:

img.Apply( -1.0F, ImageOp::Add );

or more specifically in your code:

img.Apply( float( fixFactor ), ImageOp::Add );

Apply() with a floating point literal argument invokes a completely different routine, which will yield the correct values. Let me know if this helps.
 
Juan Conejero
PixInsight Development Team
http://pixinsight.com/

Offline Juan Conejero

  • PTeam Member
  • PixInsight Jedi Grand Master
  • ********
  • Posts: 7111
    • http://pixinsight.com/
Re: Generic2DImage<P>::Apply problem with integer images
« Reply #3 on: 2012 May 13 16:07:43 »
Georg,

Quote
- PI only considers double values between 0 and 1. This is equivalent to the full range of unsigned(!) integers (0...2^bits-1). PI does not have a concept of negative values, neither for double sample nor for int samples.

If this statement was true, the whole PixInsight platform would not work in any way! Actually, pixel samples of a floating point real or complex image can take the whole range of IEEE 754 values in PCL, either in 32-bit (float) or 64-bit (double) format, including non-numeric entities such as NaN and infinities. There are absolutely no restrictions in this regard.

However, when a module submits a floating point image to the platform, it is expected to be represented it in the normalized [0,1] range, where 0 is black and 1 is white. So the [0,1] range is only required for images that are to be managed directly by the graphical interface, but not for internal image data or working images. In fact, although it is considered nonstandard practice (and in most cases, bad practice), a module can submit an out-of-range floating point image to the GUI. In this case the image won't be rendered correctly on the screen, color management and masking won't work, and some modules can have problems to work with it, but otherwise it will be tolerated.

In the case of integer images, PixInsight supports unsigned integer pixel data exclusively, where 0 is black and 2n-1 is white, being n the number of bits used to store a pixel sample. This does not mean that an unsigned integer image cannot be used to perform any kind of image processing algorithms, such as convolutions, Fourier transforms, wavelet transforms, etc, where negative values happen naturally. Unsigned integer data are automatically converted to/from floating point working values as necessary, in a completely transparent way.

Finally, PCL supports signed integer images, although they have not been implemented as standard image types. For example, you can instantiate the GenericPixelTraits template as follows:

class PCL_CLASS Int16PixelTraits : public GenericPixelTraits<int16>
{
   // ...
};


which is straightforward, and then this should work without problems:

typedef Generic2DImage<Int16PixelTraits>    Int16Image;

However, no standard module would be able to work with instances of this class, and no View object could transport them either.
Juan Conejero
PixInsight Development Team
http://pixinsight.com/

Offline georg.viehoever

  • PTeam Member
  • PixInsight Jedi Master
  • ******
  • Posts: 2132
Re: Generic2DImage<P>::Apply problem with integer images
« Reply #4 on: 2012 May 13 23:13:12 »
...
img.Apply( -1.0F, ImageOp::Add );
...
FixFactor in the NKV's code is a double, so according to your statement this should work properly?!?
Georg
Georg (6 inch Newton, unmodified Canon EOS40D+80D, unguided EQ5 mount)

ruediger

  • Guest
Re: Generic2DImage<P>::Apply problem with integer images
« Reply #5 on: 2012 May 13 23:35:02 »
[...]a module can submit an out-of-range floating point image to the GUI. In this case the image won't be rendered correctly on the screen, color management and masking won't work, and some modules can have problems to work with it, but otherwise it will be tolerated.[...]
Hi Juan,
can you please explain the reason, why the following does not work:
- subtract two images a,b with PixelMath "a - b" and store the result in a new image named "diff". The result will have some negative values. The "diff" image is not used for rendering on screen, just for storing temporary values.
- add "b" to "diff" (PixelMath "$T + b"). You will not get "a" again.

I would like to see this kind of operation fully work, because it makes some programming tasks much easier.

Rüdiger

Offline Juan Conejero

  • PTeam Member
  • PixInsight Jedi Grand Master
  • ********
  • Posts: 7111
    • http://pixinsight.com/
Re: Generic2DImage<P>::Apply problem with integer images
« Reply #6 on: 2012 May 14 00:26:47 »
...
img.Apply( -1.0F, ImageOp::Add );
...
FixFactor in the NKV's code is a double, so according to your statement this should work properly?!?
Georg

Oops! :-[ I was writing faster than thinking (not surprising at 1 am) :) I forgot that I am working with a different version of PCL, where these problems are already fixed. Please do the following:

- In the pcl/PixelTraits.h PCL header, go to line 3383. The exact point doesn't matter, but it should be below the declaration of "static void Add( sample& a, T b )" and within the UInt32PixelTraits class. Insert the following static member functions:

Code: [Select]
   static void Add( sample& a, float b )
   {
      double fa; FromSample( fa, a );
      a = ToSample( fa + b );
   }

   static void Add( sample& a, double b )
   {
      double fa; FromSample( fa, a );
      a = ToSample( fa + b );
   }

   static void Sub( sample& a, float b )
   {
      double fa; FromSample( fa, a );
      a = ToSample( fa - b );
   }

   static void Sub( sample& a, double b )
   {
      double fa; FromSample( fa, a );
      a = ToSample( fa - b );
   }

Do the same for Uint16PixelTraits (line 2868) and UInt8PixelTraits (line 2337). There is no need to recompile the PCL library after these changes, since they are inline functions.

For integer images, if the Add or Sub operations you are performing with the Apply member function of Generic2DImage lead to negative values, the above operations will underflow with the default PCL configuration. You can prevent this to happen by declaring this macro:

__PCL_ENFORCE_PIXTRAITS_FLOAT_RANGE

in your code, *before* including any PCL standard header file. In this way out-of-range results will be detected and truncated, with a relatively small cost in execution speed.

These patches should fix the problem. Let me know otherwise.

I'll try to release a new 1.x PCL version as soon as possible.
Juan Conejero
PixInsight Development Team
http://pixinsight.com/

Offline Juan Conejero

  • PTeam Member
  • PixInsight Jedi Grand Master
  • ********
  • Posts: 7111
    • http://pixinsight.com/
Re: Generic2DImage<P>::Apply problem with integer images
« Reply #7 on: 2012 May 14 00:41:00 »
Hi Rüdiger,

Quote
I would like to see this kind of operation fully work, because it makes some programming tasks much easier.

When you execute PixelMath from a script, you can prevent it from rescaling or truncating the resulting image by setting to false its rescale and truncate properties, respectively. This code implements your example:

Code: [Select]
var diff = View.viewById( "diff" );

var P = new PixelMath;
P.expression = "a - b";
P.useSingleExpression = true;
P.rescale = false;
P.truncate = false;
P.executeOn( diff );

P.expression = "$T + b";
P.executeOn( diff );

Assuming that "diff" is a floating point image, this code should work just as you expect.

However, you must make sure that the image in the "diff" view is strictly in the [0,1] range before your script yields control to the core application's message loop, that is, before your script allows any GUI interaction.
Juan Conejero
PixInsight Development Team
http://pixinsight.com/

ruediger

  • Guest
Re: Generic2DImage<P>::Apply problem with integer images
« Reply #8 on: 2012 May 14 01:43:12 »
Assuming that "diff" is a floating point image, this code should work just as you expect.
This code works fine, thanks for fast reply.
However, it does not work when you use PixelMath in the GUI to temporarily store images with possibly negative values. The "truncate" parameter is set to "true" and not accesible in the PixelMath dialogue. Or have I overseen something?

But I start to understand why some of my code does not work. I think I copied code fragments from the history explorer and did not look closely at all parameters.

Rüdiger

Offline NKV

  • PTeam Member
  • PixInsight Guru
  • ****
  • Posts: 677
Re: Generic2DImage<P>::Apply problem with integer images
« Reply #9 on: 2012 May 14 07:07:10 »
Insert the following static member functions:
Thank you. It's works perfectly.