Author Topic: Aplicación radial de un proceso  (Read 6900 times)

Offline David Serrano

  • PTeam Member
  • PixInsight Guru
  • ****
  • Posts: 503
Aplicación radial de un proceso
« on: 2008 December 16 09:04:43 »
Hace mucho que quería hacer esto...

Este programilla aplica un proceso (en este caso ColorSaturation con un valor aleatorio de la saturación) a varias zonas de la imagen, divididas radialmente. Probadlo con una imagen diurna de 800x600.

Code: [Select]
#include <pjsr/UndoFlag.jsh>

var angular_width = 30;
var blur_angular_width = 2;
var radial_step = 100;
var center = new Point (400, 300);  // center of an 800x600 image

//

var pi = 3.1415926536;

function deg2rad (deg) { return deg * pi / 180; }

function apply_proc (v) {
    var rnd = Math.random() * -10;
    var p = new ColorSaturation;
    p.HS = [ // x, y
        [0.00000, rnd],
        [1.00000, rnd]
    ];
    p.HSt = 0;  // Akima
    p.hueShift = 0.000;
    p.executeOn (v);
}

var w = ImageWindow.activeWindow;
var v = w.currentView;
var i = v.image;

// window for the mask
var new_iw = new ImageWindow (
    i.width, i.height, 1, 8,  // 1 channel, 8 bits
    false,  // float sample type
    false,  // color
    'mask'
);
var new_v = new_iw.currentView;
var new_i = new_v.image;
//new_iw.show();

// setup
var white_pen = new Pen (0xffffffff);
var black_pen = new Pen (0xff000000);
var white_brush = new Brush (0xffffffff);
var black_brush = new Brush (0xff000000);

var bmp = new Bitmap (i.width, i.height);
var g = new Graphics (bmp);
new_v.beginProcess (UndoFlag_PixelData);

// action!
for (var rs = 0; rs < 300; rs += radial_step) {
    for (var n = 0; n < 360; n += angular_width) {
        var start = n + (blur_angular_width / 2);
        var end   = n + angular_width - (blur_angular_width / 2);
        console.writeln ("from start (",start,") to end (",end,")");

        // generate mask: larger pie minus smaller pie
        bmp.fill (0xff000000);
        g.pen = white_pen; g.brush = white_brush;
        g.drawPie (center, rs + radial_step, deg2rad (start), deg2rad (end - start));
        g.pen = black_pen; g.brush = black_brush;
        g.drawPie (center, rs,               deg2rad (start), deg2rad (end - start));
        new_i.blend (bmp);

        w.maskVisible = false;
        w.mask = new_iw;

        apply_proc (v);
        //break;
    }
    //break;
}

// cleanup
g.end();
new_v.endProcess();
with (new_iw) { removeMaskReferences(); undoAll(); purge(); close(); }
new_iw = null;
gc();


En principio el objetivo básico es sustituir ColorSaturation por Deconvolution, usando PSFs distintas pero siempre parecidas a esta... ;)
--
 David Serrano

Offline Juan Conejero

  • PTeam Member
  • PixInsight Jedi Grand Master
  • ********
  • Posts: 7111
    • http://pixinsight.com/
Aplicación radial de un proceso
« Reply #1 on: 2008 December 17 01:31:43 »
Esto es una muy buena idea que merece ser desarrollada en profundidad. Además se la puede generalizar a cualquier distribución espacial, más allá de una simetría radial. ¡Excelente! :)

En las pruebas que he hecho con este código no obtengo los resultados que se esperan. Básicamente me desatura la imagen (porque multiplicas por -10 al principio de apply_proc(), creo) y no genera una distribución radial. En cualquier caso se entiende perfectamente la idea y tus intenciones, que es lo que cuenta. Pero si además nos funciona el ejemplo, harás que babeemos, también :)

¡Bienvenido de vuelta!  :wink:
Juan Conejero
PixInsight Development Team
http://pixinsight.com/

Offline David Serrano

  • PTeam Member
  • PixInsight Guru
  • ****
  • Posts: 503
Aplicación radial de un proceso
« Reply #2 on: 2008 December 17 01:55:26 »
Quote from: "Juan Conejero"
Básicamente me desatura la imagen (porque multiplicas por -10 al principio de apply_proc(), creo)


Sí, es el valor mínimo que acepta el interface de ColorSaturation. Entre 0 y 1 apenas se nota el efecto.

Quote from: "Juan Conejero"
y no genera una distribución radial.


Hmm? esto sí que me mosquea. A mí me genera una caja de quesitos el Caserío (de los todavía fabricados en Menorca, mientras duren), pero en lugar de tener 8 tiene 12 (30 grados de arco cada uno). Y luego por fuera hace 2 pasadas más, pero a eso ya no se le pueden llamar "quesitos" :^P, ¿qué tal "limpiaparabrisas"? xD.

Una pega grande que he visto es que aplica el proceso a toda la imagen, aunque luego en virtud de la máscara sólo se aprovecha una pequeña parte. Si con ColorSaturation va lento, con Deconv tiene que ser la leche. Estoy pensando ahora en calcular la "bounding box" de los quesitos y así, para cada aplicación del proceso, crear una imagen nueva con ese trocito, aplicar y luego pegar el resultado por encima. También quiero revisar bien los bordes de las máscaras: tienen que estar difuminados de alguna manera, pero no quiero que algunos píxels se vean más afectados que otros; esto jode sobre todo en las esquinas de los quesitos.

Con respecto a mi regreso, este finde me cojo vacaciones y ya desaparezco otra vez. Estoy por aquí porque estos días no tengo mucho curro en la ofi (y también porque últimamente el foro rebosa de vida ;)).
--
 David Serrano

Offline OriolLehmkuhl

  • PixInsight Addict
  • ***
  • Posts: 177
    • http://www.astrosurf.com/brego-sky
Aplicación radial de un proceso
« Reply #3 on: 2008 December 17 15:18:27 »
Buenas,

la idea mola  8) pero coincido con vosotros que el javascript puede flipar intentando hacer tantas Deconvoluciones de manera global, a ver si te funciona lo de la fracionar la imagen ;) Ostras! seria casi como meterle threads al javascript

Oriol

Offline David Serrano

  • PTeam Member
  • PixInsight Guru
  • ****
  • Posts: 503
Aplicación radial de un proceso
« Reply #4 on: 2008 December 17 16:08:34 »
Está funcionando ya. Básicamente faltan 3 cosas:

- que los parámetros ATW se puedan cambiar después de creado, según el otro hilo,
- poder usar una PSF con forma de coma (a ver si cuela y el Altísimo la incorpora en Deconvolution ;); de lo contrario a ver cómo me arreglo),
- y que algunas partes de la imagen no se vean más o menos afectadas que otras, debido al difuminado de los bordes de las máscaras. Se aprecia super bien después de ejecutar el script sobre una imagen y seleccionando el modo de visualización "HSI Saturation". Creo que pondré un post para ilustrar mejor esto.

Ahora desarrollo sobre una imagen de 1280x800, y el proceso que se ejecuta es un HistogramTransformation, para un resultado más colorido.

Code: [Select]
#include <pjsr/ColorSpace.jsh>
#include <pjsr/SampleType.jsh>
#include <pjsr/UndoFlag.jsh>

var angular_width = 30;  // limitation: must be a divisor of 90: 45, 30, 15, 10, 9, 6, 5, 4, 3, 2, 1
var mask_blur = 5;
var radial_step = 150;
var center = new Point (640, 400);  // center of 1280x800

//

function deg2rad (deg) { return deg * Math.PI / 180; }

function apply_proc (v) {
    var rnd1; var rnd2; var rnd3; var rnd4;

    rnd1 = Math.random();
    rnd2 = Math.random();
    rnd3 = Math.random();
    rnd4 = Math.random();
    var p = new HistogramTransformation;
    p.H = [ // c0, m, c1, r0, r1
        [0.00000000,       rnd1, 1.00000000, 0.00000000, 1.00000000],
        [0.00000000,       rnd2, 1.00000000, 0.00000000, 1.00000000],
        [0.00000000,       rnd3, 1.00000000, 0.00000000, 1.00000000],
        [0.00000000,       rnd4, 1.00000000, 0.00000000, 1.00000000],
        [0.00000000, 0.50000000, 1.00000000, 0.00000000, 1.00000000]
    ];
    p.executeOn (v);
    return;
   
    rnd1 = Math.random() * -10;
    var p = new ColorSaturation;
    p.HS = [ // x, y
        [0.00000, rnd],
        [1.00000, rnd]
    ];
    p.HSt = 0;  // Akima
    p.hueShift = 0.000;
    p.executeOn (v);
    return;
}

function calc_bbox (start, end, rs, margin) {
    margin += 5;
    var corners_x = new Array (
        center.x + Math.cos (deg2rad (start)) * (rs + radial_step),
        center.x + Math.cos (deg2rad (end))   * (rs + radial_step),
        center.x + Math.cos (deg2rad (start)) * (rs),
        center.x + Math.cos (deg2rad (end))   * (rs)
    );
    var corners_y = new Array (
        center.y - Math.sin (deg2rad (start)) * (rs + radial_step),
        center.y - Math.sin (deg2rad (end))   * (rs + radial_step),
        center.y - Math.sin (deg2rad (start)) * (rs),
        center.y - Math.sin (deg2rad (end))   * (rs)
    );

    var left   = Math.min (corners_x) - margin;
    var top    = Math.min (corners_y) - margin;
    var right  = Math.max (corners_x) + margin;
    var bottom = Math.max (corners_y) + margin;

    return (new Rect (left, top, right, bottom));
}

var main_iw = ImageWindow.activeWindow;
main_iw.maskVisible = false;
var main_v = main_iw.currentView;
var main_i = main_v.image;

// window for the mask (same size as the main window)
var mask_iw = new ImageWindow (
    main_i.width, main_i.height, 1, 16,  // 1 channel, 16 bits
    false,  // real sample type
    false,  // colorspace gray
    'mask'
);
var mask_v = mask_iw.currentView;
var mask_i = mask_v.image;
//mask_iw.show();

// setup
var white_pen = new Pen (0xffffffff);
var black_pen = new Pen (0xff000000);
var white_brush = new Brush (0xffffffff);
var black_brush = new Brush (0xff000000);

var bmp = new Bitmap (main_i.width, main_i.height);
var g = new Graphics (bmp);
console.hide();
main_v.beginProcess (UndoFlag_PixelData);

// action!
for (var rs = radial_step; rs < 750; rs += radial_step) {
    for (var n = 0; n < 360; n += angular_width) {
        var start = n;                 // + (blur_angular_width / 2);
        var end   = n + angular_width; // - (blur_angular_width / 2);
        console.writeln ("from start (",start,") to end (",end,")");

        // STEP 1: make mask

        // generate mask: larger pie minus smaller pie
        bmp.fill (0xff000000);
        g.pen = white_pen; g.brush = white_brush;
        g.drawPie (center, rs + radial_step, deg2rad (start), deg2rad (end - start));
        g.pen = black_pen; g.brush = black_brush;
        g.drawPie (center, rs,               deg2rad (start), deg2rad (end - start));
        mask_v.beginProcess (UndoFlag_PixelData);
        mask_i.blend (bmp);
        mask_v.endProcess();

        // blur mask
        var atw = new ATrousWaveletTransform;
        with (atw) {
            version = 257;
            layers = [
                // enabled, biasEnabled, structureDetectionThreshold, structureDetectionRange, bias,
                // noiseReductionEnabled, noiseReductionFilter, noiseReductionAmount, noiseReductionIterations, noiseReductionKernelSize,
                // noiseReductionProtectSignificant, deringingEnabled, deringingAmount, deringingThreshold
                [false, true, 1.00, 3.00, 0.000, false, Recursive, 0.50, 2, 5, false, false, 0.50, 0.02000],
                [false, true, 1.00, 3.00, 0.000, false, Recursive, 0.50, 2, 5, false, false, 0.50, 0.02000],
                [false, true, 1.00, 3.00, 0.000, false, Recursive, 0.50, 2, 5, false, false, 0.50, 0.02000],
                [true, true, 1.00, 3.00, 0.000, false, Recursive, 0.50, 2, 5, false, false, 0.50, 0.02000],
                [true, true, 1.00, 3.00, 0.000, false, Recursive, 0.50, 2, 5, false, false, 0.50, 0.02000]
            ];
            scaleDelta = 1;
            scalingFunctionData = [
                0.292893,0.5,0.292893,
                0.5,1,0.5,
                0.292893,0.5,0.292893
            ];
            scalingFunctionKernelSize = 3;
            scalingFunctionNoiseSigma = [
                0.809,0.269,0.118,
                0.057,0.028,0.014,
                0.007,0.004,0.002
            ];
            scalingFunctionNoiseLayers = 9;
            scalingFunctionName = "3x3 Linear Interpolation";
            largeScaleFunction = NoFunction;
            curveBreakPoint = 0.75;
            noiseThresholdingAmount = 0.00;
            noiseThreshold = 3.00;
            lowRange = 0.000;
            highRange = 0.000;
            previewMode = Disabled;
            previewLayer = 0;
            toLuminance = true;
            toChrominance = false;
            linear = false;
        }
        //atw.layers[0][0] = false;
        //atw.layers[1][0] = false;
        //atw.layers[2][0] = false;
        //for (var l = 0; l < mask_blur; l++) {
        //    console.writeln ("before, layer (",l,") is (",atw.layers[l][0],")");
        //    atw.layers[l][0] = false;
        //    console.writeln ("after,  layer (",l,") is (",atw.layers[l][0],")");
        //}
        atw.executeOn (mask_v);

        // STEP 2: calculate bounding box
        var bbox = new Rect (calc_bbox (start, end, rs, mask_blur));
        //console.writeln ("bbox left (",bbox.left,") top (",bbox.top,") right (",bbox.right,") bottom (",bbox.bottom,")");
        if (0) { // draw four lines to the mask, to make the bbox visible
            g.pen = white_pen;
            g.drawLine (bbox.left, 0, bbox.left, i.height);
            g.drawLine (0, bbox.top, i.width, bbox.top);
            g.drawLine (bbox.right, 0, bbox.right, i.height);
            g.drawLine (0, bbox.bottom, i.width, bbox.bottom);
            mask_v.beginProcess (UndoFlag_PixelData);
            mask_i.blend (bmp);
            mask_v.endProcess();
        }

        // STEP 3: new images (main image and mask) using bbox data
        var preview;
        // PixInsight doesn't allow previews to be partly outside of an image
        // if part of bbox is out of the image, the preview will be:
        //   moved if the left of top sides are involved
        //   resized if the right or bottom sides are involved
        // we don't care about the resizing, but when PixInsight moves a preview,
        // it ends up covering some chunks of the image already modified. Plus,
        // the selectedPoint assignment in Step 6 /can/ be negative, and the
        // subsequent .apply() actually moves the chunk [up|left]wards.
        //
        // So, let's take care of left and top. If they're negative, we'll make
        // the preview smaller using left_off and top_off
        var left_off = 0; if (bbox.left < 0) { left_off = -bbox.left; }
        var top_off  = 0; if (bbox.top  < 0) { top_off  = -bbox.top;  }

        // finally, if all the preview is going to be outside of the image, skip it!
        var small_width  = Math.floor (bbox.right  - bbox.left - left_off);
        var small_height = Math.floor (bbox.bottom - bbox.top  - top_off);
        if (small_width <= 0 || small_height <= 0) {
            continue;
        }

        var small_main_iw = new ImageWindow (
            small_width, small_height,
            main_i.numberOfChannels, main_i.bitsPerSample,
            main_i.sampleType == SampleType_Real,  // real sample type
            main_i.colorSpace != ColorSpace_Gray,  // colorspace gray
            'small_main'
        );
        small_main_iw.maskVisible = false;
        var small_main_v = small_main_iw.currentView;
        var small_main_i = small_main_v.image;
        //small_main_iw.show();

        preview = main_iw.createPreview (bbox, 'pre');
        small_main_v.beginProcess (UndoFlag_PixelData);
        small_main_i.assign (preview.image);
        small_main_v.endProcess();
        main_iw.deletePreview (preview);

        var small_mask_iw = new ImageWindow (
            Math.floor (bbox.right - bbox.left), Math.floor (bbox.bottom - bbox.top),
            1, 16,  // 1 channel, 16 bits
            false,  // real sample type
            false,  // colorspace gray
            'small_mask'
        );
        var small_mask_v = small_mask_iw.currentView;
        var small_mask_i = small_mask_v.image;
        //small_mask_iw.show();

        preview = mask_iw.createPreview (bbox, 'pre');
        small_mask_v.beginProcess (UndoFlag_PixelData);
        small_mask_i.assign (preview.image);
        small_mask_v.endProcess();
        mask_iw.deletePreview (preview);

        // STEP 4: use small b/w image as mask for the small main image
        small_main_iw.mask = small_mask_iw;

        // STEP 5: apply
        apply_proc (small_main_iw.currentView);

        // STEP 6: copy new portion to main image and destroy small images
        // if left_off or top_off are set, the respective sum will add up to zero
        main_i.selectedPoint = new Point (bbox.left + left_off, bbox.top + top_off);
        main_i.apply (small_main_i);
        with (small_main_iw) { removeMaskReferences(); undoAll(); purge(); close(); }
        small_main_iw = null;
        with (small_mask_iw) { removeMaskReferences(); undoAll(); purge(); close(); }
        small_mask_iw = null;

        //break;
    }
    //break;
}

// cleanup
g.end();
main_v.endProcess();
with (mask_iw) { undoAll(); purge(); close(); }
mask_iw = null;
gc();
--
 David Serrano