New Script: Deconvolution Preview

Silvercup

Well-known member
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:

user_14_deconvultionpreview.jpg


Code:
#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.

 
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
 
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

 
 
Well done,

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

Thanks

Max
 
Back
Top