Author Topic: PreviewAggregator Script  (Read 16961 times)

Offline David Serrano

  • PTeam Member
  • PixInsight Guru
  • ****
  • Posts: 503
PreviewAggregator Script
« on: 2009 February 20 12:18:49 »
[English below]

Cucú!

Normalmente, cuando estamos ajustando los parámetros de un proceso, trabajamos con un preview. Esto mola porque podemos tocar y probar los parámetros rápidamente sin tener que esperar a que el proceso trabaje sobre la imagen entera. Sin embargo, este modo de trabajo tiene un pequeño inconveniente: al concentrarnos en un preview y basar los parámetros en los resultados que vamos obteniendo con él, es más que posible que otros previews definidos pasen a mostrar un comportamiento subóptimo sin que nos demos cuenta. Y cuando finalmente lo hacemos, nos vemos en la tesitura de tener que buscar unos parámetros que funcionen decentemente bien para todos los previews, y probar el proceso alternativamente en todos ellos.

He probado a jugar con un ImageContainer para aliviar este problema y, si bien se puede aplicar el proceso de una sola vez a todos los previews que queramos, me encontré con dos pegas:

- No podemos ver todos los previews al mismo tiempo, y tenemos que alternar entre ellos para ver el resultado.
- El botón "Undo preview / Redo preview" se queda inactivo, no se pueden ver los cambios que el proceso ha hecho sobre los previews.

Esto provoca que las ventajas de usar ImageContainer sean muy pocas, y probablemente el inconveniente de no poder usar "Undo/Redo" las supera.

He hecho, por tanto, un scriptcillo que crea una imagen nueva conteniendo todos los previews que queramos. Así, los tenemos todos en la pantalla al mismo tiempo y podemos trabajar con ellos de una sola vez. El script también genera un preview conteniendo toda esta imagen nueva, ya que de todas formas sería lo primero que el usuario haría con ella.

Espero que os guste y saquéis buen provecho de él!

-
-

Hiya!

Usually, when we'are adjusting the parameters of some process, we work on a preview. This is nice because we can quickly touch and test the parameters without having to wait for the process to work on the entire image. However, this methodology has a little drawback: when we concentrate on a preview and base the parameters on the results we obtain with it, it's more than probable that the result in other defined previews becomes suboptimal without us noticing. And when we finally do, we're forced to look for a set of parameters that work fine for all previews, and alternately test the process on them all.

I tried to play with an ImageContainer to alleviate this problem and, while you can apply the process to all the previews you want at once, I've found two snags:

- We can't see all the previews at the same time, and we're forced to alternate among all of them to see the result.
- The button "Undo preview / Redo preview" becomes inactive, you can't see the changes that the process has done to the previews.

This causes that the advantage of using ImageContainer is very small, and probably surpassed by the drawback of not being able to use "Undo/Redo".

Therefore, I've done a little script that creates a new image containing all the previews we want. Thus, we have them all on the screen at the same time and we can work with them at once. The script also generates a preview containing this whole new image, since it would be the first thing the user would do on it anyway.

I hope you like it and get a lot of it!

--

Aquí va la versión 0.2.1. Here goes version 0.2.1.

Code: [Select]
/*
    PreviewAggregator.js v0.2.1 - Joins previews into a new image.
    Copyright (C) 2009  David Serrano.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, version 3 of the License.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/*
   Changelog:
   0.2.1: Bugfix: working without a mask was broken.
   0.2:   If all selected previews belong to same imagen, take into account
          the mask that the image may have, as per Oriol suggestion.
   0.1:   Initial version.
*/

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

#feature-id PreviewAggregator
#feature-info When using previews to fine-tune the parameters of a process, \
   it's often desired to pay attention two more than a preview at the same \
   time, so the adjustments done to the parameters in order to improve the \
   result in one area of the image don't degrade other areas. This script \
   creates an image containing other images' previews, so processes can be \
   applied to it and the user can see the results in all original previews \
   at once.
// #feature-icon KSIntegration.xpm

#define VERSION "0.2.1"

function pa_engine() {
   this.imgIDs = new Array;
   this.views = new Array;
   this.mask_views = new Array;
   this.anchors = new Array;
   this.new_image_width  = 0;
   this.new_image_height = 0;

   // Populates the views array.
   this.load = function() {
      var N = this.imgIDs.length;
      this.views.length = 0;
      for (var i = 0; i < N; i++) {
         this.views.push (new View (this.imgIDs[i]));
      }
   };

   // calculates new image dimensions
   this.imagesize = function () {
      var x = Math.ceil (Math.sqrt (this.views.length));
      var y = Math.ceil (this.views.length / x);

      var max_w = 0;
      var max_h = 0;
      for (var i = 0; i < this.views.length; i++) {
         var w = this.views[i].image.width;
         var h = this.views[i].image.height;
         if (w > max_w) { max_w = w; }
         if (h > max_h) { max_h = h; }
      }

      this.new_image_width  = x * max_w;
      this.new_image_height = y * max_h;
      //console.writeln ("x (",x,") y (",y,") max_w (",max_w,") max_h (",max_h,") width (",this.new_image_width,") height (",this.new_image_height,")");

      // now build array of anchors using x, y, max_w and max_h
      var anchor_x = -1;
      var anchor_y = 0;
      for (var i = 0; i < this.views.length; i++) {
         anchor_x++;
         if (anchor_x >= x) {   // technically '==' works, but let's cover us from any unexpected event
            anchor_x = 0;
            anchor_y++;
         }
         //console.writeln ("about to push point at x (",anchor_x*max_w,") y (",anchor_y*max_h,") for view (",i,")");
         this.anchors.push (new Point (anchor_x * max_w, anchor_y * max_h));
      }
   }

   this.parent_images = function() {
      var parent_images = {};
      for (var i = 0; i < this.imgIDs.length; i++) {
         var end_idx = this.imgIDs[i].indexOf ("->");
         var img_id = this.imgIDs[i].substr (0, end_idx);
         parent_images[img_id] = 1;
      }

      // this can probably be done by some sort of parent_images.length,
      // parent_images.keys or something like that, but didn't put much
      // effort in figuring out exactly how. Sorry for this, but...
      var n = 0;
      for (k in parent_images) { n++; }

      return n;
   }

   this.create_image = function() {
      var do_mask = 0;
      var orig_mask_win;
      if (this.views[0].window.mask.mainView.fullId) { console.writeln ("mask true (",this.views[0].window.mask.mainView.fullId,")"); }
      if (
         1 == this.parent_images() &&
         this.views[0].window.mask.mainView.fullId
      ) {
         do_mask = 1;
         orig_mask_win = this.views[0].window.mask;
      }

      this.imagesize();
      var window = new ImageWindow (
         this.new_image_width,
         this.new_image_height,
         this.views[0].image.numberOfChannels,
         this.views[0].image.bitsPerSample,
         this.views[0].image.sampleType != SampleType_Integer,
         this.views[0].image.colorSpace != ColorSpace_Gray,
         "Aggregated"
      );

      var mask_window;
      var mask_view;
      if (do_mask) {
         //console.writeln ("should work with a mask");
         // replicate previews in the mask
         for (var i = 0; i < this.views.length; i++) {
            var r = this.views[i].window.previewRect (this.views[i]);
            //console.writeln ("going to replicate preview (",i,") x (",r.x0,") y (",r.y0,") w (",r.width,") h (",r.height,")");
            this.mask_views[i] = orig_mask_win.createPreview (r, this.views[i].id);
         }

         // create another identical image for the mask
         new_mask_win = new ImageWindow (
            this.new_image_width,
            this.new_image_height,
            this.views[0].image.numberOfChannels,
            this.views[0].image.bitsPerSample,
            this.views[0].image.sampleType != SampleType_Integer,
            this.views[0].image.colorSpace != ColorSpace_Gray,
            "Aggregated_mask"
         );

         mask_view = new_mask_win.mainView;
         mask_view.beginProcess (UndoFlag_NoSwapFile);
      }

      var view = window.mainView;
      view.beginProcess (UndoFlag_NoSwapFile);
         view.image.fill (0);
         if (do_mask) { mask_view.image.fill (0); }

         // paste previews in their proper place
         for (var i = 0; i < this.views.length; i++) {
            //console.writeln ("applying image (",i,")");
            view.image.selectedPoint = this.anchors[i];
            view.image.apply (this.views[i].image);

            if (do_mask) {
               mask_view.image.selectedPoint = this.anchors[i];
               mask_view.image.apply (this.mask_views[i].image);
            }
         }
      view.endProcess();
      if (do_mask) {
         mask_view.endProcess();

         // delete created previews
         for (var i = 0; i < this.mask_views.length; i++) {
            orig_mask_win.deletePreview (this.mask_views[i]);
         }

         // assign mask
         window.mask = new_mask_win;
      }

      // usually users will create a preview on the new image. Anticipate it
      window.createPreview (0, 0, this.new_image_width, this.new_image_height);

      // TODO: select the new preview

      window.show();
      if (do_mask) { new_mask_win.show(); }
   }
}
var engine = new pa_engine();

function pa_dialog() {
   this.__base__ = Dialog;
   this.__base__();

   // ----- HELP LABEL

   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>PreviewAggregator v"+VERSION+"</b> - A script to create " +
      "a new image based on the contents of other images' previews. " +
      "Select the previews to be aggregated and click 'Ok'.";

   // ----- LIST OF TARGETS

   this.target_List = new TreeBox (this);
   this.target_List.setMinSize (400, 200);
   this.target_List.font = new Font ("monospace", 10); // best to show tabulated data
   this.target_List.numberOfColumns = 2;
   this.target_List.headerVisible = true;
   this.target_List.headerSorting = true;
   this.target_List.setHeaderText (0, "Views");
   this.target_List.setHeaderText (1, "Dimensions");
   this.target_List.setHeaderAlignment (0, Align_Left);
   this.target_List.setHeaderAlignment (1, Align_Left);

   var active_id = ImageWindow.activeWindow.mainView.fullId;
   // Node creation helper
   this.addViewNode = function (parent, view) {
      var node = new TreeBoxNode (parent);
      node.checkable = true;
      var id = view.fullId;
      // automatically select all previews in the active window
      var arrow = id.indexOf ("->");
      var parent_id = id.substring (0, arrow);
      if (active_id == parent_id) {
         node.checked = true;
      } else {
         node.checked = false;
      }

      node.setText (0, id);

      var image = view.image;
      var metadata = format ("%5d x %5d x %d", image.width, image.height, image.numberOfChannels);
      node.setText (1, metadata);

      return node;
   }

   // build the view tree structure
   var windows = ImageWindow.windows;
   for (var i = 0; i < windows.length; ++i) {
      var previews = windows[i].previews;
      for (var j = 0; j < previews.length; ++j) {
         this.addViewNode (this.target_List, previews[j]);
      }
   }
   this.target_List.sort();

   // Ensure that all columns are initially visible
   this.target_List.adjustColumnWidthToContents (0);
   this.target_List.adjustColumnWidthToContents (1);

   this.target_GroupBox = new GroupBox (this);
   this.target_GroupBox.title = "Previews to aggregate";
   this.target_GroupBox.sizer = new VerticalSizer;
   this.target_GroupBox.sizer.margin = 4;
   this.target_GroupBox.sizer.spacing = 4;
   this.target_GroupBox.sizer.add (this.target_List, 100);

   // ----- BUTTONS

   this.ok_Button = new PushButton (this);
   this.ok_Button.text = " OK ";
   // transfer the names of the selected images to the engine
   this.ok_Button.onClick = function() {
      for (n = 0; n < this.dialog.target_List.numberOfChildren; n++)
         if (this.dialog.target_List.child (n).checked)
            engine.imgIDs.push (this.dialog.target_List.child (n).text (0));
      this.dialog.ok();
   };

   this.cancel_Button = new PushButton (this);
   this.cancel_Button.text = " Cancel ";
   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);

   // ----- PACK EVERYTHING

   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.target_GroupBox, 100);
   this.sizer.add (this.buttons_Sizer);

   this.windowTitle = "PreviewAggregator v" + VERSION;
   this.adjustToContents();
}
pa_dialog.prototype = new Dialog;

function main() {
   var dialog = new pa_dialog();

   for (;;) {
      if (!dialog.execute()) {
         break;
      }

      engine.load();
      engine.create_image();

      // Quit after successful execution.
      break;
   }
}
main();
--
 David Serrano

Offline Juan Conejero

  • PTeam Member
  • PixInsight Jedi Grand Master
  • ********
  • Posts: 7111
    • http://pixinsight.com/
Re: PreviewAggregator Script
« Reply #1 on: 2009 February 23 05:47:57 »
Hi David,

An excellent work, and a really useful tool ;) Can I include this one in the next version?
Juan Conejero
PixInsight Development Team
http://pixinsight.com/

Offline OriolLehmkuhl

  • PixInsight Addict
  • ***
  • Posts: 177
    • http://www.astrosurf.com/brego-sky
Re: PreviewAggregator Script
« Reply #2 on: 2009 February 23 06:17:08 »
Hi David,

nice script, it is very useful :) Good work ;)

Regards,

Offline Nocturnal

  • PixInsight Jedi Council Member
  • *******
  • Posts: 2727
    • http://www.carpephoton.com
Re: PreviewAggregator Script
« Reply #3 on: 2009 February 23 06:59:58 »
Good grief that's a lot of code :)

Sounds like an excellent idea David, I'll have to give it a try.
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 C. Sonnenstein

  • PixInsight Addict
  • ***
  • Posts: 262
    • http://astrosurf.com/astro35mm
Re: PreviewAggregator Script
« Reply #4 on: 2009 February 23 14:15:23 »
Pues David, muchas gracias por compartirlo.

En estos momentos este script me va a ser muy útil. Por ejemplo, ahora estoy procesando imágenes de gran campo. Siempre que defines previsualizaciones sobre los objetos interesantes suelen estar dispersados por todo el campo:



Una vez ejecutado el script PreviewAggregator se obtiene una imagen de un tamaño mucho menor y con todas las nebulas juntas:



Muy molón, si. :D

Saludos.
Carlos Sonnenstein

Offline OriolLehmkuhl

  • PixInsight Addict
  • ***
  • Posts: 177
    • http://www.astrosurf.com/brego-sky
Re: PreviewAggregator Script
« Reply #5 on: 2009 February 23 14:31:55 »
Hi,

I was playing a little with the sctipt,  IMHO there is something to improve, if you want to use it in conjunction with a mask, the script don't build an equivalent mask for the new image. An option is to clone all the previews to the mask and use the script again in order to have the equivalent mask,



why not implement this step in the script also?

Offline David Serrano

  • PTeam Member
  • PixInsight Guru
  • ****
  • Posts: 503
Re: PreviewAggregator Script
« Reply #6 on: 2009 February 24 10:22:15 »
Quote from: "Juan Conejero"
Can I include this one in the next version?


Please do! :)


Quote from: "OriolLehmkuhl"
if you want to use it in conjunction with a mask, the script don't build an equivalent mask for the new image. An option is to clone all the previews to the mask and use the script again in order to have the equivalent mask


Hmm, quite right. In fact, while developing and testing the script, I found this behaviour myself, although didn't regard it as a problem and kept making duplicate masks as a result :). Working on it right now ;), this will only work when all selected previews belong to the same image.

On a side note, it's best that all previews have the same size, so the resulting image doesn't have any area with a black background. To achieve this, use the "Clone preview" button; it's in the Preview toolbar.

Thanks C. Sonnenstein for publishing some screen shots, I'm too lazy to do that ;).
--
 David Serrano

Offline OriolLehmkuhl

  • PixInsight Addict
  • ***
  • Posts: 177
    • http://www.astrosurf.com/brego-sky
Re: PreviewAggregator Script
« Reply #7 on: 2009 February 25 13:04:23 »
Quote from: "David Serrano"

On a side note, it's best that all previews have the same size, so the resulting image doesn't have any area with a black background. To achieve this, use the "Clone preview" button; it's in the Preview toolbar.


Good!  :wink:  Thanks for trick :D




It's great to view the effect of our prossesing on the galaxies all togather and quick   :P

regards,

Offline David Serrano

  • PTeam Member
  • PixInsight Guru
  • ****
  • Posts: 503
Re: PreviewAggregator Script
« Reply #8 on: 2009 February 25 14:08:07 »
He actualizado el primer mensaje con la versión 0.2. Por si acaso la voy a marcar como beta, que es tarde y no me he entretenido mucho en usarla y probarla.

Just updated the OP including version 0.2. I'd like to tag it as beta just in case, since it's late now and I didn't spend much time in using and testing it.
--
 David Serrano

Offline David Serrano

  • PTeam Member
  • PixInsight Guru
  • ****
  • Posts: 503
Re: PreviewAggregator Script
« Reply #9 on: 2009 March 04 15:30:21 »
Vaya, ya veo que todo el mundo usa esto con máscaras... lo acabo de probar sin máscara, una semana después de haber publicado la 0.2, y me he dado cuenta de que estaba roto ;). En fins, he actualizado con la 0.2.1, con este error corregido.
--
 David Serrano

Offline Juan Conejero

  • PTeam Member
  • PixInsight Jedi Grand Master
  • ********
  • Posts: 7111
    • http://pixinsight.com/
Re: PreviewAggregator Script
« Reply #10 on: 2009 March 05 15:47:40 »
Hola David

Muy buen trabajo. Sólo quería avisarte de que ya tengo PreviewAggregator incluido en la 1.5 que estoy preparando ;)
Juan Conejero
PixInsight Development Team
http://pixinsight.com/

Offline Jordi Gallego

  • PixInsight Addict
  • ***
  • Posts: 279
Re: PreviewAggregator Script
« Reply #11 on: 2009 April 13 05:22:08 »
Hola David,

este script es una magnífica aportación, realmente útil. Muchas gracias!! :wink:
Jordi Gallego
www.astrophoto.es

Offline Jack Harvey

  • PTeam Member
  • PixInsight Padawan
  • ****
  • Posts: 975
    • PegasusAstronomy.com & Starshadows.com
Re: PreviewAggregator Script
« Reply #12 on: 2009 April 13 06:11:56 »
What a great tool.  Thanks for all the work you put into this.  Lately the planning and building of PixInsight by Juan has, like the flowers of Spring, started to really bloom with these great scripts showing up.
Jack Harvey, PTeam Member
Team Leader, SSRO/PROMPT Imaging Team, CTIO