Author Topic: New Script: Deconvolution Preview  (Read 9388 times)

Offline Silvercup

  • PixInsight Addict
  • ***
  • Posts: 187
New Script: Deconvolution Preview
« on: 2009 June 15 14:41:22 »
Hola a todos:

He realizado mi primer script para Pixinsight: DeconvolutionPreview. El script nos permite obtener varias vistas previas de una imagen deconvolucionada con los factores Sigma (standart deviation) y Shape seleccionados.

El Script esta pensado para aquellos casos en que no tengamos datos para obtener la PSF. Estamos ante el caso de la Blind Deconvolution Procedure que funciona intentando adivinar la PSF de la imágen a procesar estimando la mejora obtenida en cada intento. Los valores Sigma y Shape se obtinen a base de prueba y error. Este Script nos ayuda a elegir los valores correctos fusionando en una imágen los "previews" seleccionados insertando en cada uno de ellos los valores Sigma y Shape en la esquina inferior derecha para tener una facil referencia.

Os adjunto una imagen con el resultado del script:



Code: [Select]
#include <pjsr/Sizer.jsh>
#include <pjsr/FrameStyle.jsh>
#include <pjsr/TextAlign.jsh>
#include <pjsr/StdButton.jsh>
#include <pjsr/StdIcon.jsh>
#include <pjsr/UndoFlag.jsh>
#include <pjsr/NumericControl.jsh>
#include <pjsr/SampleType.jsh>

#define VERSION "1.0"
#define TITLE "Deconvolution Preview Script"
function DeconvultionPreviewData()
{
    this.targetView = ImageWindow.activeWindow.currentView;
    this.GaussianStart= 1.50;
    this.GaussianAmount = 0.50;
    this.GaussianIterations = 3;
    this.ShapeStart= 2.00;
    this.ShapeAmount = 0.50;
    this.ShapeIterations = 3;

}

var data = new DeconvultionPreviewData;
function DeconvultionPreviewDialog()
{
   this.__base__ = Dialog;
   this.__base__();

   var emWidth = this.font.width( 'M' );
   var labelWidth1 = this.font.width( "PSF Gaussian Start:" + 'T' );

   //

   this.helpLabel = new Label( this );
   this.helpLabel.frameStyle = FrameStyle_Box;
   this.helpLabel.margin = 4;
   this.helpLabel.wordWrapping = true;
   this.helpLabel.useRichText = true;
   this.helpLabel.text =
      "<p><b>Deconvolution Preview v" + VERSION + "</b> - A script for "
      + "Blind Deconvolution procedure.</p>"
      + "<p>The script provides several previews applying deconvolutions with "
      + "selected Sigma and Shape factors to the original image.</p>"
      + "<p>The Sigma and Shape factors are drawn in each deconvoluted preview for easy reference."
      + "<p>Copyright &copy; 2009 Juan M. Gómez (Pixinsight User)</p>";

   this.targetImage_Label = new Label( this );
   this.targetImage_Label.minWidth = labelWidth1 + 6+1; // align with labels inside group boxes below
   this.targetImage_Label.text = "Target image:";
   this.targetImage_Label.textAlignment = TextAlign_Right|TextAlign_VertCenter;

   this.targetImage_ViewList = new ViewList( this );
   this.targetImage_ViewList.minWidth = 200;
   this.targetImage_ViewList.getAll(); // include main views as well as previews
   this.targetImage_ViewList.currentView = data.targetView;
   this.targetImage_ViewList.toolTip = "Select the image to perform the Deconvoluted previews.";
   this.targetImage_ViewList.onViewSelected = function( view )
   {
      data.targetView = view;
   };

   this.targetImage_Sizer = new HorizontalSizer;
   this.targetImage_Sizer.spacing = 4;
   this.targetImage_Sizer.add( this.targetImage_Label );
   this.targetImage_Sizer.add( this.targetImage_ViewList, 100 );


   // PSF Gaussian parameters
   this.GaussianStart_NC = new NumericControl (this);
   with ( this.GaussianStart_NC ) {
      label.text = "PSF Gaussian Start:";
      label.minWidth = labelWidth1;
      setRange (0.10, 9.99);
      slider.setRange (0, 1000);
      slider.minWidth = 250;
      setPrecision (2);
      setValue (data.GaussianStart);
      toolTip = "<p>PSF Sigma Start value.</p>";
      onValueUpdated = function (value) { data.GaussianStart = value; };
   }

   this.GaussianAmount_NC = new NumericControl (this);
   with ( this.GaussianAmount_NC ) {
      label.text = "Amount:";
      label.minWidth = labelWidth1;
      setRange (0.25, 6.00);
      slider.setRange (0, 1000);
      slider.minWidth = 250;
      setPrecision (2);
      setValue (data.GaussianAmount);
      toolTip = "<p>PSF Sigma amount for each iteration.</p>";
      onValueUpdated = function (value) { data.GaussianAmount = value; };
   }



   this.Gaussianiter_Label = new Label( this );
   this.Gaussianiter_Label.minWidth = labelWidth1;
   this.Gaussianiter_Label.text = "Previews:";
   this.Gaussianiter_Label.textAlignment = TextAlign_Right|TextAlign_VertCenter;

   this.Gaussianiter_SpinBox = new SpinBox( this );
   this.Gaussianiter_SpinBox.minValue = 1;
   this.Gaussianiter_SpinBox.maxValue = 10;
   this.Gaussianiter_SpinBox.value = data.GaussianIterations;
   this.Gaussianiter_SpinBox.toolTip = "<p>Number of Sigma iterations.</p>";
   this.Gaussianiter_SpinBox.onValueUpdated = function( value )
   {
      data.GaussianIterations = value;
   };

   this.Gaussianiter_Sizer = new HorizontalSizer;
   this.Gaussianiter_Sizer.spacing = 4;
   this.Gaussianiter_Sizer.add( this.Gaussianiter_Label );
   this.Gaussianiter_Sizer.add( this.Gaussianiter_SpinBox );
   this.Gaussianiter_Sizer.addStretch();

   this.GaussianParGroupBox = new GroupBox( this );
   this.GaussianParGroupBox.title = "PSF Gaussian Parameters";
   this.GaussianParGroupBox.sizer = new VerticalSizer;
   this.GaussianParGroupBox.sizer.margin = 6;
   this.GaussianParGroupBox.sizer.spacing = 4;
   this.GaussianParGroupBox.sizer.add( this.GaussianAmount_NC );
   this.GaussianParGroupBox.sizer.add( this.GaussianStart_NC );

   this.GaussianParGroupBox.sizer.add( this.Gaussianiter_Sizer );

  // PSF Shape parameters
   this.ShapeStart_NC = new NumericControl (this);
   with ( this.ShapeStart_NC ) {
      label.text = "PSF Shape Start:";
      label.minWidth = labelWidth1;
      setRange (0.10, 9.99);
      slider.setRange (0, 1000);
      slider.minWidth = 250;
      setPrecision (2);
      setValue (data.ShapeStart);
      toolTip = "<p>PSF Shape Start value.</p>";
      onValueUpdated = function (value) { data.ShapeStart = value; };
   }

   this.ShapeAmount_NC = new NumericControl (this);
   with ( this.ShapeAmount_NC ) {
      label.text = "Amount:";
      label.minWidth = labelWidth1;
      setRange (0.10, 9.99);
      slider.setRange (0, 1000);
      slider.minWidth = 250;
      setPrecision (2);
      setValue (data.ShapeAmount);
      toolTip = "<p>PSF Shape amount for each iteration.</p>";
      onValueUpdated = function (value) { data.ShapeAmount = value; };
   }



   this.Shapeiter_Label = new Label( this );
   this.Shapeiter_Label.minWidth = labelWidth1;
   this.Shapeiter_Label.text = "Previews:";
   this.Shapeiter_Label.textAlignment = TextAlign_Right|TextAlign_VertCenter;

   this.Shapeiter_SpinBox = new SpinBox( this );
   this.Shapeiter_SpinBox.minValue = 1;
   this.Shapeiter_SpinBox.maxValue = 10;
   this.Shapeiter_SpinBox.value = data.ShapeIterations;
   this.Shapeiter_SpinBox.toolTip = "<p>Number of Shape iterations.</p>";
   this.Shapeiter_SpinBox.onValueUpdated = function( value )
   {
      data.ShapeIterations = value;
   };

   this.Shapeiter_Sizer = new HorizontalSizer;
   this.Shapeiter_Sizer.spacing = 4;
   this.Shapeiter_Sizer.add( this.Shapeiter_Label );
   this.Shapeiter_Sizer.add( this.Shapeiter_SpinBox );
   this.Shapeiter_Sizer.addStretch();

   this.ShapeParGroupBox = new GroupBox( this );
   this.ShapeParGroupBox.title = "PSF Shape Parameters";
   this.ShapeParGroupBox.sizer = new VerticalSizer;
   this.ShapeParGroupBox.sizer.margin = 6;
   this.ShapeParGroupBox.sizer.spacing = 4;
   this.ShapeParGroupBox.sizer.add( this.ShapeAmount_NC );
   this.ShapeParGroupBox.sizer.add( this.ShapeStart_NC );

   this.ShapeParGroupBox.sizer.add( this.Shapeiter_Sizer );


    // usual control buttons
    this.ok_Button = new PushButton( this );
    this.ok_Button.text = "OK";
#ifneq __PI_PLATFORM__ MACOSX
    this.ok_Button.icon = new Bitmap( ":/images/icons/ok.png" );
#endif
    this.ok_Button.onClick = function()
    {
        this.dialog.ok();
    };

    this.cancel_Button = new PushButton( this );
    this.cancel_Button.text = "Cancel";
#ifneq __PI_PLATFORM__ MACOSX
    this.cancel_Button.icon = new Bitmap( ":/images/icons/cancel.png" );
#endif
    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 );

    this.sizer = new VerticalSizer;
    this.sizer.margin = 8;
    this.sizer.spacing = 6;
    this.sizer.add( this.helpLabel );
    this.sizer.addSpacing( 4 );
    this.sizer.add( this.targetImage_Sizer );
    this.sizer.add( this.GaussianParGroupBox);
    this.sizer.add( this.ShapeParGroupBox);
    this.sizer.addSpacing( 4 );
    this.sizer.add( this.buttons_Sizer );

    this.windowTitle = TITLE;
    this.adjustToContents();
    this.setFixedSize();
}

DeconvultionPreviewDialog.prototype = new Dialog;

/*
 * Script entry point.
 */
function main()
{
   console.show();

   if ( !data.targetView )
   {
      var msg = new MessageBox( "There is no active image window!",
                                "Deconvolution Preview Script",
                                StdIcon_Error, StdButton_Ok );
      msg.execute();
      return;
   }

   var dialog = new DeconvultionPreviewDialog();
   for ( ;; )
   {
      if ( !dialog.execute() )
         break;

      // A view must be selected.
      if ( data.targetView.isNull )
      {
         var msg = new MessageBox( "You must select a view to apply this script.",
                                   "Deconvolution Preview Script", StdIcon_Error, StdButton_Ok );
         msg.execute();
         continue;
      }

      //console.show();
      doDeconvultionPreview( data );
      break;
   }
console.hide();
}
main();

function doDeconvultionPreview(data){

   var w = ImageWindow.activeWindow;
   //if ( w.currentView.isNull )
     // throw Error( "No image window." );

    var v =data.targetView;

    var preview_img = new ImageWindow (
            v.image.width*(data.GaussianIterations)+((data.GaussianIterations+1)*3),
            v.image.height*(data.ShapeIterations)+((data.ShapeIterations+1)*3),
            3, w.mainView.image.bitsPerSample,
            w.mainView.image.sampleType == SampleType_Real,
            true, "DeconvultionPreview");
   with ( preview_img.mainView )
   {

       beginProcess( UndoFlag_NoSwapFile );
       image.fill (0x80000000);
       endProcess();
   }


   var copy_img = new ImageWindow (
            v.image.width,
            v.image.height,
            3, w.mainView.image.bitsPerSample,
            w.mainView.image.sampleType == SampleType_Real,
            true, "WorkingWindow");






for (var j = 0; j < data.GaussianIterations; j++) {
for (var i = 0; i < data.ShapeIterations; i++) {

   with ( copy_img.mainView )
   {

       beginProcess( UndoFlag_NoSwapFile );
       image.apply( v.image );
       doDeconvultion(copy_img.mainView,data.GaussianStart+(data.GaussianAmount)*j,data.ShapeStart+(data.ShapeAmount)*i);
       endProcess();
   }



   with ( preview_img.mainView )
   {

       beginProcess( UndoFlag_NoSwapFile );
       image.selectedPoint = new Point(v.image.width*j+(j*3)+3,v.image.height*i+(i*3)+3);
       image.apply( copy_img.mainView.image );
       endProcess();
   }
}
}
copy_img.undoAll();
copy_img.purge();
  copy_img.close();
  copy_img=null;
  preview_img.show();
  preview_img.zoomToFit();
}

function doDeconvultion(ParsedView,GaussianSigma,GaussianShape){


var p = new Deconvolution;
with ( p )
{
   algorithm = RichardsonLucy;
   numberOfIterations = 30;
   deringing = true;
   deringingDark = 0.1000;
   deringingBright = 0.0000;
   deringingSupport = false;
   deringingSupportAmount = 0.70;
   deringingSupportViewId = "";
   useLuminance = true;
   linear = false;
   psfMode = Gaussian;
   psfGaussianSigma = GaussianSigma;
   psfGaussianShape = GaussianShape;
   psfGaussianAspectRatio = 1.00;
   psfGaussianRotationAngle = 0.00;
   psfMotionLength = 5.00;
   psfMotionRotationAngle = 0.00;
   psfViewId = "";
   psfFFTSizeLimit = 15;
   useRegularization = true;
   waveletLayers = // noiseThreshold, noiseReduction
   [
   [3.00, 1.00],
   [2.00, 0.70],
   [1.00, 0.70],
   [1.00, 0.70],
   [1.00, 0.70]];
   noiseModel = Gaussian;
   numberOfWaveletLayers = 2;
   scalingFunction = B3Spline5x5;
   convergence = 0.0000;
   rangeLow = 0.0000000;
   rangeHigh = 0.0000000;
   iterations = // count
   [
   [0],
   [0],
   [0]];
}
    p.executeOn(ParsedView);
    var data = new DrawSignatureData(ParsedView,"Sigma:" + GaussianSigma +" Shape: " + GaussianShape);
    DrawSignature(data );
}



function DrawSignature(data )
{
var image = data.targetView.image;
   // Create the font
   var font = new Font( data.fontFace );
   font.pixelSize = data.fontSize;
   // Calculate a reasonable inner margin in pixels
   var innerMargin = Math.round( font.pixelSize/5 );
   // Calculate the sizes of our drawing box
   var width = font.width( data.text ) + 2*innerMargin;
   var height = font.ascent + font.descent + 2*innerMargin;
   // Create a bitmap where we'll perform all of our drawing work
   var bmp = new Bitmap( width, height );
  // Fill the bitmap with the background color
   bmp.fill( 0x80000000 );
   // Create a graphics context for the working bitmap
   var G = new Graphics( bmp );

   // Select the required drawing tools: font and pen.
   G.font = font;
   G.pen = new Pen( data.textColor );
   G.transparentBackground = true; // draw text with transparent bkg
   G.textAntialiasing = true;

   // Now draw the signature
   G.drawText( innerMargin, height - font.descent - innerMargin, data.text );

   // Finished drawing
   G.end();
image.selectedPoint = new Point( data.margin,
                                    image.height - data.margin - height );
   image.blend( bmp );
}


function DrawSignatureData(cView,text)
{
      this.targetView = cView;
      this.text = text;
      this.fontFace = "Helvetica";
      this.fontSize = 14; // px
      this.bold = true;
      this.italic = false;
      this.stretch = 100;
      this.textColor = 0xffff7f00;
      this.bkgColor = 0x80000000;
      this.margin = 2;
 }

Espero que os sea de utilidad. Un saludo. Silvercup.



Hi all:

I just write my first script: Deconvolution Preview. The script allows us to show several previews of a deconvoluted image width the Sigma (Standart deviation) and Shape factors selected.

The script was designed for cases where we don't have data for the PSF calculation. We are at the case of Blind Deconvolution. Blind deconvolution works by guessing different PSFs from actual image data and estimating the obtained improvement in the image after each try. Sigma and Shape values are obtained based on trial and error. This script helps us to choose the correct values by merging the selected "previews" in an image and drawing each Shape and Sigma values in the lower right corner for an easy reference.

An image with the result of the script and the source code are in the spanish text part.

I hope you find useful. Greetings. Silvercup.

« Last Edit: 2009 June 16 14:24:19 by Silvercup »

Offline Nocturnal

  • PixInsight Jedi Council Member
  • *******
  • Posts: 2727
    • http://www.carpephoton.com
Re: New Script: Deconvolution Preview
« Reply #1 on: 2009 June 15 15:04:40 »
That looks really useful, thanks!
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/
Re: New Script: Deconvolution Preview
« Reply #2 on: 2009 June 16 03:31:27 »
Hola Juan Manuel

Bienvenido a PixInsight Forum, y felicidades por un magnífico script :)

El script funciona muy bien, es una gran idea y una herramienta muy útil. Sólo un pequeño detalle, no es "deconvultion", sino deconvolution.

Ahora sólo nos faltaría idear una forma eficiente de evaluar la calidad o idoneidad del resultado obtenido automáticamente (¿análisis de entropía multiescala? ¿cuantificación de estructuras?). Teniendo eso y refinando algo más el algoritmo de generación de ensayos (por ejemplo, haciendo una búsqueda binaria para optimizar el resultado), tendríamos un sistema de deconvolución a ciegas (blind deconvolution). ¿Alguien se anima? ;D

=======================================

Hi Juan Manuel

Welcome to PixInsight Forum, and congratulations on an excellent script! :)

The script works very well; it's a great idea and a very useful tool. Just a minor thing: it isn't "deconvultion", but deconvolution.

Now we just need an efficient way to evaluate the quality or suitability of the obtained result automatically (multiscale entropy analysis? structure quantification?). Then we could refine the trial generation algorithm (for example, performing a binary search to optimize the result), and this would be a true blind deconvolution system. Somebody? ;D
Juan Conejero
PixInsight Development Team
http://pixinsight.com/

Offline Silvercup

  • PixInsight Addict
  • ***
  • Posts: 187
Re: New Script: Deconvolution Preview
« Reply #3 on: 2009 June 16 15:15:46 »
Hola Juan:

Gracias por los comentarios. Ya he cambiado las referencias a deconvultion por deconvolution, no se porqué pero siempre lo he leido como lo he escrito sin darme cuenta.

Espero, si el tiempo me lo permite, mejorar el script con soporte para local derringing en los próximos días.

Tambien estoy con otro script que promete mucho.

Un saludo.

================================================

Hi Juan:

Thanks for your comments. I Have changed the references of deconvultion to deconvolution. I don't know why, but I always have readed it as I have wrote it without notice.

I hope to improve the Script width local derringing support in the next days, if I have some time.

I am developing another Script with great perspectives.

Best,

Silvercup

 

Offline mmirot

  • PixInsight Padawan
  • ****
  • Posts: 881
Re: New Script: Deconvolution Preview
« Reply #4 on: 2009 June 18 17:42:35 »
Well done,

Using all varibles such as noise, threshold, deringing would be nice.
This was on my wish list.

Thanks

Max