Author Topic: Fix dark columns script  (Read 9296 times)

Offline bianry

  • PixInsight Enthusiast
  • **
  • Posts: 90
Fix dark columns script
« on: 2014 March 11 13:37:35 »
I need to fix dark columns in some of my frames.
I found this script http://pixinsight.com/forum/index.php?topic=806.15 and has added to it.
You can now save it as a process to the desktop. There were also some bugs in the remove procedure that are fixed now.

I have only a Win7 machine to test on so would appreciate if someone tried it. Might be some bugs still.....

Code: [Select]
// Script to correct columns and row defects of an image
// Based on the script from the image processing tutorial:
// "NGC 5189 from GeminiObservatory..." by J.Conejero
// and on the script to average two columns by Sander:
// http://pixinsight.com/forum/viewtopic.php?t=748

//Modified by Mats Andersson 2014

#feature-id Utilities > FixDarkColumns

#feature-info  An example script to fix darker columns that are still in frame \
               after calibration.

#feature-icon  DrawSignature.xpm

#include <pjsr/Sizer.jsh>
#include <pjsr/FrameStyle.jsh>
#include <pjsr/TextAlign.jsh>
#include <pjsr/StdButton.jsh>
#include <pjsr/StdIcon.jsh>
#include <pjsr/DataType.jsh>
#include <pjsr/ColorSpace.jsh>

#define VERSION   1.1
#define TITLE     FixDarkColumns
#define NO__DEBUG__


function FixColumnEngine()
{

   this.initialize = function()
   {
      // Default parameters
      this.fixColumn = true;
      this.fixRow    = false;
      this.position  = 0;
      this.iterations = 1;

      this.doColumns = false;
      this.doRows    = false;
      this.columns   = new Array;
      this.rows      = new Array;
      this.average   = true;


      if ( Parameters.isViewTarget || Parameters.isGlobalTarget )
      {
         // Our script is being executed as a Script instance.

         // Retrieve instance parameters
         if ( Parameters.has( "position" ) )
            this.position = Parameters.getUInt( "position" );

         if ( Parameters.has( "iterations" ) )
            this.iterations = Parameters.getUInt( "iterations" );

         if ( Parameters.has( "columns" ) )
         {
            var temp = Parameters.getString( "columns" ).split(",");
            for(var i = 0; i < temp.length; i++)
               this.columns.push(parseInt(temp[i]));
            this.doColumns = true;
         }

         if ( Parameters.has( "rows" ) )
         {
            var temp = Parameters.getString( "rows" ).split(",");
            for(var i = 0; i < temp.length; i++)
               this.rows.push(parseInt(temp[i]));
            this.doRows = true;
         }


      }

      if ( Parameters.isViewTarget )
         // View context: use the target view.
         this.targetView = Parameters.targetView;
      else
      {
         // Direct or global contexts: use the active view.
         var window = ImageWindow.activeWindow;
         if ( !window.isNull )
            this.targetView = window.currentView;
      }

   };

   this.apply = function()
   {
      with ( this )
      {
         // Export script parameters. We must carry out this here, *before* applying
         // our routine to targetView, so that a newly created Script instance will
         // encapsulate our current set of working parameters.
         exportParameters();

         // Tell the core application that we are going to change this view.
         // Without doing this, we'd have just read-only access to the view's image.
         targetView.beginProcess();

         // Perform our drawing routine.
         fixLinesEngine();

         // Done with view.
         targetView.endProcess();
      }
   };

   this.exportParameters = function()
   {
      with ( this )
      {
         Parameters.set( "position", position );
         Parameters.set( "iterations", iterations );

         if(columns.length > 0)
            Parameters.set( "columns", columns );

         if(rows.length > 0)
            Parameters.set( "rows", rows );


      }
   };



   this.initialize();

}

// Global FixColumns parameters.
var engine = new FixColumnEngine;

/**
 * FixColumnDialog is a graphical user interface to define
 * FixColumn parameters.
 */
function FixColumnDialog()
{
   this.__base__ = Dialog;
   this.__base__();

   var emWidth = this.font.width( 'M' );
    var labelWidth1 = this.font.width( "Column/Row position (px):" );
    var labelWidth2 = this.font.width( "Iterations:" );
    var spinWidth1 = 6 * emWidth;
    var spinWidth2 = 4 * emWidth;

    //

    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 = "<b>FixDarkColumns Script</b> - Script to correct  "+
        "columns or rows defects in an image."
    //

    this.colRowTreeBox = new TreeBox( this );
    this.colRowTreeBox.multipleSelection = true;
    this.colRowTreeBox.rootDecoration = false;
    this.colRowTreeBox.setMinSize( 500, 200 );
    this.colRowTreeBox.numberOfColumns = 2;
    this.colRowTreeBox.headerVisible = false;

    this.dialog.colRowTreeBox.canUpdate = false;

    for (var i = 0;i < engine.columns.length; i++)
    {
       var node = new TreeBoxNode(this.dialog.colRowTreeBox);
       node.setText(0,"column");
       node.setText(1,format("%d",engine.columns[i]));
     }
     this.dialog.colRowTreeBox.canUpdate = true;

    for (var i = 0;i < engine.rows.length; i++)
    {
       var node = new TreeBoxNode(this.dialog.colRowTreeBox);
       node.setText(0,"row");
       node.setText(1,format("%d",engine.rows[i]));
     }
     this.dialog.colRowTreeBox.canUpdate = true;

    //

    this.colRowPos_Label = new Label( this );
    this.colRowPos_Label.text = "Column/Row position (px):";
    this.colRowPos_Label.textAlignment = TextAlign_Right|TextAlign_VertCenter;
    this.colRowPos_Label.minWidth = labelWidth1;

    this.colRow_SpinBox = new SpinBox( this );
    this.colRow_SpinBox.minValue = 0;
    this.colRow_SpinBox.maxValue = Math.max(engine.targetView.image.height - 1,engine.targetView.image.width - 1);
    this.colRow_SpinBox.value = engine.position ;
    this.colRow_SpinBox.setFixedWidth( spinWidth1 );
    this.colRow_SpinBox.toolTip = "Column or Row position";

    this.iterations_Label = new Label( this );
    this.iterations_Label.text = "Iterations:";
    this.iterations_Label.textAlignment = TextAlign_Right|TextAlign_VertCenter;
    this.iterations_Label.minWidth = labelWidth2;

    this.iterations_SpinBox = new SpinBox( this );
    this.iterations_SpinBox.minValue = 1;
    this.iterations_SpinBox.maxValue = 20;
    this.iterations_SpinBox.value = engine.iterations ;
    this.iterations_SpinBox.setFixedWidth( spinWidth2 );
    this.iterations_SpinBox.toolTip = "Number of iterations";

    this.colRow_SpinBox.onValueUpdated = function( value )
    {
        engine.position = value;
    };

    this.iterations_SpinBox.onValueUpdated = function( value )
    {
        engine.iterations = value;
    };


    this.colRow_Sizer = new HorizontalSizer;
    this.colRow_Sizer.spacing = 6;
    this.colRow_Sizer.add( this.colRowPos_Label );
    this.colRow_Sizer.add( this.colRow_SpinBox );
    //this.colRow_Sizer.addStretch();
    this.colRow_Sizer.add( this.iterations_Label );
    this.colRow_Sizer.add( this.iterations_SpinBox );
    this.colRow_Sizer.addStretch();

    // Type of defect: label
    this.typeOfDefectLab = new Label (this);
    with (this.typeOfDefectLab) {
        text = "Type of Defect:";
        textAlignment = TextAlign_Right | TextAlign_VertCenter;
        minWidth = labelWidth1;
    }
    // Type of defect: radio buttons
    this.typeOfDefectCol = new RadioButton (this);
    with (this.typeOfDefectCol) {
        text = "Column";
        checked=true;
        onCheck = function(checked) {
            engine.fixColumn = checked;
        }
    }

    this.typeOfDefectRow = new RadioButton (this);
    with (this.typeOfDefectRow) {
        text = "Row";
        onCheck = function(checked) {
            engine.fixRow = checked;
        }
    }

    // mask blur method: sizer
    this.typeOfDefectSize = new HorizontalSizer (this);
    with (this.typeOfDefectSize) {
        spacing = 16;
        add (this.typeOfDefectLab);
        add (this.typeOfDefectCol);
        add (this.typeOfDefectRow);
        addStretch();
    }

    //

    this.addButton = new PushButton( this );
    this.addButton.text = " Add ";

    this.addButton.onClick = function() {
       this.dialog.colRowTreeBox.canUpdate = false;
       if(engine.fixColumn) {
           var isDone = false;
           for(var i=0;i<engine.columns.length;++i)
                if(engine.position==engine.columns[i]) isDone = true;
           if(!isDone) {
               var node = new TreeBoxNode(this.dialog.colRowTreeBox);
               node.setText(0,"column");
               node.setText(1,format("%d",engine.position));
               engine.columns.push(engine.position);
               engine.doColumns = true;
           }
       } else {
           var isDone = false;
           for(var i=0;i<engine.rows.length;++i)
               if(engine.position==engine.rows[i]) isDone = true;
           if(!isDone) {
               var node = new TreeBoxNode(this.dialog.colRowTreeBox);
               node.setText(0,"row");
               node.setText(1,format("%d",engine.position));
               engine.rows.push(engine.position);
               engine.doRows = true;
           }
       }
       this.dialog.colRowTreeBox.canUpdate = true;
       CheckButtonStatus(this.dialog);

    };

    this.removeButton = new PushButton( this );
    this.removeButton.text = " Remove ";
    this.removeButton.onClick = function() {

      engine.columns.length = 0;
      engine.doColumns = false;
      engine.rows.length = 0;
      engine.doRows = false;
      for ( var i = 0; i <this.dialog.colRowTreeBox.numberOfChildren; i++ )
      {
         console.writeln("i=" + i);
         if ( !this.dialog.colRowTreeBox.child( i ).selected )
         {
            if(this.dialog.colRowTreeBox.child(i).text(0)=="column")
            {
               engine.columns.push(parseInt(this.dialog.colRowTreeBox.child(i).text(1)));
               engine.doColumns=true;
            }
            else
            {
               engine.rows.push(parseInt(this.dialog.colRowTreeBox.child(i).text(1)));
               engine.doRows = true;
            }
         }

      }
      //Empty tree
      for (var i = this.dialog.colRowTreeBox.numberOfChildren; i >= 0; --i) this.dialog.colRowTreeBox.remove(i);

      this.dialog.colRowTreeBox.canUpdate = false;
      for(var i = 0; i < engine.columns.length; i++)
      {
         var node = new TreeBoxNode(this.dialog.colRowTreeBox);
         node.setText(0,"column");
         node.setText(1,format("%d",engine.columns[i]));
      }
      for(var i = 0; i < engine.rows.length; i++)
      {
         var node = new TreeBoxNode(this.dialog.colRowTreeBox);
         node.setText(0,"row");
         node.setText(1,format("%d",engine.rows[i]));
      }

      this.dialog.colRowTreeBox.canUpdate = true;
      CheckButtonStatus(this.dialog);

    };

    this.clearButton = new PushButton( this );
    this.clearButton.text = " Clear ";
    this.clearButton.onClick = function()
    {
      for (var i = this.dialog.colRowTreeBox.numberOfChildren; i >= 0; --i) this.dialog.colRowTreeBox.remove(i);
      engine.columns.length = 0;
      engine.doColumns = false;
      engine.rows.length = 0;
      engine.doRows = false;
      CheckButtonStatus(this.dialog);

    }

    this.addRemButtonsSizer = new HorizontalSizer;
    this.addRemButtonsSizer.spacing = 4;
    this.addRemButtonsSizer.add( this.typeOfDefectSize );
    this.addRemButtonsSizer.addStretch();
    this.addRemButtonsSizer.add( this.addButton );
    this.addRemButtonsSizer.add( this.removeButton );
    this.addRemButtonsSizer.add( this.clearButton );

    //

    this.colRowGroupBox = new GroupBox( this );
    this.colRowGroupBox.title = "Defects to correct:";
    this.colRowGroupBox.sizer = new VerticalSizer;
    this.colRowGroupBox.sizer.margin = 4;
    this.colRowGroupBox.sizer.spacing = 4;
    this.colRowGroupBox.sizer.add( this.colRowTreeBox, 100 );
    this.colRowGroupBox.sizer.add( this.colRow_Sizer );
    this.colRowGroupBox.sizer.add( this.addRemButtonsSizer );

    //

    this.save_Button = new PushButton( this );
    this.save_Button.text = " Save ";

    this.save_Button.onClick = function()
    {
        var saveFile = new SaveFileDialog;
        if(saveFile.execute()) {
            var fileName = saveFile.fileName;
            var f = new File;
            f.createForWriting(fileName);
            f.write(engine.columns.length,DataType_UInt16);
            console.writeln("Number of columns saved: " + engine.columns.length);
            if(engine.columns.length != 0)
               f.write(engine.columns,DataType_Int32);

            f.write(engine.rows.length,DataType_UInt16);
            console.writeln("Number of rows saved: " + engine.rows.length);
            if(engine.rows.length != 0)
               f.write(engine.rows,DataType_Int32);

            f.write(engine.iterations,DataType_UInt16);
            console.writeln("Number of iterations saved: " + engine.iterations);
            f.close();
        }
    };
    //

    this.load_Button = new PushButton( this );
    this.load_Button.text = " Load ";



    this.load_Button.onClick = function()
    {

        var loadFile = new OpenFileDialog;

        if(loadFile.execute()) {
            var fileName = loadFile.fileName;
            var f = new File;

            f.openForReading(fileName);

            for (var i = this.dialog.colRowTreeBox.numberOfChildren; i >= 0; --i) this.dialog.colRowTreeBox.remove(i);

            engine.columns.length = 0;
            engine.doColumns = false;

            var size = f.read(DataType_UInt16,1);
            if(size!=0) {
                engine.doColumns = true;
                if(size!=1) engine.columns = f.read(DataType_Int32,size);
                else        engine.columns.push(f.read(DataType_Int32,size));
                this.dialog.colRowTreeBox.canUpdate = false;
                for (var i = 0;i<size; i++) {
                    var node = new TreeBoxNode(this.dialog.colRowTreeBox);
                    node.setText(0,"column");
                    node.setText(1,format("%d",engine.columns[i]));
                }
                this.dialog.colRowTreeBox.canUpdate = true;
            }

            console.writeln("Number of columns read: " + engine.columns.length);

            engine.rows.length = 0;
            engine.doRows = false;

            size = f.read(DataType_UInt16,1);
            if(size!=0) {
                engine.doRows = true;
                if(size!=1) engine.rows = f.read(DataType_Int32,size);
                else        engine.rows.push(f.read(DataType_Int32,size));
                this.dialog.colRowTreeBox.canUpdate = false;
                for (var i = 0;i<size; i++) {
                    var node = new TreeBoxNode(this.dialog.colRowTreeBox);
                    node.setText(0,"row");
                    node.setText(1,format("%d",engine.rows[i]));
                }
                this.dialog.colRowTreeBox.canUpdate = true;
            }
            console.writeln("Number of rows read: " + engine.rows.length);

            size = f.read(DataType_UInt16,1);
            if(size != 0)
            {
              engine.iterations = parseInt(size);
              this.dialog.iterations_SpinBox.value = engine.iterations ;
            }

            f.close();
        }

        CheckButtonStatus(this.dialog);

    };


    //

    this.ok_Button = new PushButton( this );
    this.ok_Button.text = " OK ";

    this.ok_Button.onClick = function()
    {
        this.dialog.ok();
    };

    this.cancel_Button = new PushButton( this );
    this.cancel_Button.text = " Cancel ";

    this.cancel_Button.onClick = function()
    {
        this.dialog.cancel();
    };

   this.newInstanceButton = new ToolButton(this);
   this.newInstanceButton.icon = ":/process-interface/new-instance.png";
   this.newInstanceButton.toolTip = "New Instance";
   this.newInstanceButton.onMousePress = function ()
   {
      this.hasFocus = true;

      engine.exportParameters();

      this.pushed = false;
      this.dialog.newInstance();
   };

    this.buttons_Sizer = new HorizontalSizer;
    this.buttons_Sizer.spacing = 4;
    this.buttons_Sizer.add( this.newInstanceButton );
    this.buttons_Sizer.add( this.load_Button );
    this.buttons_Sizer.add( this.save_Button );
    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 = 6;
    this.sizer.spacing = 6;
    this.sizer.add( this.helpLabel );
    this.sizer.addSpacing( 4 );
    this.sizer.add( this.colRowGroupBox );
    this.sizer.add( this.buttons_Sizer );

    this.windowTitle = "FixLines Script";
    this.adjustToContents();
    this.setFixedSize();

   CheckButtonStatus(this);

}

// Our dialog inherits all properties and methods from the core Dialog object.
FixColumnDialog.prototype = new Dialog;

function CheckButtonStatus(sender)
{
   sender.save_Button.enabled = sender.dialog.colRowTreeBox.numberOfChildren > 0;
   sender.ok_Button.enabled = sender.dialog.colRowTreeBox.numberOfChildren > 0;
   sender.removeButton.enabled = sender.dialog.colRowTreeBox.numberOfChildren > 0;
   sender.clearButton.enabled = sender.dialog.colRowTreeBox.numberOfChildren > 0;
}

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

function FixRow( image, row, channel)
{
   if(row == 0)
      row = 1;
    image.selectedChannel = channel;
    for ( var x = 0; x < image.width; ++x )
        image.setSample( (image.sample( x, row-1, channel ) + image.sample( x , row+1, channel ))/2, x, row, channel );
}

function fixLinesEngine() {
   var view = engine.targetView;
   var image = engine.targetView;

   with (view) {
       var isGray = image.colorSpace==ColorSpace_Gray;
       console.writeln("iterations: " + engine.iterations);
       
       for(var ii = 1 ; ii <= engine.iterations; ++ii)
       {
          if(engine.doColumns) {
              if(isGray) {
                  for(var i=0;i<engine.columns.length;++i) {
                     FixColumn(image, engine.columns[i], 0);
                  }
              } else {
                  for(var i=0;i<engine.columns.length;++i) {
                      FixColumn(image,engine.columns[i], 0);
                      FixColumn(image,engine.columns[i], 1);
                      FixColumn(image,engine.columns[i], 2);
                  }
              }
          }
          if(engine.doRows) {
              if(isGray) {
                  for(var i=0;i<engine.rows.length;++i) {
                      FixRow(image, engine.rows[i], 0);
                  }
              } else {
                  for(var i=0;i<engine.rows.length;++i) {
                      FixRow(image,engine.rows[i], 0);
                      FixRow(image,engine.rows[i], 1);
                      FixRow(image,engine.rows[i], 2);
                  }
              }
         }
      }

   }
}

/*
 * Script entry point.
 */
function main()
{
   // If the script is being executed as a Script instance on a view context,
   // then apply it and exit, without showing any graphical user interface.
   // This allows us to run a script just as a regular (module-defined) process
   // instance.
     if ( Parameters.isViewTarget )
   {
      engine.apply();
      return;
   }
#ifndef __DEBUG__
   console.hide();
#endif

   // If the script is being executed either directly or in the global context,
   // then we need a target view, so an image window must be available.
   if ( !engine.targetView )
   {
      var msg = new MessageBox( "There is no active image window!",
                                (#TITLE + " Script"), StdIcon_Error, StdButton_Ok );
      msg.execute();
      return;
   }
   var dialog = new FixColumnDialog();
   for ( ;; )
   {
      // Execute the FixColumnDialog dialog.
      if ( !dialog.execute() )
         break;
      // A view must be selected.
      if ( engine.targetView.isNull )
      {
         var msg = new MessageBox( "You must select a view to apply this script.",
                                   (#TITLE + " Script"), StdIcon_Error, StdButton_Ok );
         msg.execute();
         continue;
      }
      // Perform the FixColumns routine.
      engine.apply();
      // Quit after successful execution.
      break;
   }
}

main();

// ****************************************************************************
// EOF



Regards

Mats
« Last Edit: 2014 March 12 01:04:03 by bianry »

Offline Alejandro Tombolini

  • PTeam Member
  • PixInsight Jedi
  • *****
  • Posts: 1267
    • Próxima Sur
Re: Fix dark columns script
« Reply #1 on: 2014 March 11 13:57:18 »
Hi Mats, thank you for the script.   :)
I will try it on weekend on Linux.

Saludos
Alejandro.

Offline bianry

  • PixInsight Enthusiast
  • **
  • Posts: 90
Re: Fix dark columns script
« Reply #2 on: 2014 March 12 01:05:58 »
Added Clear button and spinbox for iterations
Source in first post.

r

Mats

Offline sreilly

  • PixInsight Padawan
  • ****
  • Posts: 791
    • Imaging at Dogwood Ridge Observatory
Re: Fix dark columns script
« Reply #3 on: 2014 April 02 15:59:38 »
OK, willing to show my ignorance here. I've used MaxIm for years to acquire my data and calibrate it. Now I use PI almost exclusively  for processing , including calibration, until I have something I can't do or at least am too ignorant to discover. One of those problems has been creating bad pixel maps as I do in MaxIm. I basically need two. One for bin 1x1 and the other bin 2x2. Typically calibration with the darks have taken care of all but a few. I'm guessing I can read the column locations on the maps created by MaxIm and input them into this script. Where I'm lost after much looking is where is the downloadable script or how do I use the included code to create and save one?

Steve
www.astral-imaging.com
AP1200
OGS 12.5" RC
Tak FSQ-106ED
ST10XME/CFW8/AO8
STL-11000M/FW8/AO-L
Pyxis 3" Rotator
Baader LRGBHa Filters
PixInsight/MaxIm/ACP/Registar/Mira AP/PS CS5

Offline Geoff

  • PixInsight Padawan
  • ****
  • Posts: 908
Re: Fix dark columns script
« Reply #4 on: 2014 April 02 16:40:57 »
One of those problems has been creating bad pixel maps as I do in MaxIm. I basically need two. One for bin 1x1 and the other bin 2x2.
Hi Steve
Try making a defect map:
http://pixinsight.com/forum/index.php?topic=4218.msg29663#msg29663
http://pixinsight.com/forum/index.php?topic=4411.msg30905#msg30905
http://pixinsight.com/forum/index.php?topic=5323.msg36831#msg36831
Geoff
Don't panic! (Douglas Adams)
Astrobin page at http://www.astrobin.com/users/Geoff/
Webpage (under construction) http://geoffsastro.smugmug.com/

Offline Alejandro Tombolini

  • PTeam Member
  • PixInsight Jedi
  • *****
  • Posts: 1267
    • Próxima Sur
Re: Fix dark columns script
« Reply #5 on: 2014 April 02 18:29:14 »
Where I'm lost after much looking is where is the downloadable script or how do I use the included code to create and save one?

Hi Steve,
to run script presented like Mats published here see this video

Saludos, Alejandro.

Offline Josh Lake

  • PixInsight Old Hand
  • ****
  • Posts: 424
Re: Fix dark columns script
« Reply #6 on: 2014 April 03 08:39:57 »
OK, willing to show my ignorance here. I've used MaxIm for years to acquire my data and calibrate it. Now I use PI almost exclusively  for processing , including calibration, until I have something I can't do or at least am too ignorant to discover. One of those problems has been creating bad pixel maps as I do in MaxIm. I basically need two. One for bin 1x1 and the other bin 2x2. Typically calibration with the darks have taken care of all but a few. I'm guessing I can read the column locations on the maps created by MaxIm and input them into this script. Where I'm lost after much looking is where is the downloadable script or how do I use the included code to create and save one?

Also, have you tried Cosmetic Correction? I can be added into the Batch Preprocessing script and it is like a magic eraser for bad stuff once you set it up. Check out Harry's tutorial (part of the BPP tutorial): http://www.harrysastroshed.com/Calibration.html

Offline sreilly

  • PixInsight Padawan
  • ****
  • Posts: 791
    • Imaging at Dogwood Ridge Observatory
Re: Fix dark columns script
« Reply #7 on: 2014 April 04 19:04:34 »
Thanks guys, I figured the copy and paste part and saved as needed. Now I need to figure out if this can be used as a process that can be used on a batch of images. I typically dark subtract and flat field the images. At this point I would think it appropriate to use the script to fix the bad columns. My current image of M58 now has 35 fifteen minute luminance frames to use and 38 rgb frames (10 minutes each). It would be wonderful to be able to correct these defects at one time versus 73 times. I'd actually be happy doing it in the batches needed by bin sizes. Any ideas?

Thanks
Steve
www.astral-imaging.com
AP1200
OGS 12.5" RC
Tak FSQ-106ED
ST10XME/CFW8/AO8
STL-11000M/FW8/AO-L
Pyxis 3" Rotator
Baader LRGBHa Filters
PixInsight/MaxIm/ACP/Registar/Mira AP/PS CS5