diff --git a/add_overlap.pro b/add_overlap.pro new file mode 100644 index 0000000000000000000000000000000000000000..695969b619f9b39e6e1181e3f58e8c2825e9cdbe --- /dev/null +++ b/add_overlap.pro @@ -0,0 +1,42 @@ +; $Id: add_overlap.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; ADD_OVERLAP +; +; PURPOSE: +; Find the overlap region of two 2D arrays, after ideally superposing the +; relative positions of a reference point, and add the overlap region of +; the second array to the overlap region of the first one. +; +; CATEGORY: +; Array manipulation. +; +; CALLING SEQUENCE: +; ADD_OVERLAP, Array1, Array2, R1, R2 +; +; INPUTS: +; Array1, Array2: Input arrays +; +; R1: 2-components vector of coordinates of reference point in Array1 +; +; R2: 2-components vector of coordinates of reference point in Array2 +; +; OUTPUTS: +; Array1: Input Array1 + overlap region of Array2 +; +; SIDE EFFECTS: +; Array1 is overwritten. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +PRO add_overlap, array1, array2, r1, r2 + + on_error, 2 + array_overlap, size52(array1, /DIM), size52(array2, /DIM), r1, r2, $ + lx1, ux1, ly1, uy1, lx2, ux2, ly2, uy2 + array1[lx1,ly1] = array1[lx1:ux1,ly1:uy1] + array2[lx2:ux2,ly2:uy2] + return +end diff --git a/add_subscript.pro b/add_subscript.pro new file mode 100644 index 0000000000000000000000000000000000000000..04349e04bef5a369ee66ec75c67c977f17dad661 --- /dev/null +++ b/add_subscript.pro @@ -0,0 +1,36 @@ +; $Id: add_subscript.pro, v 1.1 Mar 2012 e.d. $ +; +;+ +; NAME: +; ADD_SUBSCRIPT +; +; PURPOSE: +; Add new subscript to subscript vector. +; +; CATEGORY: +; STARFINDER auxiliary procedures. +; +; CALLING SEQUENCE: +; Result = ADD_SUBSCRIPT(Subscripts, S) +; +; INPUTS: +; Subscripts: 1D vector of subscripts. +; +; S: new subscripts to append to Subscripts +; +; OUTPUTS: +; Return appended vector of subscripts. +; If input vector Subscripts is not valid, return S. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, June 2001. +; 1) Created this file (E. D., March 2012). +;- + +FUNCTION add_subscript, subscripts, s + + on_error, 2 + if subscripts[0] lt 0 then $ + w = s else w = [subscripts, s] + return, w +end diff --git a/airy_pattern.pro b/airy_pattern.pro new file mode 100644 index 0000000000000000000000000000000000000000..215a1486edb9b879f4ec4e7e00d9acceb7bb5d36 --- /dev/null +++ b/airy_pattern.pro @@ -0,0 +1,52 @@ +; $Id: airy_pattern.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; AIRY_PATTERN +; +; PURPOSE: +; Compute Airy pattern. +; +; CATEGORY: +; Models. +; +; CALLING SEQUENCE: +; Result = AIRY_PATTERN(X_size, Y_size, X_center, Y_center, Sampling_factor) +; +; INPUTS: +; X_size, Y_size: X- and y_ size of output array +; +; X_center, Y_center: Coordinates of center, not necessarily integer +; +; OPTIONAL INPUTS: +; Sampling_factor: Ratio of actual sampling factor to critical sampling +; step for the optical system producing the Airy pattern. +; The default is Sampling_factor = 1, i.e. critical sampling +; +; OUTPUTS: +; Result: 2D array containing Airy pattern normalized to maximum = 1 +; +; PROCEDURE: +; Compute Airy pattern as defined in +; Born, Wolf, "Principles of Optics", Pergamon Press, 2nd revised edition. +; Suitably adjust sampling step. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +FUNCTION airy_pattern, x_size, y_size, x_center, y_center, sampling_factor + + if n_elements(sampling_factor) eq 0 then $ + sampling_factor = 1 ; critical sampling + scale = !pi / 2 + ; define 2D array of radial distances + r = radial_dist(x_size, y_size, x_center, y_center) + r = temporary(r) * scale * sampling_factor + ; compute diffraction pattern + w = where(r ne 0) + d = r & d[w] = 2 * (beselj(r, 1))[w] / r[w] + w = where(r eq 0, n) & if n ne 0 then d[w] = 1 + d = temporary(d)^2 + return, d +end \ No newline at end of file diff --git a/all_max.pro b/all_max.pro new file mode 100644 index 0000000000000000000000000000000000000000..74ec9c53eba4c2d84111e20ea79f937534f57937 --- /dev/null +++ b/all_max.pro @@ -0,0 +1,61 @@ +; $Id: all_max.pro, v 1.1 Sep 2001 e.d. $ +; +;+ +; NAME: +; ALL_MAX +; +; PURPOSE: +; Find relative maxima in a 2D array. +; A given pixel is considered a relative maximum if it is brighter +; than its 8-neighbors or 4-neighbors. +; +; CATEGORY: +; Signal processing. +; +; CALLING SEQUENCE: +; ALL_MAX, Array, X, Y, N +; +; INPUTS: +; Array: 2D array to be searched +; +; KEYWORD PARAMETERS: +; BOX: Size of sub-regions where the local maxima are defined. +; The default is 3, i.e. each returned peak is the relative +; maximum in a 3x3 sub-array. +; +; FOUR: Set this keyword to identify relative maxima as pixels +; brighter than their 4-neighbors. The default is to use +; 8-neighbors. +; +; OUTPUTS: +; X, Y: Coordinates of detected maxima +; +; N: Number of detected maxima +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +; 1) Added BOX keyword (Emiliano Diolaiti, September 2001). +;- + +PRO all_max, array, x, y, n, BOX = box, FOUR = four + + on_error, 2 + if n_elements(box) eq 0 then box = 3 + siz = size52(array, /DIM) & sx = siz[0] & sy = siz[1] + xedge = box/2 & yedge = box/2 + ext_array = extend_array(array, sx + 2*xedge, sy + 2*yedge) + m = make_array(sx + 2*xedge, sy + 2*yedge, /BYTE, VALUE = 1B) + for dx = -box/2, box/2 do for dy = -box/2, box/2 do begin + if keyword_set(four) then $ + check = abs(dx) ne abs(dy) else $ ; 4-neighbors + check = dx ne 0 or dy ne 0 ; 8-neighbors + if check then $ + m = temporary(m) and ext_array gt shift(ext_array, dx, dy) + endfor + w = where(m[xedge:xedge+sx-1,yedge:yedge+sy-1] eq 1, n) + if n ne 0 then subs_to_coord, w, sx, x, y + if n eq 1 then begin + x = x[0] & y = y[0] + endif + return +end diff --git a/angle.pro b/angle.pro new file mode 100644 index 0000000000000000000000000000000000000000..740c238dffc7588f729e1e8938965f2b71e5e85e --- /dev/null +++ b/angle.pro @@ -0,0 +1,49 @@ +; $Id: angle.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; ANGLE +; +; PURPOSE: +; Compute the position angles of a set of points on a plane with +; respect to the horizontal axis of a reference frame passing through +; a fixed origin. The angles are measured counter-clockwise in radians +; and belong to the interval [0, 2*pi[. +; The computations are performed in floating-point arithmethic. +; +; CATEGORY: +; Mathematics. +; +; CALLING SEQUENCE: +; Result = ANGLE(X0, Y0, X, Y) +; +; INPUTS: +; X0, Y0: Couple of scalars, representing coordinates of the origin +; +; X, Y: Coordinates of the points for which the position angle +; must be computed +; +; OUTPUTS: +; Result: Array of position angles, with the same size as the input +; arrays X and Y. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +FUNCTION angle, x0, y0, x, y + + on_error, 2 + a = float(x) - x & dx = x - float(x0[0]) & dy = y - float(y0[0]) + w = where(dx eq 0 and dy ne 0, n) + if n ne 0 then a[w] = !pi/2 + w = where(dx ne 0, n) + if n ne 0 then a[w] = atan(dy[w] / dx[w]) + w = ((dx lt 0) or ((dx eq 0) and (dy lt 0))) and 1B + a = a + w * !pi + w = where(a lt 0, n) + if n ne 0 then a[w] = a[w] + 2*!pi + w = where(a eq 2*!pi, n) + if n ne 0 then a[w] = 0 + return, a +end diff --git a/array_overlap.pro b/array_overlap.pro new file mode 100644 index 0000000000000000000000000000000000000000..1ac97fe0124ab00e4ee24b9c896731e057c7728d --- /dev/null +++ b/array_overlap.pro @@ -0,0 +1,60 @@ +; $Id: array_overlap.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; ARRAY_OVERLAP +; +; PURPOSE: +; Find bounds of overlap region of two 2D arrays, by ideally +; matching the coordinates of a reference point. +; +; CATEGORY: +; Array manipulation. +; +; CALLING SEQUENCE: +; ARRAY_OVERLAP, Size1, Size2, R1, R2, $ +; Lx1, Ux1, Ly1, Uy1, Lx2, Ux2, Ly2, Uy2 +; +; INPUTS: +; Size1: 2-components vector, size of array 1, as returned by +; size52(array1, /DIM) +; +; Size2: 2-components vector, size of array 2 +; +; R1: 2-components vector, coordinates of reference pixel in array 1 +; +; R2: 2-components vector, coordinates of reference pixel in array 2 +; +; OUTPUTS: +; Lx1, Ux1, Ly1, Uy1: Lower and Upper X- and Y- bounds of intersection +; in array 1 +; +; Lx2, Ux2, Ly2, Uy2: Lower and Upper X- and Y- bounds of intersection +; in array 2 +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + + + +; INT_BOUNDS: auxiliary procedure. + +PRO int_bounds, p1, p2, size1, size2, l, u + + l = (p1 - p2) > 0 < (size1 - 1) + u = (p1 + size2 - p2 - 1) > 0 < (size1 - 1) + return +end + + +PRO array_overlap, size1, size2, r1, r2, $ + lx1, ux1, ly1, uy1, lx2, ux2, ly2, uy2 + + on_error, 2 + int_bounds, r1[0], r2[0], size1[0], size2[0], lx1, ux1 + int_bounds, r1[1], r2[1], size1[1], size2[1], ly1, uy1 + int_bounds, r2[0], r1[0], size2[0], size1[0], lx2, ux2 + int_bounds, r2[1], r1[1], size2[1], size1[1], ly2, uy2 + return +end diff --git a/array_partition.pro b/array_partition.pro new file mode 100644 index 0000000000000000000000000000000000000000..2cb69fde95a6b528f2b006b097837b3ed77c7935 --- /dev/null +++ b/array_partition.pro @@ -0,0 +1,97 @@ +; $Id: array_partition.pro, v 1.2 Mar 2012 e.d. $ +; +;+ +; NAME: +; ARRAY_PARTITION +; +; PURPOSE: +; Define bounds to partition an array into a pre-fixed number of +; parts along one dimension. +; +; CATEGORY: +; Array manipulation. +; +; CALLING SEQUENCE: +; ARRAY_PARTITION, Siz, N, L, U, S +; +; INPUTS: +; Siz: (Long) integer, representing the array size along the +; dimension of interest. +; +; N: Number of parts into which the dimension must be partitioned. +; The size of the sub-domains may be defined using the keyword STEP. +; In this case, the input N may be left undefined; on output, it is +; set to the actual number of sub-domains. +; +; KEYWORD INPUTS: +; STEP: Set this keyword to the integer value of the desired size of each +; sub-domain. If STEP is set, the parameter N is set on output to +; the actual number of sub-domains. +; +; OVERLAP: Set this keyword to overlap sub-domains (upper boundary of +; a sub-domain coincides with lower boundary of next sub-domain). +; +; OUTPUTS: +; L, U: N-element long integer vectors, defined as follows: +; [L[i], U[i]] are the bounds of the i-th partition, i = 0, N - 1. +; Notice that L[0] = 0, U[N - 1] = Siz - 1. +; The following relation holds: +; L[i + 1] = U[i] + 1, if the keyword OVERLAP is not set +; L[i + 1] = U[i], if the keyword OVERLAP is set +; +; S: N-element long integer vector, with the size of each sub-domain. +; +; EXAMPLE: +; Given a 512x512 array, partition it into 2x2 sub-regions. Define the +; bounds of the sub-arrays along one dimension: +; +; IDL> ARRAY_PARTITION, 512, 2, L, U, S +; IDL> PRINT, L, U +; 0 256 +; 255 511 +; IDL> ARRAY_PARTITION, 512, 2, L, U, S, /OVERLAP +; IDL> PRINT, L, U +; 0 256 +; 256 512 +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, December 1999. +; 1) Added keyword STEP (E.D., July 2007). +; 2) Added keyword OVERLAP (E.D., March 2012). +;- + +PRO array_partition, siz, n, l, u, s, STEP = step, OVERLAP = overlap + + on_error, 2 + if keyword_set(step) then begin + n = siz / step + if siz mod step ne 0 then n = n + 1 + endif else $ + step = siz / n + if keyword_set(overlap) then begin + l = lindgen(n + 1) * step + u = l[1: *] + u[n - 1] = siz + l = l[0: n - 1] + endif else begin + l = lindgen(n) * step + u = l + step - 1 + u[n - 1] = siz - 1 + endelse + s = u - l + 1 + return +end + + +; on_error, 2 +; if keyword_set(step) then begin +; n = siz / step +; if siz mod step ne 0 then n = n + 1 +; endif else $ +; step = siz / n +; l = lindgen(n) * step +; u = l + step - 1 +; u[n - 1] = siz - 1 +; s = u - l + 1 +; return +;end diff --git a/b_splines.pro b/b_splines.pro new file mode 100644 index 0000000000000000000000000000000000000000..356709dd350ba665127b68abf38fe03f2ad94295 --- /dev/null +++ b/b_splines.pro @@ -0,0 +1,102 @@ +; $Id: b_splines.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; B_SPLINES +; +; PURPOSE: +; Compute the 1-D observation matrix to determine the coefficients +; of an interpolating spline in the B-splines representation. +; +; CATEGORY: +; Mathematics. Interpolation. +; +; CALLING SEQUENCE: +; B_SPLINES, Points, Knots, Degree, A, Npt +; +; INPUTS: +; Points: 1D vector of observation points +; +; Knots: Knots of the spline function +; +; Degree: Degree of the spline (integer, odd) +; +; KEYWORD PARAMETERS: +; BOUNDS: Option used by SPLINE_INTERP (see the function SPLINE_INTERP +; in the file 'splie_interp.pro') to compute the observation matrix. +; +; FULL: The observation array has a band structure of width (Degree+1). +; Normally only the nonzero entries are returned. Set the keyword +; FULL to return the full observation matrix. +; +; OUTPUTS: +; A: Observation matrix of size (Degree+1)*N, where N is the number +; of observation points. If the keyword FULL is set, A is returned +; as a N*N array. +; +; Npt: 1D Vector of subscripts defining the relative position of +; the observation points with respect to the knots of the spline +; +; PROCEDURE: +; Apply the procedures described in +; Paul Dierckx, "Curve and surface fitting with splines", +; Clarendon Press, Oxford (1995) +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +; Adapted in IDL from the sofware FITPACK, written in FORTRAN by P.Dierckx. +; Complete references may be found in +; Paul Dierckx, "Curve and surface fitting with splines", +; Clarendon Press, Oxford (1995) +;- + + +;;; Auxiliary function. + +; SPL_BSPL: given a set of knots k and a point x, with k[s] <= x < k[s+1], +; compute the (d + 1) non-zero B-splines of degree d at x, using +; the recurrence relations of de Boor and Cox. + +FUNCTION spl_bspl, k, d, x, s + + on_error, 2 + v = fltarr(d + 1) & aux = fltarr(d) + v[0] = 1 + for j = 1, d do begin + aux[0:j-1] = v[0:j-1] & v[0] = 0 + for i = 1, j do begin + si = s + i & sj = si - j + f = aux[i-1] / (k[si] - k[sj]) + v[i-1] = v[i-1] + f * (k[si] - x) + v[i] = f * (x - k[sj]) + endfor + endfor + return, v +end + +;;; The main routine. + +PRO b_splines, points, knots, degree, a, npt, $ + BOUNDS = chk_bounds, FULL = full + + on_error, 2 + npoints = n_elements(points) & nk = n_elements(knots) + if keyword_set(full) then $ + a = fltarr(nk - degree - 1, npoints) else $ + a = fltarr(degree + 1, npoints) + npt = lonarr(npoints) + l = degree & r = l + 1 & num = 0 + for n = 0L, npoints - 1 do begin + point = points[n] + if keyword_set(chk_bounds) then $ + point = (point > knots[degree]) < knots[nk-degree-1] + while point ge knots[r] and l lt nk - degree - 2 do begin + l = r & r = l + 1 & num = num + 1 + endwhile + npt[n] = num + ; knots[degree + npt[n]] <= points[n] < knots[degree + npt[n] + 1] + b = spl_bspl(knots, degree, point, l) + if keyword_set(full) then a[num,n] = b else a[*,n] = b + endfor + return +end diff --git a/background.fits b/background.fits new file mode 100644 index 0000000000000000000000000000000000000000..7c9b6821de69ee0a29b4782314f2c73bfc4734f9 Binary files /dev/null and b/background.fits differ diff --git a/binary_array.pro b/binary_array.pro new file mode 100644 index 0000000000000000000000000000000000000000..0741ac12904a123731f23e1bae957cc2d14479a7 --- /dev/null +++ b/binary_array.pro @@ -0,0 +1,36 @@ +; $Id: binary_array.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; BINARY_ARRAY +; +; PURPOSE: +; Transform an array to binary by thresholding. +; +; CATEGORY: +; Signal processing. +; +; CALLING SEQUENCE: +; Result = BINARY_ARRAY(Array, Threshold) +; +; INPUTS: +; Array: Input array to be thresholded +; +; Threshold: Scalar value or 2D array with the same size as Array. +; +; OUTPUTS: +; Result: Byte array, defined as follows: +; Result[j,i] = 1B, if Array[j,i] >= Threshold +; = 0B, otherwise +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +FUNCTION binary_array, array, threshold + + on_error, 2 + siz = size52(array, /DIM) & b = bytarr(siz[0], siz[1]) + w = where(array ge threshold, n) & if n ne 0 then b[w] = 1 + return, b +end diff --git a/center_array.pro b/center_array.pro new file mode 100644 index 0000000000000000000000000000000000000000..92c7e90c4f8f08131fe85d0027ceb82816d9512b --- /dev/null +++ b/center_array.pro @@ -0,0 +1,46 @@ +; $Id: center_array.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; CENTER_ARRAY +; +; PURPOSE: +; Shift a 2D array in order to put its maximum at the specified position. +; +; CATEGORY: +; Array manipulation. +; +; CALLING SEQUENCE: +; Result = CENTER_ARRAY(Array, X, Y) +; +; INPUTS: +; Array: 2D array to shift +; +; X, Y: X- and y- final coordinates of the Array maximum +; +; KEYWORD PARAMETERS: +; NO_EXTEND: Set this keyword to a nonzero value to indicate that +; the Array must be extended before being shifted. The original +; size is restored after the shift. Use this keyword to prevent +; circular shift effects. +; +; OUTPUTS: +; Result: Shifted array +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +FUNCTION center_array, image, x, y, NO_EXTEND = no_extend + + on_error, 2 + s = size52(image, /DIM) + if n_elements(x) * n_elements(y) eq 0 then begin + x = s[0]/2 & y = s[1]/2 + endif + m = get_max(image) & xs = x - m[0] & ys = y - m[1] + if xs eq 0 and ys eq 0 then return, image + if keyword_set(no_extend) then $ + return, shift(image, xs, ys) else $ + return, extend_shift(image, xs, ys) +end diff --git a/centroid.pro b/centroid.pro new file mode 100644 index 0000000000000000000000000000000000000000..d5f20f376255d308e9ec38207d6715a55f7f7b02 --- /dev/null +++ b/centroid.pro @@ -0,0 +1,41 @@ +; $Id: centroid.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; CENTROID +; +; PURPOSE: +; Compute the centroid of a 2D array, defined as the "center of mass" +; of a 2D intensity distribution. +; +; CATEGORY: +; Mathematics. +; +; CALLING SEQUENCE: +; Result = CENTROID(Array) +; +; INPUTS: +; Array: 2D array +; +; OUTPUTS: +; Result: 2-components floating point vector, containing the +; coordinates of the centroid +; +; RESTRICTIONS: +; Apply only to 2D arrays. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +FUNCTION centroid, array + + on_error, 2 + s = size(array, /DIM) & sx = s[0] & sy = s[1] + x = findgen(sx) & x_one = make_array(sx, VALUE = 1) + y = findgen(sy) & y_one = make_array(sy, VALUE = 1) + tot_array = total(array) + xc = total(array * (x # y_one)) / tot_array + yc = total(array * (x_one # y)) / tot_array + return, [xc, yc] +end diff --git a/centroider.pro b/centroider.pro new file mode 100644 index 0000000000000000000000000000000000000000..3b5a313c920e44a84ebb44ed193048e2dbcf426b --- /dev/null +++ b/centroider.pro @@ -0,0 +1,107 @@ +; $Id: centroider.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; CENTROIDER +; +; PURPOSE: +; Sub-pixel centering of an image by iterative fractional shift. +; +; CATEGORY: +; Signal processing. Interpolation. +; +; CALLING SEQUENCE: +; Result = CENTROIDER(Image) +; +; INPUTS: +; Image: 2D array to be centered +; +; KEYWORD PARAMETERS: +; XC, YC: Reference pixel for centroid computation. The default is the +; Image maximum. +; +; CENTROID_BOX: Width of box centered at the reference pixel used to +; compute the Image centroid. The default is equal to the (rounded) +; FWHM of the peak at the reference position. A minimum value of +; 3 pixels is fixed. +; +; CENTROID_TOL: This keyword defines the stopping condition for the +; iterative algorithm, i.e. the maximum allowed off-centering of +; the Image centroid. The default is CENTROID_TOL = 0.05 pixels. +; +; CENTROID_IT: Use this keyword to fix the maximum number of +; iterations. The default is 20, even though centering is achieved +; in fewer iterations (tipically 5). +; +; INTERP_TYPE: Set this keyword to a string identifying one of the +; interpolation techiques supported by IMAGE_SHIFT. For more details +; see the function IMAGE_SHIFT in the file 'image_shift.pro'. +; +; OUTPUTS: +; Result: Centered array +; +; OPTIONAL OUTPUTS: +; XSHIFT, YSHIFT: Use these output keywords to retrieve the total shifts +; performed to center the Image, starting from the reference position. +; The quantities XC + X_SHIFT and YC + Y_SHIFT represent and estimate +; of the true centroid location in the original Image +; +; RESTRICTIONS: +; The Image is shifted by interpolation techniques, which are suited to +; well sampled data. This method should not applied to undersampled +; images. +; +; PROCEDURE: +; Compute first the offset of the image centroid and shift the array in +; order to cancel the off-centering. The operation is iterated until the +; offset is smaller than a pre-fixed tolerance. +; The centroid is computed on a small box centered at a reference position, +; which generally coincides with the image maximum. +; The image shift is performed by interpolation. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +; Adapted from an algorithm described in: +; Christou J.C., Bonaccini D., "Technical Report ESO VLT", +; Doc. No. GEN-TRE-ESO-11620-1261 (1996) +;- + +FUNCTION centroider, image, XC = xc, YC = yc, CENTROID_BOX = box, $ + CENTROID_TOL = tol, CENTROID_IT = maxit, $ + XSHIFT = xs, YSHIFT = ys, _EXTRA = extra + + on_error, 2 + ; reference pixel + if n_elements(xc) * n_elements(yc) eq 0 then begin + m = get_max(image) & x = m[0] & y = m[1] + endif else begin + x = xc & y = yc + endelse + x = round(x) & y = round(y) + ; box size to compute centroid + if n_elements(box) eq 0 then $ + b = fwhm(image, X = x, Y = y) else b = box + b = round(b) + if n_elements(b) eq 1 then b = [b, b] + b = b < size52(image, /DIM) & b = b + 1 - b mod 2 & b = b > 3 + ; other parameters + if n_elements(tol) eq 0 then tol = 0.05 + if n_elements(maxit) eq 0 then maxit = 20 + imag = image & it = 0 & xs = 0. & ys = 0. + ; iteration + repeat begin + it = it + 1 + ; compute centroid + c = centroid(sub_array(imag, b[0], b[1], $ + REFERENCE = [x, y], LX = lx, LY = ly)) + dx = x - lx - c[0] & dy = y - ly - c[1] + ; shift image to center centroid + xs = xs + dx & ys = ys + dy + imag = image_shift(imag, xs, ys, _EXTRA = extra, shift_data) + convergence = abs(dx) lt tol and abs(dy) lt tol + endrep until convergence or it eq maxit + if it eq maxit and not convergence then begin + imag = image & xs = 0. & ys = 0. + endif + return, imag +end diff --git a/check_border.pro b/check_border.pro new file mode 100644 index 0000000000000000000000000000000000000000..31c0cc44af9aa303f47de77ca21e71e2462eaf3a --- /dev/null +++ b/check_border.pro @@ -0,0 +1,59 @@ +; $Id: check_border.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; CHECK_BORDER +; +; PURPOSE: +; Given two 2D arrays, ideally superpose the positions of a reference +; point and check if the first array is larger than the second by a +; pre-fixed edge of pixels. Resize the arrays if the condition is +; not fulfilled. +; +; CATEGORY: +; Array manipulation. +; +; CALLING SEQUENCE: +; CHECK_BORDER, Array1, Array2, Pix1, Pix2, Edge, $ +; Array1_out, Array2_out, Pix1_out, Pix2_out +; +; INPUTS: +; Array1, Array2: Input 2D arrays +; +; Pix1, Pix2: 2-components vectors, coordinates of reference point in +; Array1 and Array2 respectively +; +; Edge: When the coordinates of the reference point have been matched, +; the size of Array1 must be equal to the size of Array2 plus an +; additional border, whose width is specified by Edge +; +; OUTPUTS: +; Array1_out, Array2_out: Output arrays +; +; Pix1_out, Pix2_out: Positions of reference points in output arrays +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +PRO check_border, array1, array2, pix1, pix2, edge, $ + array1_out, array2_out, pix1_out, pix2_out + + on_error, 2 + s1 = size52(array1, /DIM) & s2 = size52(array2, /DIM) + l1 = [0, 0] & u1 = s1 - 1 & l2 = [0, 0] & u2 = s2 - 1 + ; resize lower bounds + d = pix1 - (pix2 + edge) + l1 = l1 + (d > 0) & pix1_out = pix1 - (d > 0) + l2 = l2 - (d < 0) & pix2_out = pix2 + (d < 0) + ; resize upper bounds + d = (s1 - pix1) - (s2 - pix2 + edge) + u1 = u1 - (d > 0) & u2 = u2 + (d < 0) + ; resize arrays + array1_out = array1 & array2_out = array2 + if min(u1 - l1) ge 0 and min(u2 - l2) ge 0 then begin + array1_out = array1_out[l1[0]:u1[0],l1[1]:u1[1]] + array2_out = array2_out[l2[0]:u2[0],l2[1]:u2[1]] + endif + return +end diff --git a/circ_mask.pro b/circ_mask.pro new file mode 100644 index 0000000000000000000000000000000000000000..57c43efd415b81593d443c13438bbe3f31d1a3db --- /dev/null +++ b/circ_mask.pro @@ -0,0 +1,51 @@ +; $Id: circ_mask.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; CIRC_MASK +; +; PURPOSE: +; Apply a circular mask to a 2D array, setting to a pre-fixed value all +; the pixels whose distance from a reference position is either greater +; or smaller equal than a specified threshold. +; +; CATEGORY: +; Signal processing. +; +; CALLING SEQUENCE: +; Result = CIRC_MASK(Array, X0, Y0, R0) +; +; INPUTS: +; Array: 2D array to mask +; +; X0, Y0: Coordinates of center of circular mask +; +; R0: Radius of circular mask in pixels +; +; KEYWORD PARAMETERS: +; INNER: Set this keyword to a nonzero value to mask the pixels within +; a distance R0 from (X0, Y0): the boundary is included (i.e. masked). +; The default is to mask the pixels outside that distance (in this +; case the boundary is excluded, i.e. not masked). +; +; VALUE: Use this value to replace masked pixels. The default is VALUE = 0 +; +; OUTPUTS: +; Result: Array with region defined by circular mask set to the value +; defined by the keyword VALUE. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +FUNCTION circ_mask, array, x0, y0, r0, INNER = inner, VALUE = v + + on_error, 2 + siz = size52(array, /DIM) + r = radial_dist(siz[0], siz[1], x0, y0) + if keyword_set(inner) then $ + w = where(r le r0, npix) else w = where(r gt r0, npix) + if n_elements(v) eq 0 then v = 0 + circ = array & if npix ne 0 then circ[w] = v + return, circ +end diff --git a/click_on_max.pro b/click_on_max.pro new file mode 100644 index 0000000000000000000000000000000000000000..1bd8765c3eab6a11acc5ee7175f243a790ef7efa --- /dev/null +++ b/click_on_max.pro @@ -0,0 +1,119 @@ +; $Id: click_on_max.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; CLICK_ON_MAX +; +; PURPOSE: +; Interactive selection of local maxima in an image by mouse click. +; +; CATEGORY: +; Data visualization. +; +; CALLING SEQUENCE: +; CLICK_ON_MAX, Image, X, Y +; +; INPUTS: +; Image: 2D data array containing local maxima to be selected +; +; KEYWORD PARAMETERS: +; MARK: Set this keyword to a nonzero value to mark selected objects +; as soon as they are clicked on +; +; N_SELECT: Set this keyword to an integer (positive) number to specify +; the number of maxima to select. If this keyword is not used, the +; selection is interrupted by a right-button click. +; +; BOXSIZE: Specify the tolerance on the maximum position in pixel +; units: after each click, the maximum within a box having this +; size and centered at the click location is selected. +; The default is BOXSIZE = 11. +; +; UPPER: Set this keyword to a scalar value or a 2D array with the same +; size as the Image to specify the threshold above which the position +; of a click must be retained as it is, without searching for the +; nearest local maximum. This odd requirement is related to the +; selection of stars in a stellar field. +; +; DARK: Set this keyword to a nonzero value to have dark marks for the +; selected points. This options is available only if the keyword MARK +; is set (as obvious). For more details, see the procedure CROSSES +; in the file 'crosses.pro'. +; +; SYMSIZE: Specify the size of the symbol used by the routine CROSSES +; to mark the selected points. +; +; SILENT: Set this keyword to avoid printing a brief instruction message +; +; OUTPUTS: +; X, Y: x- and y- coordinates (in pixel units, data coordinates) of +; the selected local maxima +; +; OPTIONAL OUTPUTS: +; Mark each selected maximum if the keyword MARK is set. +; +; RESTRICTIONS: +; This procedure assumes that the image containing the objects of +; interest is already displayed in the currently active window. +; It is also assumed that the window is completely filled by the image. +; +; PROCEDURE: +; Select peaks by clicking with the left button of the mouse. +; The selection ends when the number of maxima specified by the input +; keyword N_SELECT is reached or when the right button of the mouse +; is pushed. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +; 2) Removed call to obsolete routine APPEND_ELEMENTS +; (Emiliano Diolaiti, June 2001). +;- + +PRO click_on_max, image, MARK = mark, N_SELECT = n_sel, $ + BOXSIZE = boxsize, UPPER = threshold, SILENT = silent, $ + _EXTRA = extra, x, y + + on_error, 2 + s = float(size52(image, /DIM)) + if n_elements(boxsize) eq 0 then boxsize = 11 + if n_elements(threshold) eq 0 then threshold = max(image) + 1 + fixed = n_elements(n_sel) ne 0 + if fixed then n_select = round(n_sel > 0) + if not keyword_set(silent) then begin + print, 'Select by clicking with the left button of your mouse...' + if not fixed then print, 'Push right button to exit' + endif + !MOUSE.button = 1 & n_click = 0 + cursor, x_click, y_click, /NORMAL, /DOWN + while !MOUSE.button eq 1 do begin + x_click = round(x_click * s[0]) & y_click = round(y_click * s[1]) + if boxsize ge 2 and image[x_click, y_click] lt threshold then $ + m = get_max(sub_array(image, boxsize, REF = [x_click, y_click], $ + LX = lx, LY = ly)) $ + else begin + m = [0, 0] & lx = x_click & ly = y_click + endelse + x_click = x_click + m[0] - (x_click - lx) + y_click = y_click + m[1] - (y_click - ly) + if n_elements(x_saved) eq 0 then begin + x_saved = x_click + y_saved = y_click + endif else begin + x_saved = [x_saved, x_click] + y_saved = [y_saved, y_click] + endelse + if keyword_set(mark) then $ + crosses, tvrd(), x_click/(s[0] - 1)*!D.x_size, y_click/(s[1] - 1)*!D.y_size, $ + /EXISTING, _EXTRA = extra, /DEVICE + if fixed then begin + n_click = n_click + 1 + if n_click eq n_select then !MOUSE.button = 4 + endif + if !MOUSE.button eq 1 then cursor, x_click, y_click, /NORMAL, /DOWN + endwhile + if n_elements(x_saved) ne 0 then begin + x = x_saved & y = y_saved + endif + remove_coincident, x, y, x, y + return +end diff --git a/compare_lists.pro b/compare_lists.pro new file mode 100644 index 0000000000000000000000000000000000000000..67f6f0b074f6b534af612f8a4132df030c8816db --- /dev/null +++ b/compare_lists.pro @@ -0,0 +1,114 @@ +; $Id: compare_lists, v 1.1 Feb 2000 e.d. $ +; +;+ +; NAME: +; COMPARE_LISTS +; +; PURPOSE: +; Given two sets of points on a plane, compare and match their +; coordinates to find coincidences. +; +; CATEGORY: +; Miscellaneous. +; +; CALLING SEQUENCE: +; COMPARE_LISTS, X1, Y1, X2, Y2, X1c, Y1c, X2c, Y2c +; +; INPUTS: +; X1, Y1: Coordinates of first set of points +; +; X2, Y2: Coordinates of second set of points +; +; KEYWORD PARAMETERS: +; MAX_DISTANCE: Two points in set 1 and 2 are considered coincident if +; their reciprocal distance is smaller than a pre-fixed threshold. +; Use the MAX_DISTANCE threshold to fix a threshold. +; If MAX_DISTANCE is undefined, any distance is considered acceptable. +; In this case, the COMPARE_LISTS procedure just sorts the second +; list according to the ordering of the first one. +; +; OUTPUTS: +; X1c, Y1c: Coordinates of common points in set 1 +; +; X2c, Y2c: Coordinates of common points in set 2 +; +; OPTIONAL OUTPUTS: +; X_in1, Y_in1: Coordinates of points in set 1 with no +; counterpart in set 2. +; +; X_in2, Y_in2: Coordinates of points in set 2 with no +; counterpart in set 1. +; +; SUBSCRIPTS_1: Use this output keyword to retrieve the subscripts of +; the common points in set 1. In other words, the outputs X1c is +; equal to the quantity X1[Subscripts1], where Subscripts1 is the +; output value of the keyword SUBSCRIPTS_1. +; +; SUBSCRIPTS_2: Use this output keyword to retrieve the subscripts of +; the common points in set 2. In other words, the outputs X2c is +; equal to the quantity X2[Subscripts2], where Subscripts2 is the +; output value of the keyword SUBSCRIPTS_2. +; +; SUB1, SUB2: Use these output keywords to retrieve the subscripts of +; the elements belonging only to set 1 and 2 respectively. +; +; RESTRICTIONS: +; The COMPARE_LISTS procedure assumes the two sets of coordinates are +; referred to the same reference frame. If there is an offset or a rotation +; between the two sets, use MATCH_COORD before (see 'match_coord.pro'). +; +; PROCEDURE: +; Find coincidences between points in sets 1 and 2 by comparing their +; positions. For each point in the first set, the second set is searched +; for the still unexamined point nearest to the point under examination. +; If the distance between the two points is smaller than the pre-fixed +; threshold, a new coincidence is found and recorded. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +; Updates: +; 1) Fixed bug on output keywords SUBSCRIPTS_1 and SUBSCRIPTS_2 +; (Emiliano Diolaiti, January 2000). +; 2) Modified output keywords (Emiliano Diolaiti, February 2000). +;- + +PRO compare_lists, x1, y1, x2, y2, x1c, y1c, x2c, y2c, $ + MAX_DISTANCE = max_distance, $ + SUBSCRIPTS_1 = subc1, SUBSCRIPTS_2 = subc2, $ + SUB1 = sub1, SUB2 = sub2 + + on_error, 2 + any_dist = n_elements(max_distance) eq 0 and 1B + n1 = n_elements(x1) & n2 = n_elements(x2) & nc = min([n1, n2]) + x1c = make_array(nc, TYPE = size52(x1, /TYPE)) + y1c = make_array(nc, TYPE = size52(y1, /TYPE)) + x2c = make_array(nc, TYPE = size52(x2, /TYPE)) + y2c = make_array(nc, TYPE = size52(y2, /TYPE)) + marked1 = bytarr(n1) & marked2 = bytarr(n2) + k = 0L + for n = 0L, n1 - 1 do begin + u = where(marked2 eq 0, n_unmarked) + if n_unmarked ne 0 then begin + d = min(distance(x1[n], y1[n], x2[u], y2[u]), w) + accept_it = any_dist + if not accept_it then accept_it = d le max_distance + if accept_it then begin + x1c[k] = x1[n] & y1c[k] = y1[n] + x2c[k] = x2[u[w]] & y2c[k] = y2[u[w]] + if k eq 0 then begin + subc1 = n & subc2 = u[w] + endif else begin + subc1 = [subc1, n] & subc2 = [subc2, u[w]] + endelse + marked1[n] = 1B & marked2[u[w]] = 1B + k = k + 1 + endif + endif + endfor + if k ne 0 then begin + x1c = x1c[0:k-1] & y1c = y1c[0:k-1] + x2c = x2c[0:k-1] & y2c = y2c[0:k-1] + endif + sub1 = where(marked1 eq 0B) & sub2 = where(marked2 eq 0B) + return +end diff --git a/convergence.pro b/convergence.pro new file mode 100644 index 0000000000000000000000000000000000000000..f132a07a78c646131248a57eb92c9e44b8693e9c --- /dev/null +++ b/convergence.pro @@ -0,0 +1,49 @@ +; $Id: convergence.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; CONVERGENCE +; +; PURPOSE: +; Check convergence condition between two IDL variables. +; +; CATEGORY: +; Mathematics. +; +; CALLING SEQUENCE: +; Result = CONVERGENCE(Var1, Var2, Tolerance) +; +; INPUTS: +; Var1: First IDL variable +; +; Var2: Second IDL variable +; +; Tolerance: Tolerance for convergence check +; +; KEYWORD PARAMETERS: +; ABSOLUTE: Set this keyword to a nonzero value to check the absolute +; error convergence between Var1 and Var2. The default is to check +; the relative error convergence +; +; OUTPUTS: +; Return the byte value 1B if the convergence condition is fulfilled +; and 0B otherwise. +; +; RESTRICTIONS: +; The relative error between a variable set to zero and a nonzero +; variable is 1 (100%) by definition. In this case the convergence +; condition will never be fulfilled, unless the relative Tolerance +; is > 1, which is rather unlikely. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +FUNCTION convergence, var1, var2, tolerance, ABSOLUTE = absolute + + on_error, 2 + if keyword_set(absolute) then $ + error = abs(var2 - var1) else $ + error = abs(relative_error(var1, var2)) + return, max(error) lt tolerance and 1B +end diff --git a/coord_to_subs.pro b/coord_to_subs.pro new file mode 100644 index 0000000000000000000000000000000000000000..e2f690dc3abf0f4e5cbd57d8e996985c63b83fee --- /dev/null +++ b/coord_to_subs.pro @@ -0,0 +1,36 @@ +; $Id: coord_to_subs.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; COORD_TO_SUBS +; +; PURPOSE: +; Convert pixel coordinates in a 2D array to array subscripts, +; to access the array in memory address order. +; +; CATEGORY: +; Array manipulation. +; +; CALLING SEQUENCE: +; Result = COORD_TO_SUBS(X, Y, N_columns) +; +; INPUTS: +; X, Y: Coordinates of pixels +; +; N_columns: Number of columns in the 2D array +; +; OUTPUTS: +; Result: Long integer vector of array subscripts +; +; RESTRICTIONS: +; Apply only to 2D arrays. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +FUNCTION coord_to_subs, x, y, n_columns + + on_error, 2 + return, round(y) * n_columns + round(x) +end \ No newline at end of file diff --git a/correlate_max.pro b/correlate_max.pro new file mode 100644 index 0000000000000000000000000000000000000000..13ff16f9a824affaa3fc4634979860e627cd957c --- /dev/null +++ b/correlate_max.pro @@ -0,0 +1,128 @@ +; $Id: correlate_max.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; CORRELATE_MAX +; +; PURPOSE: +; Given an object, a reference template and an estimate of the object's +; center, obtain a better estimate of the object's position by maximizing +; its correlation with the template. +; +; CATEGORY: +; Image statistics. +; +; CALLING SEQUENCE: +; CORRELATE_MAX, Image, Template, X_i, Y_i, Search_box, $ +; Max_correl, X_opt, Y_opt +; +; INPUTS: +; Image: 2D array containing the object to be correlated +; +; Template: reference template to compute the correlation +; +; X_i, Y_i: x- and y- coordinates of the object's presumed center +; +; Search_box: width of box centered at (X-i, Y_i) inside which the template +; must be ideally moved to find the best match with the object +; +; KEYWORD PARAMETERS: +; XT, YT: reference pixel in Template, i.e. pixel to be ideally superposed +; to the presumed object's center. The default is the central pixel +; of the Template array +; +; X_BAD, Y_BAD: x- and y- coordinates of bad pixels, i.e. unreliable data +; points to be excluded from the computation. Default: no bad pixels +; +; TEMPLATES: stack of sub-pixel-shifted templates, used to maximize the +; correlation with sub-pixel accuracy positioning +; +; DX, DY: 1D arrays of fractional shifts corresponding to the shifted +; templates in the Templates stack +; +; OUTPUTS: +; Max_correl: maximum correlation coefficient +; +; X_opt, Y_opt: x- and y- coordinates of the object position's +; guess yielding the largest correlation coefficient +; +; RESTRICTIONS: +; 1) The correlation between the object and the template is computed +; on the overlap area of the two arrays, which is found after ideally +; superposing their reference pixels. The template must be 'moved' +; within the range specified by the input parameter Search_box in order +; to optimize the correlation coefficient. To ensure that the area of +; the overlapping region be the same for all possible shifts, the Image +; array must be larger than the Template array, by an amount depending +; on the width of the search box. The CORRELATE_MAX procedure doesn't +; check this condition! If for some reason the overlapping region is +; less than 3 x 3 pixels in size, the correlation is set to 0. +; 2) Sub-pixel positioning is performed only if all the input keywords +; TEMPLATES, DX and DY are defined. These parameters may be defined by +; means of the function SHIFT_TEMPLATES in the file 'shift_templates.pro' +; +; PROCEDURE: +; Find the intersection between Template and Image after superposing +; their reference pixels and compute the correlation coefficient by +; means of CORRELATION_COEFF. The operation is performed for all possible +; shifts within the range specified by the parameter Search_box; the +; position yielding the largest match is taken as the best estimate of +; the object's center. +; The shift + match + correlate procedure may then be repeated for sub- +; pixel shifts around the optimal position previously found, in order +; to determine a better estimate of the object's center. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +PRO correlate_max, image, template, x_i, y_i, search_box, $ + XT = x_t, YT = y_t, X_BAD = xb, Y_BAD = yb, $ + TEMPLATES = templates, DX = dx, DY = dy, $ + max_correl, x_opt, y_opt + + on_error, 2 + ; optimize correlation applying integer shifts, + ; within the range specified by search_box + lo_xy = [0, 0] & up_xy = size52(image, /DIM) - 1 & min_size = 3 + h = round(search_box > 1) / 2 & nshift = (2*h + 1)^2 + x = lonarr(nshift) & y = lonarr(nshift) & correl = fltarr(nshift) + xi = round(x_i) & yi = round(y_i) + if n_elements(x_t) eq 0 or n_elements(y_t) eq 0 then begin + s = size52(template, /DIM) & xt = s[0]/2 & yt = s[1]/2 + endif else begin + xt = round(x_t) & yt = round(y_t) + endelse + n = 0L + for j = -h, +h do for i = -h, +h do begin + x[n] = (xi + j) > lo_xy[0] < up_xy[0] + y[n] = (yi + i) > lo_xy[1] < up_xy[1] + extract_overlap, image, template, [x[n], y[n]], [xt, yt], ima, tem, $ + lxi, uxi, lyi, uyi + sx = uxi - lxi + 1 & sy = uyi - lyi + 1 + if min([sx, sy]) ge min_size then begin + if n_elements(xb) ne 0 and n_elements(yb) ne 0 then begin + wb = where(xb ge lxi and xb le uxi and yb ge lyi and yb le uyi, nb) + if nb ne 0 then $ + wb = coord_to_subs(xb[wb] - lxi, yb[wb] - lyi, sx) + endif + correl[n] = correlation_coeff(ima, tem, EXCLUDE = wb) + endif + n = n + 1 + endfor + max_correl = max(correl, w) & x_opt = x[w] & y_opt = y[w] + ; optimize correlation applying sub-pixel shifts + mag = n_elements(templates) * n_elements(dx) * n_elements(dy) ne 0 + if mag then mag = size52(templates, /N_DIM) eq 3 + if mag then begin + nshift = (size52(templates, /DIM))[2] + x = x_opt + dx & y = y_opt + dy & correl = fltarr(nshift) + for n = 0L, nshift - 1 do begin + correlate_max, image, templates[*,*,n], x_opt, y_opt, 1, $ + XT = xt, YT = yt, X_BAD = xb, Y_BAD = yb, correl_n + correl[n] = correl_n + endfor + max_correl = max(correl, w) & x_opt = x[w] & y_opt = y[w] + endif + return +end diff --git a/correlation_coeff.pro b/correlation_coeff.pro new file mode 100644 index 0000000000000000000000000000000000000000..2834cf83eb5320addc03d5feb7ff22fc2b45886b --- /dev/null +++ b/correlation_coeff.pro @@ -0,0 +1,59 @@ +; $Id: correlation_coeff.pro, v 1.0 Aug 1999 e.d. $ +;+ +; NAME: +; CORRELATION_COEFF +; +; PURPOSE: +; Compute the correlation coefficient of a pattern with a reference template. +; +; CATEGORY: +; Image statistics. +; +; CALLING SEQUENCE: +; Results = CORRELATION_COEFF(Pattern, Template) +; +; INPUTS: +; Pattern: Pattern to compare with the template + +; Template: Reference template +; +; KEYWORD PARAMETERS: +; EXCLUDE: Vector of array subscripts identifying data points +; which must be excluded from the computation of the correlation +; +; OUTPUTS: +; Return the correlation coefficient between Pattern and Template. +; +; RESTRICTIONS: +; The input data Pattern and Template must be equally sized arrays +; (with any number of dimensions). +; +; PROCEDURE: +; Compute the correlation coefficient according to the formula in +; Gonzalez, Woods, "Digital Image Processing", +; Addison-Wesley (1992), p. 584' +; Optionally exclude 'bad pixels' from the computation +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +FUNCTION correlation_coeff, pattern, template, EXCLUDE = ex + + on_error, 2 + ; find acceptable data points + n = n_elements(pattern) & s = lindgen(n) + if n_elements(ex) ne 0 then begin + w = where(ex ge min(s) and ex le max(s), n) + if n ne 0 then e = ex[w] + endif + if n_elements(e) ne 0 then begin + s[e] = -1 & w = where(s ge 0, n) + if n ne 0 then s = s[w] + endif + if n eq 0 then return, 0. + ; compute correlation coefficient + p = pattern[s] & p = p - mean(p) + t = template[s] & t = t - mean(t) + return, total(p*t) / sqrt(total(p*p) * total(t*t)) +end diff --git a/create_element.pro b/create_element.pro new file mode 100644 index 0000000000000000000000000000000000000000..2e60199199fcfbee744283a59fc2404cd024d80f --- /dev/null +++ b/create_element.pro @@ -0,0 +1,37 @@ +; $Id: create_element.pro, v 1.1 Mar 2012 e.d. $ +; +;+ +; NAME: +; CREATE_ELEMENT +; +; PURPOSE: +; Generate new element of star list and initialize it. +; +; CATEGORY: +; STARFINDER auxiliary procedures. +; +; CALLING SEQUENCE: +; Result = CREATE_ELEMENT(X, Y, F) +; +; INPUTS: +; X, Y: x- and y- position of object. +; +; OPTIONAL INPUTS: +; F: object flux. +; +; OUTPUTS: +; Return initialized element, representing a (possibly) +; presumed star +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, June 2001. +; 1) Created this file (E. D., March 2012). +;- + +FUNCTION create_element, x, y, f + + on_error, 2 + element = star() + update_list, element, x, y, f + return, element +end diff --git a/crosses.pro b/crosses.pro new file mode 100644 index 0000000000000000000000000000000000000000..ec3b6669769a443520c2bf016d835be2566a1970 --- /dev/null +++ b/crosses.pro @@ -0,0 +1,82 @@ +; $Id: crosses.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; CROSSES +; +; PURPOSE: +; Mark interesting points in an image with crosses. +; +; CATEGORY: +; Data visualization. +; +; CALLING SEQUENCE: +; CROSSES, image, x, y +; +; INPUTS: +; Image: 2D data array +; X, Y: x- and y- coordinates of points to mark (with a '+' sign) +; +; KEYWORD PARAMETERS: +; X2, Y2: x- and y- coordinates of another set of interesting points, +; to be marked with a different symbol ('x' sign) +; +; EXISTING: Set this keyword when the image is already displayed on +; some graphic window +; +; _EXTRA: Input keywords of DISPLAY_IMAGE (namely display options). +; Neglected if the keyword EXISTING is set. +; +; DARK: Set this keyword to specify that the color of the marks must +; be the IDL default background color (dark marks). The default is +; to use the highest color number available (bright marks). +; +; DEVICE: Set this keyword to a nonzero value to specify that the input +; coordinates are device coordinates. The default is data coordinates. +; +; SYMSIZE: Keyword of the IDL routine PLOTS: specifies symbol size. +; +; OUTPUTS: +; Graphic output. +; +; SIDE EFFECTS: +; Open a new graphic window if the image is displayed for the first time. +; +; RESTRICTIONS: +; 1) If the keyword EXISTING is set, the procedure assumes that the image +; is already display in the currently active window. +; 2) If the keyword EXISTING is not set, the image is displayed on a new +; window, using the default display options (see the function +; DEFAULT_DISPLAY_OPT in the file 'default_display_opt.pro') if the input +; keyword OPTIONS is not used. +; 3) The color of the marks (bright by default, dark if the keyword DARK +; is set) is correct assuming the IDL system variables !D and !P have not +; been previously modified. +; +; PROCEDURE: +; Display the image on a new window if the keyword EXISTING is not set, +; using the display options defined by the keyword OPTIONS or the default +; options if the keyword is not defined. Then put a sign on each point. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +PRO crosses, image, x, y, X2 = x2, Y2 = y2, EXISTING = existing, $ + _EXTRA = extra, DARK = dark, DEVICE = device, SYMSIZE = symsize + + on_error, 2 + if not keyword_set(existing) then $ + display_image, image, _EXTRA = extra + if keyword_set(dark) then $ + color = !P.background else $ + color = !D.n_colors - 1 + if keyword_set(device) then scale = [1, 1] else $ + scale = [!D.x_size, !D.y_size] / float(size52(image, /DIM) - 1) + plots, x * scale[0], y * scale[1], PSYM = 1, /DEVICE, $ + COLOR = color, SYMSIZE = symsize + if n_elements(x2) * n_elements(y2) ne 0 then $ + plots, x2 * scale[0], y2 * scale[1], PSYM = 7, /DEVICE, $ + COLOR = color, SYMSIZE = symsize + return +end diff --git a/default_display_opt.pro b/default_display_opt.pro new file mode 100644 index 0000000000000000000000000000000000000000..493ff0bede377d548873d870cc5459523b4da73c --- /dev/null +++ b/default_display_opt.pro @@ -0,0 +1,41 @@ +; $Id: default_display_opt.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; DEFAULT_DISPLAY_OPT +; +; PURPOSE: +; Define default options to display a 2D image. +; +; CATEGORY: +; Data visualization +; +; CALLING SEQUENCE: +; Result = DEFAULT_DISPLAY_OPT(Image) +; +; INPUTS: +; Image: 2D image +; +; OUTPUTS: +; Return structure of default options. The structure fields are: +; range: 2-components vector, containing the min. and max. level to display +; ( default = [min(image), max(image)] ) +; chop: chopping threshold; all the gray-levels above this threshold will be +; replaced with the min. level. The default value is greater than the +; image maximum, i.e. no chopping +; reverse: boolean, true if the gray-levels must be reversed (default false) +; stretch: string, representing the stretch to be used. Possible values are +; 'square', 'linear' (default), 'square root', 'logarithm' +; color_table: long integer, representing the color table number (default 0) +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +FUNCTION default_display_opt, image + + range = [min(image), max(image)] + chop = 1e6 & while chop le range[1] do chop = 10 * chop + return, { range: range, chop: chop, reverse: 0B, $ + stretch: 'linear', color_table: 0L } +end diff --git a/delete_element.pro b/delete_element.pro new file mode 100644 index 0000000000000000000000000000000000000000..c85295c161949d5a0e446f1d8031e3d83229ef96 --- /dev/null +++ b/delete_element.pro @@ -0,0 +1,32 @@ +; $Id: delete_element.pro, v 1.1 Mar 2012 e.d. $ +; +;+ +; NAME: +; DELETE_ELEMENT +; +; PURPOSE: +; Delete last element from list of stars. +; +; CATEGORY: +; STARFINDER auxiliary procedures. +; +; CALLING SEQUENCE: +; Result = DELETE_ELEMENT(List) +; +; INPUTS: +; List: input list to trim. +; +; OUTPUTS: +; Return trimmed list. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, June 2001. +; 1) Created this file (E. D., March 2012). +;- + +FUNCTION delete_element, list + + on_error, 2 + l = list & n = n_elements(l) + return, l[0:n-2] +end diff --git a/diag_mult.pro b/diag_mult.pro new file mode 100644 index 0000000000000000000000000000000000000000..5f1d9d726d783fe4048c354476d36a8fc76f5cda --- /dev/null +++ b/diag_mult.pro @@ -0,0 +1,41 @@ +; $Id: diag_mult.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; DIAG_MULT +; +; PURPOSE: +; Compute the array multiplication of a square array and a diagonal +; array, represented by a 1D vector containing its main diagonal. +; +; CATEGORY: +; Array manipulation. +; +; CALLING SEQUENCE: +; Result = DIAG_MULT(A, D) +; +; INPUTS: +; A: Square array +; +; D: 1D vector, representing main diagonal of diagonal array +; +; KEYWORD PARAMETERS: +; PREMULT: Set this keyword to a nonzero value to pre-multiply A by D. +; The default is to post-multiply. +; +; OUTPUTS: +; Return product array, having the same size as A. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +FUNCTION diag_mult, a, d, PREMULT = premult + + on_error, 2 + size_a = (size52(a, /DIM))[0] + b = a & if keyword_set(premult) then b = transpose(b) + for j = 0L, size_a - 1 do b[*,j] = b[*,j] * d + if keyword_set(premult) then b = transpose(b) + return, b +end diff --git a/display_help.txt b/display_help.txt new file mode 100644 index 0000000000000000000000000000000000000000..e3f4d02e008fedb8e4a833fd0005b3342c5a991a --- /dev/null +++ b/display_help.txt @@ -0,0 +1,16 @@ + 'Display' menu help page + + + The 'Display' menu contains the following sub-menus: + + 'Select data': + Select the data array to display (image, PSF, background, + detected stars, synthetic field, other files). + + 'Options': + Modify the display options of the currently displayed data + array. If the displayed array is a 'global variable' (image, + PSF, background, detected stars, synthetic field), the new + display options become the current options for the array + itself. + \ No newline at end of file diff --git a/display_image.pro b/display_image.pro new file mode 100644 index 0000000000000000000000000000000000000000..5b471349e18ee3e2fd338ca7df362088b7e5aa85 --- /dev/null +++ b/display_image.pro @@ -0,0 +1,98 @@ +; $Id: display_image.pro, v 1.2 Sep 2001 e.d. $ +; +;+ +; NAME: +; DISPLAY_IMAGE +; +; PURPOSE: +; Display a 2D image, according to a pre-fixed or default set of options. +; +; CATEGORY: +; Data visualization. +; +; CALLING SEQUENCE: +; DISPLAY_IMAGE, Image [, wnum] +; +; INPUTS: +; Image: 2D data array +; +; OPTIONAL INPUT PARAMETERS: +; Wnum: number of existing graphic window +; +; KEYWORD PARAMETERS: +; OPTIONS: structure containing display options, +; as defined by the function DEFAULT_DISPLAY_OPT +; +; /MODIFY_OPT: set this keyword to modify display options +; +; OUTPUTS: +; Graphic output on the window identified by the parameter wnum +; +; OPTIONAL OUTPUT PARAMETERS: +; Wnum: number of new graphic window, if undefined on input +; +; OPTIONS: display options +; +; SIDE EFFECTS: +; 1) Use WSET to activate the graphic window identified by the parameter wnum. +; 2) If wnum is undefined, open a new graphic window. The size of this window +; is proportional to the image size and however smaller than the screen size. +; 3) If /MODIFY_OPT is set, call XDisplayOpt to modify the display options. +; +; RESTRICTIONS: +; If the parameter Wnum is a window number < 32 but the corresponding +; window does not exist (or has been deleted), the new window size is set +; by IDL and may not fit the image x- and y size. If Wnum >=32 and the +; corresponding window does not exists, an error occurs. +; +; PROCEDURE: +; Activate the window identified by Wnum (or create a new one) and +; display the input image, using the display options passed with the +; keyword OPTIONS or the default ones defined by DEFAULT_DISPLAY_OPT. +; NOTE on intensity stretch. Possible value are: +; square: display Image^2 +; linear: display Image +; square root: display sqrt(Image > 0) +; logarithm: display alog10(Image > max(Image)*1e-15) +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +; Updates: +; 1) Replaced TVScl with scaling + TV; +; Corrected stretch with reverse option. +; (Emiliano Diolaiti, November 1999). +; 2) Replaced again TV with TVScl +; (Emiliano Diolaiti, September 2001). +;- + +PRO display_image, image, wnum, OPTIONS = options, MODIFY_OPT = modify_opt + + on_error, 2 + ; define graphic window if necessary + if n_elements(wnum) eq 0 then begin + smax = get_screen_size() & smax = min(smax)*2./3. + s = size52(image, /DIM) & s = s / max(s) * smax & s = round(s) + window, /FREE, XSIZE = s[0], YSIZE = s[1] & wnum = !D.window + endif + ; define default display options if necessary + if n_elements(options) eq 0 then $ + options = default_display_opt(image) + ; process image according to display options + data = image + data = (data > options.range[0]) < options.range[1] + w = where(data gt options.chop, count) + if count ne 0 then data[w] = min(data) + case options.stretch of + 'square': data = data^2 + 'linear': ; do nothing + 'square root': data = sqrt(data > 0) + 'logarithm': data = alog10(data > max(data) * 1e-15) + endcase + if options.reverse then data = -data + ; display image + wset, wnum & erase, wnum & loadct, options.color_table, /SILENT + tvscl, congrid(data, !D.x_size, !D.y_size) + if keyword_set(modify_opt) then $ + options = xdisplayopt(image, wnum, OPTIONS = options, /NODISPLAY) + return +end diff --git a/distance.pro b/distance.pro new file mode 100644 index 0000000000000000000000000000000000000000..7dfd01239c29a1bbb9014ed240a41a3aba526508 --- /dev/null +++ b/distance.pro @@ -0,0 +1,33 @@ +; $Id: distance.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; DISTANCE +; +; PURPOSE: +; Compute the euclidean distance of a set of points on a plane from a +; fixed origin. Distances are computed in floating-point aritmethic. +; +; CATEGORY: +; Mathematics. +; +; CALLING SEQUENCE: +; Result = DISTANCE(X0, Y0, X, Y) +; +; INPUTS: +; X0, Y0: Couple of scalars, representing the position of the origin +; +; X, Y: Vectors of coordinates +; +; OUTPUTS: +; Result: Vector of euclidean distances, with the same size as X and Y +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +FUNCTION distance, x0, y0, x, y + + on_error, 2 + return, sqrt((x - float(x0[0]))^2 + (y - float(y0[0]))^2) +end diff --git a/estimate_background.pro b/estimate_background.pro new file mode 100644 index 0000000000000000000000000000000000000000..c57251adb2ffc647eb7ac6cb9eefd2ea9d221a39 --- /dev/null +++ b/estimate_background.pro @@ -0,0 +1,57 @@ +; $Id: estimate_background.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; ESTIMATE_BACKGROUND +; +; PURPOSE: +; Call IMAGE_BACKGROUND to estimate intensity distribution of the +; background in a given image. If an error occurs, estimate the +; background emission by median smoothing of the input image. +; +; CATEGORY: +; Signal processing. +; +; CALLING SEQUENCE: +; Result = ESTIMATE_BACKGROUND(Image, Step) +; +; INPUTS: +; Image: 2D array +; +; Step: Size of sub-regions to measure local background or box size +; for median smoothing of the Image +; +; KEYWORD PARAMETERS: +; SKY_MEDIAN: Set this keyword to a nonzero value to estimate the +; background directly by median smoothing of Image +; +; _EXTRA: All the input keywords supported by IMAGE_BACKGROUND +; +; OUTPUTS: +; Result: 2D array, having the same size as the input Image. +; +; RESTRICTIONS: +; All the restrictions described in IMAGE_BACKGROUND, if the default +; method is used. +; If median smoothing is used, it should be noticed that this technique +; tends to overestimate the local background level, especially in the +; presence of bright point sources. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +FUNCTION estimate_background, image, step, SKY_MEDIAN = sky_median, _EXTRA = extra + + on_error, 2 + if keyword_set(sky_median) then $ + b = median_filter(image, step) else $ + begin + b = image_background(image, step, _EXTRA = extra) + if n_elements(b) eq 1 then begin + message, 'unable to estimate background; trying with median smoothing', /INFO + b = median_filter(image, step) + endif + endelse + return, b +end diff --git a/extend_array.pro b/extend_array.pro new file mode 100644 index 0000000000000000000000000000000000000000..f2d6c06320802b26cbbf4320296e7bebd84a4bac --- /dev/null +++ b/extend_array.pro @@ -0,0 +1,82 @@ +; $Id: extend_array.pro, v 1.1 Jan 2000 e.d. $ +; +;+ +; NAME: +; EXTEND_ARRAY +; +; PURPOSE: +; Pad 2D array with 0s. +; +; CATEGORY: +; Array manipulation. +; +; CALLING SEQUENCE: +; Result = EXTEND_ARRAY(Array, S0, S1) +; +; INPUTS: +; Array: Array to be 0-padded +; +; S0: Size of extended array +; +; OPTIONAL INPUTS: +; S1: Second (y-) size of extended array. The default is S1 = S0 +; +; KEYWORD PARAMETERS: +; NO_OFF: Set this keyword to a nonzero value to indicate that the +; input array must not be centered in the final frame. In this case +; the element [0,0] of the output array coincides with the element +; [0,0] of the input +; +; POW2: Set this keyword to a nonzero value to indicate that the array +; size must be extended to the nearest power of 2. In this case, the +; input values of S0 and S1 are overriden +; +; OUTPUTS: +; Result: 0-padded array. If the keyword NO_OFF is not set, the original +; array is centered in the final frame. +; The input array is returned if the requested output size is smaller +; than the input size +; +; OPTIONAL OUTPUTS: +; OFFSET: Use this output keyword to retrieve a 2-components vector with +; the x- and y- offsets of the [0, 0] element of the input array +; in the final frame +; +; SIDE EFFECTS: +; If POW2 is set, the input values of S0 and S1 are overridden. If S0 and +; S1 are named variables, their values are overwritten. +; +; RESTRICTIONS: +; Apply only to 2D arrays. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +; Updates: +; 1) Fixed bug on keyword POW2 (Emiliano Diolaiti, January 2000). +; 2) Fixed other bug on keyword POW2 (E. D., May 2014). +;- + +FUNCTION extend_array, array, s0, s1, NO_OFF = no_off, POW2 = pow2, OFFSET = o + + on_error, 2 + if size52(array, /N_DIM) ne 2 then return, array + if n_params() eq 1 and not keyword_set(pow2) then return, array $ + else if n_params() eq 2 then s1 = s0 + s = size52(array, /DIM) + if keyword_set(pow2) then begin + s0 = s[0] & s1 = s[1] + l = log2(s0) & if 2^l ne s0 then s0 = 2^(l + 1) + l = log2(s1) & if 2^l ne s1 then s1 = 2^(l + 1) + endif + if s0 eq s[0] and s1 eq s[1] or s0 lt s[0] or s1 lt s[1] then begin + array1 = array & o = [0, 0] + endif else begin + if keyword_set(no_off) then o = [0, 0] $ + else begin + o = [s0, s1] - s & o = (o + o mod 2) / 2 + endelse + array1 = make_array(s0, s1, TYPE = size52(array, /TYPE)) + array1[o[0],o[1]] = array + endelse + return, array1 +end diff --git a/extend_shift.pro b/extend_shift.pro new file mode 100644 index 0000000000000000000000000000000000000000..c5a588607328a4f88bbc7794b4f44cde2d64056e --- /dev/null +++ b/extend_shift.pro @@ -0,0 +1,43 @@ +; $Id: extend_shift.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; EXTEND_SHIFT +; +; PURPOSE: +; Shift a 2D array without edge effects. +; +; CATEGORY: +; Array manipulation. +; +; CALLING SEQUENCE: +; Result = EXTEND_SHIFT(Array, Xshift, Yshift) +; +; INPUTS: +; Array: 2D array +; +; Xshift, Yshift: X- and Y- integer shifts +; +; OUTPUTS: +; Result: Shifted array +; +; RESTRICTIONS: +; Apply only to 2D arrays. +; +; PROCEDURE: +; Pad the array with 0s, perform the shift and restore +; the original size. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +FUNCTION extend_shift, array, xshift, yshift + + on_error, 2 + if xshift eq 0 and yshift eq 0 then return, array + s = size52(array, /DIM) & xs = round(xshift) & ys = round(yshift) + a = extend_array(array, s[0] + abs(xs), s[1] + abs(ys), /NO_OFF) + a = (shift(a, xs, ys))[0:s[0]-1,0:s[1]-1] + return, a +end diff --git a/extract_overlap.pro b/extract_overlap.pro new file mode 100644 index 0000000000000000000000000000000000000000..44e4b47a22f45ffbd689bb0f61fbc8a3e133a8bb --- /dev/null +++ b/extract_overlap.pro @@ -0,0 +1,46 @@ +; $Id: extract_overlap.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; EXTRACT_OVERLAP +; +; PURPOSE: +; Extract the overlap region of two 2D arrays, after ideally superposing +; the positions of a reference point. +; +; CATEGORY: +; Array manipulation. +; +; CALLING SEQUENCE: +; EXTRACT_OVERLAP, Array1, Array2, R1, R2, Over1, Over2, $ +; Lx1, Ux1, Ly1, Uy1, Lx2, Ux2, Ly2, Uy2 +; +; INPUTS: +; Array1, Array2: 2D input arrays +; +; R1: 2-components vector, coordinates of reference point in array 1 +; +; R2: 2-components vector, coordinates of reference point in array 1 +; +; OUTPUTS: +; Over1, Over2: Regions of overlap +; +; OPTIONAL OUTPUTS: +; Lx1, Ux1, Ly1, Uy1: Lower and Upper, X- and Y- bounds of overlap region +; in Array1 +; Lx2, Ux2, Ly2, Uy2: Lower and Upper, X- and Y- bounds of overlap region +; in Array2 +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +PRO extract_overlap, array1, array2, r1, r2, over1, over2, $ + lx1, ux1, ly1, uy1, lx2, ux2, ly2, uy2 + + on_error, 2 + array_overlap, size52(array1, /DIM), size52(array2, /DIM), $ + r1, r2, lx1, ux1, ly1, uy1, lx2, ux2, ly2, uy2 + over1 = array1[lx1:ux1,ly1:uy1] & over2 = array2[lx2:ux2,ly2:uy2] + return +end diff --git a/extract_stars.pro b/extract_stars.pro new file mode 100644 index 0000000000000000000000000000000000000000..ff0e6a88ac60828c31f90b5d670837a548505a6e --- /dev/null +++ b/extract_stars.pro @@ -0,0 +1,36 @@ +; $Id: extract_stars.pro, v 1.1 Mar 2012 e.d. $ +; +;+ +; NAME: +; EXTRACT_STARS +; +; PURPOSE: +; Return sub-list of stars, extracted from a list which might include +; presumed stars. +; +; CATEGORY: +; STARFINDER auxiliary procedures. +; +; CALLING SEQUENCE: +; Result = EXTRACT_STARS(List, N) +; +; INPUTS: +; List: star list +; +; OUTPUTS: +; Return sublist of true stars +; +; OPTIONAL OUTPUTS: +; N: number of extracted stars +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, June 2001. +; 1) Created this file (E. D., March 2012). +;- + +FUNCTION extract_stars, list, n + + on_error, 2 + s = where_stars(list, n) + if n ne 0 then return, list[s] else return, -1 +end diff --git a/file_name.pro b/file_name.pro new file mode 100644 index 0000000000000000000000000000000000000000..a0f6fe2f7220113d03ea35e7764fc28456cef4b3 --- /dev/null +++ b/file_name.pro @@ -0,0 +1,77 @@ +; $Id: file_name.pro, v 1.2 Jun 2000 e.d. $ +; +;+ +; NAME: +; FILE_NAME +; +; PURPOSE: +; Given the name of a file contained in a directory included +; in the IDL !Path system variable, return the fully specified +; name of the file. Operating system dependencies are taken +; into account. +; +; CATEGORY: +; Input/Output. +; +; CALLING SEQUENCE: +; Result = FILE_NAME(Dir, File) +; +; INPUTS: +; Dir: Scalar string, name of parent directory. +; It must be a directory included in the !Path variable. +; +; File: Scalar string, containing the name of the file with no +; path divider. +; +; OUTPUTS: +; Result: Scalar string with the complete name of the file. +; +; RESTRICTIONS: +; Apply only to files in directories included in the !Path system +; variable. +; +; PROCEDURE: +; Search the substring specified by the input variable Dir into the +; !Path system variable. Append a path divider and the name of the +; file specified by the input variable File. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, November 1999. +; Updates: +; 1) Fixed bug related to sub-directories +; (Emiliano Diolaiti, May 2000). +; 2) Case-insensitive (Emiliano Diolaiti, June 2000). +;- + +FUNCTION file_name, dir, file + + on_error, 2 + ; Define path of directory + case !Version.OS of + 'vms': begin + pdiv = ',' & ddiv = '.' + end + 'Win32': begin + pdiv = ';' & ddiv = '\' + end + 'MacOS' : begin + pdiv = ',' & ddiv = ':' + end + else: begin + pdiv = ':' & ddiv = '/' + end + endcase + lowpath = strlowcase(!Path) & lowdir = strlowcase(dir) + first = strpos(lowpath, lowdir) + while first gt 0 and strmid(lowpath, first, 1) ne pdiv do $ + first = first - 1 + if strmid(lowpath, first, 1) eq pdiv then first = first + 1 + last = strpos(lowpath, lowdir) & length = strlen(lowpath) + while last lt length - 1 and strmid(lowpath, last, 1) ne pdiv and $ + strmid(lowpath, last, 1) ne ddiv do $ + last = last + 1 + if strmid(lowpath, last, 1) eq ddiv or $ + strmid(lowpath, last, 1) eq pdiv then last = last - 1 + path = strmid(!Path, first, last - first + 1) + return, path + ddiv + file +end diff --git a/find_rot_trans.pro b/find_rot_trans.pro new file mode 100644 index 0000000000000000000000000000000000000000..62bba021e1f832926c53c58da30fa9a42c7a6222 --- /dev/null +++ b/find_rot_trans.pro @@ -0,0 +1,142 @@ +; $Id: find_rot_trans.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; FIND_ROT_TRANS +; +; PURPOSE: +; Given two sets of coordinates representing the same points in different +; reference frames, supposed to be reciprocally translated and rotated, +; estimate the reciprocal translation and rotation by means of the +; Newton-Gauss iterative method. +; +; CATEGORY: +; Mathematics. Spatial transformations. +; +; CALLING SEQUENCE: +; FIND_ROT_TRANS, X1, Y1, X2, Y2, Origin_0, Angle_0, Origin, Angle, Found +; +; INPUTS: +; X1, Y1: Vectors of x- and y- coordinates of points in +; reference frame no. 1 +; +; X2, Y2: Vectors of x- and y- coordinates of points in +; reference frame no. 2 +; +; Origin_0: 2-components vector representing an initial guess of the +; coordinates of the origin of reference frame 2 with respect to +; reference frame 1 +; +; Angle_0: Scalar value representing an initial guess of the rotation +; angle (in radians) of the x- axis of ref. frame 2 with respect to +; the x- axis of ref. frame 1 +; +; KEYWORD PARAMETERS: +; ORIGIN_TOL: Absolute tolerance (presumably in pixel units) to check the +; convergence condition of the origin in the iterative estimation. +; The default is 0.1 +; +; ANGLE_TOL: Relative tolerance to check the convergence of the angle. +; The default is 0.01 (i.e. 1%) +; +; _EXTRA: Optional keywords for NEWTON_GAUSS (see the NEWTON_GAUSS procedure +; in the file 'newton_gauss.pro') +; +; OUTPUTS: +; Origin: 2-components vector, representing the x- and y- coordinates of +; the origin of reference frame 2 in reference frame 1 +; +; Angle: Scalar, angle (in radians) between the x- axis of reference +; frame 2 and the x- axis of reference frame 1 +; +; Found: Logical value, true if the Newton-Gauss optimization has converged +; +; RESTRICTIONS: +; If the reciprocal translation and/or rotation between the two reference +; frames are large, it is better to provide initial guesses of the +; corresponding parameters, to ensure the convergence of the Newton-Gauss +; algorithm. +; +; PROCEDURE: +; Assume the following relationship between coordinates in ref. frame 2 +; and coordinates in ref. frame 1: +; X2 = - Origin[0] + X1 * COS(Angle) + Y1 * SIN(Angle) +; Y2 = - Origin[1] - X2 * SIN(Angle) + Y2 * COS(Angle) +; and estimate Origin and Angle by means of the iterative Newton-Gauss +; algorithm. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + + + +;;; Auxiliary procedures/functions. + +FUNCTION frt_parametrize, origin, angle + return, [origin[0], origin[1], angle] +end + +PRO frt_deparametrize, p, origin, angle + origin = p[0:1] & angle = p[2] + return +end + +FUNCTION frt_stack, x, y + + on_error, 2 + n = n_elements(x) & z = fltarr(2*n) + sx = 2 * lindgen(n) & sy = sx + 1 + z[sx] = x & z[sy] = y + return, z +end + +FUNCTION frt_model, p, DATA = data + + on_error, 2 + frt_deparametrize, p, origin, angle + rot_trans, data.x, data.y, origin, angle, rtx, rty + return, frt_stack(rtx, rty) +end + +FUNCTION frt_iacobi, p, DATA = data + + on_error, 2 + n = n_elements(data.x) & iacobi = fltarr(2*n, 3) + one = make_array(n, /FLOAT, VALUE = 1.) & zero = fltarr(n) + iacobi[*,0] = frt_stack(-one, zero) + iacobi[*,1] = frt_stack(zero, -one) + frt_deparametrize, p, origin, angle & origin = [0, 0] + rot_trans, data.x, data.y, origin, angle, rtx, rty + iacobi[*,2] = frt_stack(rty, -rtx) + return, transpose(iacobi) +end + +FUNCTION frt_converg, p0, p1, DATA = data + + on_error, 2 + frt_deparametrize, p0, o0, a0 + frt_deparametrize, p1, o1, a1 + return, convergence(o0, o1, data.o_tol, /ABSOLUTE) and $ + convergence(a0, a1, data.a_tol) +end + +;;; The main routine. + +PRO find_rot_trans, x1, y1, x2, y2, origin_0, angle_0, origin, angle, found, $ + ORIGIN_TOL = o_tol, ANGLE_TOL = a_tol, _EXTRA = extra + + on_error, 2 + if n_elements(x1) lt 2 then $ + message, 'at least 2 points are required' + if n_elements(o_tol) eq 0 then o_tol = 0.1 + if n_elements(a_tol) eq 0 then a_tol = 0.01 + ; define "global" data structure. + data = {x: x1, y: y1, o_tol: o_tol, a_tol: a_tol} + ; parameters estimation + newton_gauss, "frt_model", "frt_iacobi", "frt_converg", $ + frt_parametrize(origin_0, angle_0), frt_stack(x2, y2), $ + DATA = data, _EXTRA = extra, found, p + if found then frt_deparametrize, p, origin, angle + return +end diff --git a/fitstars.pro b/fitstars.pro new file mode 100644 index 0000000000000000000000000000000000000000..0748524047c7d8de2c8cea4d06d799ce44b4f2ab --- /dev/null +++ b/fitstars.pro @@ -0,0 +1,439 @@ +; $Id: fitstars.pro, v 1.5 Oct 2013 e.d. $ +; +;+ +; NAME: +; FITSTARS +; +; PURPOSE: +; Fit a multiple-component stellar image with a model given by a sum +; of shifted scaled replicas of a reference image (one for each star), +; superposed on a background represented by a slanting plane plus a +; fixed additive contribution. +; The parameters to be optimized are the stellar fluxes and sub-pixel +; positions along with the coefficients of the slanting plane. +; The optimization is performed by means of the Newton-Gauss iterative +; method. +; The reference image to be replicated (PSF) may either be a fixed +; template ("input PSF" option) or a model computed by an auxiliary +; procedure ("PSF model" option). +; +; CATEGORY: +; Stellar astrometry and photometry. +; +; CALLING SEQUENCE: +; FITSTARS, Image, Psf, X0, Y0, X, Y, F, B, Fit_error, $ +; Sigma_x, Sigma_y, Sigma_f, Sigma_b +; +; INPUTS: +; Image: Stellar image +; +; Psf: When the "input PSF" option is used, this parameter must +; contain a 2D image of the PSF. +; When the "space-variant PSF" option is used, Psf must be a 3D stack +; of PSF images. In this case it is necessary to supply the bounds of +; image domain partition (see KEYWORDS LX, UX, LY, UY). +; When the "PSF model" option is used, it must contain the string +; which identifies the type of PSF model to apply, as defined in the +; function IMAGE_MODEL (see the file 'image_model.pro'). +; Notice that when the first option is applied, the input template +; is shifted by interpolation to match the stars in the Image to fit. +; In this case it is possible to select an interpolation technique +; with the keyword INTERP_TYPE (see below). For details on the +; supported interpolation techniques see the functions IMAGE_SHIFT +; or IMAGE_MODEL. When the second option is applied instead, the +; procedure which computes the PSF model probably requires additional +; information, which should be passed through with the keyword +; PSF_DATA (see below). +; +; X0, Y0: Vectors of approximate positions of point sources +; +; KEYWORD PARAMETERS: +; FIXED: Fixed additive contribution, not optimized in the fitting +; process. It must have the same size has the Image. +; It may represent the contribution of point sources whose centers +; lie outside the Image support +; +; LX, UX, LY, UY: Vectors specifying the bounds of the image domain +; partition when the "space-variant PSF" option is used. In this case +; the sub-domain [LX[j]: Ux[j], LY[i]: UY[i]] must correspond to the +; (i * X_size + j)-th PSF in the input stack. +; +; PSF_DATA: Use this keyword to pass a pointer to the auxiliary data +; required either to shift the input PSF ("input PSF" option) or +; to compute the PSF model ("PSF model" option). +; Notice that when the "input PSF" option is applied, this keyword +; may be undefined on input and may be used to retrieve on output +; the auxiliary data which can be recycled in further calls. When +; the "PSF model" option is applied instead, the keyword PSF_DATA +; must generally be defined also on input (unless the user defines +; a procedure for the PSF model based on COMMON blocks). +; +; F0: Initial guesses of scaling factors (i.e. fluxes) for each point +; source. Fluxes must be referred to the normalization of the Psf. +; +; BACKGROUND: The image background is fitted with a slanting plane. +; To obtain an initial guess of the stellar fluxes (if F0 is not +; defined), it is better to have an approximate knowledge of the +; image background. If no approximate background is provided and +; F0 is not defined, the initial guesses of the stellar fluxes are +; computed under the hypothesis of no background in the image. +; +; NO_SLANT: Set this keyword to a nonzero value to fit the stellar +; image with a model made of a sum of stars (plus FIXED contribution) +; but without slanting plane +; +; NOISE_STD: Noise standard deviation. It may be a scalar +; ('white noise' case) or an array, having the same size as Image +; +; BAD_DATA: 1D vector of Image subscripts representing bad pixels in the +; Image to be masked, i.e. excluded from the fitting process +; +; POS_TOL: Absolute tolerance on stars positions, used by the fitting +; procedure for a convergence check. The default is 0.01 pixels +; +; PHOT_TOL: Relative tolerance on stars fluxes, used for the convergence +; check. The default is 0.01, i.e. 1% +; +; VERBOSE: Set this keyword to a nonzero value to have some output at +; the end of the fit +; +; INTERP_TYPE: String constant, indicating the interpolating technique +; used by IMAGE_SHIFT to interpolate the input Psf in order to match +; it to an observed star, which is generally off-centered. +; For more details, see the function IMAGE_SHIFT in the file +; 'image_shift.pro' +; +; FLUXOPT: Set this binary keyword to keep the positions of the sources +; fixed in the optimization process. The input values set by parameters +; X0 and Y0 are retained. Only fluxes (and background parameters unless +; keyword NO_SLANT is set) are estimated. +; +; _EXTRA: Optional input keyword of NEWTON_GAUSS (e.g. MAXIT, MASK, +; SVDINV, etc.), For more details, see the procedure NEWTON_GAUSS +; in the file 'newton_gauss.pro' +; +; OUTPUTS: +; X, Y: Vector of stars positions, estimated with sub-pixel accuracy +; +; F: Vector of stellar fluxes, referred to the normalization of the Psf +; +; B: 3-components vector of [B0, B1, B2] background coefficients. +; The background is defined as follows +; B(x,y) = B0 + B1 * x + B2 * y +; +; Fit_error: Least squares fitting error between the best fit model and +; the input Image. A negative value indicates that the fitting procedure +; has not converged: the output results are not reliable. +; +; Sigma_x, Sigma_y, Sigma_f, Sigma_b: Vectors containing the formal errors +; (standard deviation) on the estimated parameters (X, Y, F, B). +; Available only if some information on the noise is given on input, +; by means of the keywords GAUSSIAN_NOISE and/or PHOTON_COUNTS. +; If the keyword FIXEDPOS is set, the output values of Sigma_x and Sigma_y +; are 0 by definition. +; +; OPTIONAL OUTPUTS: +; PSF_DATA: This input keyword may also be used as output keyword to +; retrieve the additional data required for the PSF shift or the PSF +; model computation, in case these data are undefined on input or have +; been modified by IMAGE_MODEL, the function which actually shift the +; PSF or compute the PSF model. For more details, see the section about +; KEYWORD PARAMETERS above. +; +; MODEL: Best fit image model +; +; IT: Actual number of iterations performed by NEWTON_GAUSS +; +; W_BAD: Array of Image subscripts representing bad pixels which have +; been masked. It coincides with the value of the input keyword +; BAD_DATA, unless additional bad data masking has been performed +; by NEWTON_GAUSS. For more details on data masking, see the procedure +; NEWTON_GAUSS in the file 'newton_gauss.pro' +; +; RESTRICTIONS: +; The main restriction concerns Psf interpolation, a necessary step to +; match an observed off-centered star with a reference template if the +; "input PSF" of "space-variant PSF" option is applied. Common interpolation +; techniques (Fourier transform shift, spline interpolation) cannot be +; applied to undersampled data. In this case suitable techiques should be +; used instead, exploiting the "PSF model" option. For details on supported +; PSF models, see the function IMAGE_MODEL in the file 'image_model.pro'. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +; Updates: +; 1) Fixed bug on output PSF_DATA (Emiliano Diolaiti, February 2000). +; 2) Added initial estimate on background (Emiliano Diolaiti, April 2000). +; 3) PSF_DATA again (Emiliano Diolaiti, June 2000). +; 4) Added space-variant PSF stack option (E.D., December 2004). +; 5) Added check on PSF_DATA to avoid confusion with other keywords +; (E.D., February 2006). +; 6) Added keyword FLUXOPT (E.D., April 2012). +; 7) Modified initial flux estimate at lines 378-ecc (L.S, E.D, October 2013) +;- + + + +;;; Auxiliary procedures/functions. + +; FS_MODEL: compute the image model for the current set of parameters. + +FUNCTION fs_model, parameters, DATA = data, PSF_DATA = psf_data + + if n_tags((*data).extra) ne 0 then extra = (*data).extra + sx = (*data).sx & sy = (*data).sy + nstar = (*data).nstar & b_nterms = (*data).b_nterms + fs_deparam, parameters, x, y, f, b, nstar, b_nterms + unit_flux = 1 & model = fltarr(sx, sy) + for n = 0L, nstar - 1 do begin + (*data).psf_stack[*,*,n] = $ + image_model(x[n], y[n], unit_flux, sx, sy, (*data).psf, $ + psf_data, INTERP_TYPE = (*data).interp_type, _EXTRA = extra) + model = model + f[n] * (*data).psf_stack[*,*,n] + endfor + bplane = 0 + if b_nterms ne 0 then bplane = plane(b[0], b[1], b[2], sx, sy) + model = model + (*data).fixed_image + bplane + return, model +end + +; FS_PARAM: store the model parameters (i.e. user parameters x, y, etc.) +; into a single vector (program parameters). + +FUNCTION fs_param, x, y, f, b, nstar, b_nterms + + parameters = fltarr(3*nstar + b_nterms) + parameters[0] = f + subs = nstar + 2*lindgen(nstar) + parameters[subs] = x & parameters[subs+1] = y + if b_nterms ne 0 then parameters[3*nstar] = b + return, parameters +end + +; FS_DEPARAM: convert program parameters vector to user parameters. + +PRO fs_deparam, parameters, x, y, f, b, nstar, b_nterms + + f = parameters[0:nstar-1] + subs = nstar + 2*lindgen(nstar) + x = parameters[subs] & y = parameters[subs+1] + if b_nterms ne 0 then b = parameters[3*nstar:3*nstar+b_nterms-1] + return +end + +; FS_CENTER_DERIVATIVE: compute the partial derivative of an image +; with respect to the x- or y- coordinate of its center (maximum). + +FUNCTION fs_center_derivative, image, f, siz + + deriv = 2*!pi/siz * fft(image) * f + deriv = complex(imaginary(deriv), -float(deriv)) + deriv = float(fft(deriv, /INVERSE)) + return, deriv +end + +; FS_IACOBI: compute the Iacobi matrix of the parametric model. + +FUNCTION fs_iacobi, parameters, DATA = data, PVAR = pvar + + sx = (*data).sx & sy = (*data).sy + nstar = (*data).nstar & b_nterms = (*data).b_nterms + varpos = n_elements(pvar) eq 0 + if varpos then $ + npar = 3*nstar + b_nterms else $ + npar = nstar + b_nterms + n_pix = sx * sy + iacobi = fltarr(n_pix, npar) + for n = 0L, nstar - 1 do begin + iacobi[*,n] = reform((*data).psf_stack[*,*,n], n_pix) + if varpos then begin + deriv = fs_center_derivative((*data).psf_stack[*,*,n], (*data).x_frequency, sx) + iacobi[*,nstar+2*n] = reform(parameters[n] * deriv, n_pix) + deriv = fs_center_derivative((*data).psf_stack[*,*,n], (*data).y_frequency, sy) + iacobi[*,nstar+2*n+1] = reform(parameters[n] * deriv, n_pix) + endif + endfor + if varpos then k = 3*nstar else k = nstar + if b_nterms ne 0 then iacobi[*,k] = 1 + if b_nterms eq 3 then begin + iacobi[*,k+1] = reform(plane(0, 1, 0, sx, sy), n_pix) + iacobi[*,k+2] = reform(plane(0, 0, 1, sx, sy), n_pix) + endif + return, transpose(iacobi) +end + +; FS_CONVERGENCE: check the convergence condition for the iterative estimation. + +FUNCTION fs_convergence, p0, p, DATA = data + + fs_deparam, p0, x0, y0, f0, b0, (*data).nstar, (*data).b_nterms + fs_deparam, p, x, y, f, b, (*data).nstar, (*data).b_nterms + check = convergence(x0, x, (*data).pos_tol, /ABSOLUTE) and $ + convergence(y0, y, (*data).pos_tol, /ABSOLUTE) and $ + convergence(f0, f, (*data).phot_tol) +; if (*data).b_nterms ne 0 then $ +; check = check and convergence(b0, b, (*data).phot_tol) + return, check +end + +; FS_WEIGHTS: compute inverse variances for weighted least squares fit. + + FUNCTION fs_weights, var + + min_var = min(var) + if min_var le 0. then min_var = max(var) * 1e-15 + return, 1 / (var > min_var) +end + +; FS_ERROR: compute the least squares error between the model and the image. + +FUNCTION fs_error, model, data, WEIGHTS = weights, W_BAD = w_bad + + if n_elements(weights) ne 0 then w = weights else w = 1 + d = w * (model - data)^2 + if n_elements(w_bad) ne 0 then $ + if min(w_bad) ge 0 then d[w_bad] = 0 + return, total(d) +end + +; FS_OUT: print iteration no., fitting error, parameters. + +PRO fs_out, it, fit_error, parameters, sigma_p, nstar, b_nterms + + print, '' + print, 'no. of iterations ', it + print, 'fitting error ', fit_error + print, 'parameters: ' + fs_deparam, parameters, x, y, f, b, nstar, b_nterms + if n_elements(sigma_p) ne 0 then $ + fs_deparam, sigma_p, sigma_x, sigma_y, sigma_f, sigma_b, nstar, b_nterms + print, 'x coordinates: ', x + if n_elements(sigma_x) ne 0 then $ + print, 'st. dev. on x coordinates: ', sigma_x + print, 'y coordinates: ', y + if n_elements(sigma_y) ne 0 then $ + print, 'st. dev. on y coordinates: ', sigma_y + print, 'fluxes : ', f ;* scale + if n_elements(sigma_f) ne 0 then $ + print, 'st. dev. on fluxes : ', sigma_f + if b_nterms ne 0 then print, 'background : ', b + if n_elements(sigma_b) ne 0 then $ + print, 'st. dev. on background : ', sigma_b + return +end + +;;; The main routine. + +PRO fitstars, $ + image, FIXED = fixed_image, psf, PSF_DATA = psf_data, $ + x0, y0, F0 = f0, BACKGROUND = background, NO_SLANT = no_slant, $ + POS_TOL = pos_tol, PHOT_TOL = phot_tol, VERBOSE = verbose, $ + _EXTRA = extra, INTERP_TYPE = interp_type, NOISE_STD = noise_std, $ + x, y, f, b, fit_error, sigma_x, sigma_y, sigma_f, sigma_b, $ + MODEL = model, IT = it, BAD_DATA = bad_data, W_BAD = w_bad, $ + FLUXOPT = fluxopt + + catch, error + if error ne 0 then begin + if ptr_valid(data) then ptr_free, data + if ptr_valid(pdata) then ptr_free, pdata + fit_error = -1 + return + endif + ; Define pointers to 'global' variables + s = size52(image, /DIM) & sx = s[0] & sy = s[1] + if n_elements(fixed_image) eq 0 then fixed_image = 0 + if n_elements(background) eq 0 then $ + if n_elements(f0) eq 0 then background = 0 + if not keyword_set(no_slant) then begin + b_nterms = 3 & b = fltarr(b_nterms) + if n_elements(background) ne 0 then $ + b[0] = total(background) / n_elements(background) + endif else begin + b_nterms = 0 & b = 0 + endelse + if n_elements(interp_type) eq 0 then interp_type = '' + nstar = n_elements(x0) + psf_stack = fltarr(sx, sy, nstar) + if n_elements(extra) ne 0 then extra_data = extra else extra_data = 0 + if n_elements(pos_tol) eq 0 then pos_tol = 0.01 + if n_elements(phot_tol) eq 0 then phot_tol = 0.01 + x_frequency = frequency(sx) # (fltarr(sy) + 1) + y_frequency = (fltarr(sx) + 1) # frequency(sy) + data = {image: image, fixed_image: fixed_image, psf: psf, $ + psf_stack: psf_stack, x_frequency: x_frequency, $ + y_frequency: y_frequency, sx: sx, sy: sy, nstar: nstar, $ + b_nterms: b_nterms, pos_tol: pos_tol, phot_tol: phot_tol, $ + interp_type: interp_type, extra: extra_data} + data = ptr_new(data, /NO_COPY) ; pointer to data structure + pdata = ptr_new(/ALLOCATE) ; pointer to PSF auxiliary data + if (ptr_valid(psf_data))[0] then $ + if n_elements(*psf_data) ne 0 then *pdata = *psf_data + + ; Initial estimates of parameter + ndim_psf = size52(psf, /N_DIM) + if ndim_psf lt 2 then begin + avg_psf = image_model(sx/2, sy/2, 1, sx, sy, psf, pdata) + tot_psf = total(avg_psf) + max_psf = max(avg_psf) + endif else $ + if ndim_psf eq 2 then begin + tot_psf = total(sub_array(psf, sx, sy)) + max_psf = max(sub_array(psf, sx, sy)) + endif else $ + begin + npsf = (size52(psf, /DIM))[2] + avg_psf = total(psf, 3) / npsf + tot_psf = total(sub_array(avg_psf, sx, sy)) + max_psf = max(sub_array(avg_psf, sx, sy)) + endelse + x = x0 & y = y0 + if n_elements(f0) eq 0 then begin + sub = image - fixed_image - background + f = sub[round(x)>0<(sx - 1), round(y)>0<(sy - 1)] ; intensities + ; f = f / total(f) * total(sub) / tot_psf ; fluxes + f = f / max_psf + endif else f = f0 + p0 = fs_param(x, y, f, b, nstar, b_nterms) + if keyword_set(fluxopt) then begin + pvar = make_array(n_elements(p0), VALUE = 1) + k = nstar + 2*lindgen(nstar) + pvar[k] = 0 + pvar[k + 1] = 0 + endif + + ; Define noise weights + if n_elements(noise_std) ne 0 then weights = fs_weights(noise_std^2) + + ; Iterative estimation of parameters + newton_gauss, 'fs_model', 'fs_iacobi', 'fs_convergence', p0, image, $ + _EXTRA = extra, NOISE_STD = noise_std, INVERSE_DATA_VAR = $ + weights, converged, p, sigma_p, model, IT = it, $ + BAD_DATA = bad_data, W_BAD = w_bad, DATA = data, $ + PSF_DATA = pdata, PVAR = pvar + if converged then begin + fit_error = fs_error(model, image, WEIGHTS = weights, W_BAD = w_bad) + fs_deparam, p, x, y, f, b, nstar, b_nterms + if n_elements(sigma_p) ne 0 then $ + fs_deparam, sigma_p, sigma_x, sigma_y, sigma_f, sigma_b, nstar, b_nterms + if keyword_set(fluxopt) then begin + sigma_x = fltarr(nstar) + sigma_y = fltarr(nstar) + endif + if (size52(x0))[0] eq 0 then begin + x = x[0] & y = y[0] & f = f[0] + endif + if keyword_set(verbose) then begin + print, 'FITSTARS: converged' + fs_out, it, fit_error, p, sigma_p, nstar, b_nterms + endif + endif else begin + fit_error = -1 + if keyword_set(verbose) then print, 'FITSTARS: not converged' + endelse + ptr_free, data + if (ptr_valid(psf_data))[0] then $ + if n_elements(*pdata) ne 0 then *psf_data = *pdata + if ptr_valid(pdata) then ptr_free, pdata + return +end diff --git a/fitting_errors.pro b/fitting_errors.pro new file mode 100644 index 0000000000000000000000000000000000000000..ccd1041ad03d2c4ea2585b3394a382ccf64fe80a --- /dev/null +++ b/fitting_errors.pro @@ -0,0 +1,53 @@ +; $Id: fitting_errors.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; FITTING_ERRORS +; +; PURPOSE: +; Estimate formal errors (standard deviations) on the solution +; of an algebraic system of linear equations, given the inverse +; of the system matrix. +; +; CATEGORY: +; Mathematics. Linear systems. +; +; CALLING SEQUENCE: +; Result = FITTING_ERRORS(Inverse) +; +; INPUTS: +; Inverse: Inverse matrix of the linear system +; +; KEYWORD PARAMETERS: +; SCALING: Set this keyword to the vector of scaling factors +; used to scale the linear system before inversion +; +; OUTPUTS: +; Return a vector of formal errors on the components of the solution +; of the linear system. +; +; RESTRICTIONS: +; The input array must be square, as always happens when it is the +; inverse matrix of a linear system of "normal equations". +; The system of normal equations should represent the mathematical +; formulation of a weighted least squares fitting problem. It is +; essential that the least squares error is weighted (by the inverse +; variances on the data), otherwise the formal errors on the solution +; have no meaning. +; +; PROCEDURE: +; Estimate the errors as the square roots of the diagonal elements +; of the inverse array. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +FUNCTION fitting_errors, inverse, SCALING = scaling + + on_error, 2 + n = (size52(inverse, /DIM))[0] & d = lindgen(n) + e = sqrt(inverse[d,d] > 0) + if n_elements(scaling) eq n then e = e * scaling + return, e +end \ No newline at end of file diff --git a/frequency.pro b/frequency.pro new file mode 100644 index 0000000000000000000000000000000000000000..ec2d28bfb1799fa74477b8a91b9ff637e7ee2744 --- /dev/null +++ b/frequency.pro @@ -0,0 +1,36 @@ +; $Id: frequency.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; FREQUENCY +; +; PURPOSE: +; Return a 1D vector of Fourier frequencies, according to how IDL +; orders the components of the Fourier Transform of an array. +; +; CATEGORY: +; Mathematics. Transforms. +; +; CALLING SEQUENCE: +; Result = FREQUENCY(N) +; +; INPUTS: +; N: number of frequencies to compute +; +; OUTPUTS: +; Return a n-elements vector of floating-point frequencies. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +FUNCTION frequency, n + + f = fltarr(n) & nv2 = n/2 + f[0:nv2] = findgen(nv2 + 1) + if n gt 2 then $ + if n mod 2 eq 0 then $ + f[nv2+1:n-1] = -reverse(findgen(nv2-1)) - 1 else $ + f[nv2+1:n-1] = -reverse(findgen(nv2)) - 1 + return, f +end diff --git a/fwhm.pro b/fwhm.pro new file mode 100644 index 0000000000000000000000000000000000000000..bdd8087d3103f78c9348225cb109c383cb7b2578 --- /dev/null +++ b/fwhm.pro @@ -0,0 +1,78 @@ +; $Id: fwhm.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; FWHM +; +; PURPOSE: +; Compute the FWHM (Full Width at Half Maximum) of a peak in an image. +; The FWHM is estimated as the diameter of a circle having the same area +; as the peak. +; +; CATEGORY: +; Signal processing. +; +; CALLING SEQUENCE: +; Result = FWHM(Array) +; +; INPUTS: +; Array: Image containing the peak to be measured +; +; KEYWORD PARAMETERS: +; X, Y: Coordinates of the peak. +; The default is the Image absolute maximum +; +; MAG: Integer magnification factor for array magnification. It may be +; useful to improve the accuracy of the initial measurement, based on +; the area of the peak. +; The default is MAG = 1, i.e. no magnification. +; +; CUBIC: Set this keyword to a nonzero value to magnify the image with +; the cubic convolution interpolation implemented in the library +; routine CONGRID. This keyword has effect only if MAG is defined. +; If CUBIC is not set, the array magnification, when required, is +; performed with REBIN (bilinear interpolation). +; +; OUTPUTS: +; Result: FWHM of the peak. +; +; RESTRICTIONS: +; 1) The input Array is supposed to have been background-subtracted and +; centered with sub-pixel accuracy, especially if the FWHM is a few +; pixels. +; 2) The results with the default settings are not much accurate in general. +; The following calling sequence is recommended: +; Result = FWHM(Array, MAG = 3, /CUBIC). +; 3) To compute the FWHM of a peak in a large array, use PEAK_FWHM. +; For more details see the routine PEAK_FWHM in the file 'peak_fwhm.pro'. +; +; PROCEDURE: +; Call PEAK_WIDTH to obtain an initial guess of the FWHM, based on +; the area of the peak thresholded at half maximum. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +FUNCTION fwhm, array, X = x0, Y = y0, MAG = mag_fac, CUBIC = cubic + + on_error, 2 + ; magnify array + if n_elements(mag_fac) eq 0 then mag_fac = 1 + mag = round(mag_fac) > 1 + a = array & siz = size52(a, /DIM) * mag + if mag gt 1 then $ + if keyword_set(cubic) then $ + a = congrid(a, siz[0], siz[1], CUBIC = -0.5) else $ + a = rebin(a, siz[0], siz[1]) + ; define peak position + if n_elements(x0) * n_elements(y0) eq 0 then begin + m = get_max(a) & x = m[0] & y = m[1] + endif else begin + x = round(x0) * mag & y = round(y0) * mag + endelse + ; estimate FWHM + fw = peak_width(a, X = x, Y = y) + if mag gt 1 then fw = float(fw) / mag + return, fw +end diff --git a/gauss_noise_std.pro b/gauss_noise_std.pro new file mode 100644 index 0000000000000000000000000000000000000000..4975bab1473d992aab5df43a403543f235f28c76 --- /dev/null +++ b/gauss_noise_std.pro @@ -0,0 +1,248 @@ +; $Id: gaussian_noise_std.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; GAUSSIAN_NOISE_STD +; +; PURPOSE: +; Given a 2D image, compute standard deviation of normally distributed +; noise by histogram fitting. +; +; CATEGORY: +; Statistics. +; +; CALLING SEQUENCE: +; GAUSSIAN_NOISE_STD, Data, Mode, Std, H, V, Vmean, Hfit +; +; INPUTS: +; Data: 2D data array +; +; KEYWORD PARAMETERS: +; PATCH: Box size for median smoothing of the data. The median smoothed +; image is subtracted from the input data, to remove all the features +; which would cause an anomalous spread of the histogram +; +; POINT_FRAC: The histogram is computed on a subset of the input data. +; The number of points in the sample is given by the number of +; elements in the image / the value set by this keyword. The default +; is 1, i.e. all the input data are used. +; +; NOSUB: Set this keyword to a nonzero value to skip median subtraction +; +; N_STD: After extracting a sample from the original image, a further +; restriction is applied to exclude the so-called "outliers", i.e. +; pixels whose value is too distant from the sample median. +; The keyword N_STD fixes a tolerance for outliers identification. +; The rejection condition for a given element called "value" is +; abs("value" - "sample median") > +; N_STD * "standard deviation of (sample - sample median)" +; The default is 3 +; +; HIST_MINSIZE: The data histogram is optimized in order to have at +; least a minimum number of bins in the histogram's Half Width at +; Half Maximum (HWHM). The minimum number of bins in one histogram's +; HWHM is fixed by this keyword. The default is 5. +; Notice this keyword is just a hint and may be overridden. +; +; HIST_MAXSIZE: The data histogram is fitted with a gaussian curve, to +; estimate the noise standard deviation. In practice only the central +; part of the histogram is considered, around the mode. The size of +; the useful section of the histogram may be defined in units of the +; approximate histogram's FWHM by means of this keyword. +; The default is 5. +; Notice this keyword is just a hint and may be overridden. +; +; MAXIT: The gaussian fit to the histogram is performed in the range +; [MIN(V), Mode + Std], because the right tail of the histogram may +; be contaminated by the photon noise due to spatially non-uniform +; sources. The first fit is performed starting from an initial guess +; of the quantities Mode and Std and is then repeated iteratively, +; until the estimated Std converges to a stable value or a maximum +; number of iterations reached. Use the keyword MAXIT to fix the +; maximum number of iterations. The default is 10, even though 2-3 +; are generally sufficient +; +; TOL: Tolerance for the convergence of Std in the iteration of the +; gaussian fit. The default is 0.01 +; +; NTERMS_FIT: Same as the input keyword NTERMS of the library routine +; GAUSSFIT. The default is 3, i.e. the histogram is fitted with a +; pure gaussian curve (having 3 parameters: a normalization constant, +; the center and the standard deviation). Set NTERMS = 4 to add a +; constant terms to the fitting curve, NTERMS = 5 to add a linear +; background and NTERMS = 6 to add a quadratic background. +; +; OUTPUTS: +; Mode: Histogram mode, determined as the center of the best fit +; gaussian. If median subtraction is performed, Mode should be near 0. +; When median subtraction is skipped, Mode represents the mean level +; of the image gaussian noise +; +; Std: Standard deviation of gaussian noise, estimated as the "sigma" +; of the best fit gaussian. A negative value indicates an error +; +; H: Histogram (1D array) +; +; V: 1D array of data values, corresponding to the center of each +; histogram bin +; +; Vmean: 1D array of data values, corresponding to the mean of the +; pixels entering each histogram bin +; +; Hfit: Best fit gaussian. The right tail of the gaussian is truncated +; at 1 Std from the histogram mode +; +; OPTIONAL OUTPUTS: +; COEFF: Vector of best fit model coefficients, returned by GAUSSFIT. +; +; RESTRICTIONS: +; 1) This procedure assumes the image noise is normally distributed. +; Normally distributed noise includes for instance read-out-noise, but +; also the photon (Poisson) noise associated to nearly spatially uniform +; signals contributing to the image (e.g. dark current, smooth nebulosities +; in the sky, etc.). A Poisson-distributed 2-D signal may be considered as +; distributed according to a unique gaussian probability law if: +; a) the signal is not too faint +; b) the signal is approximately spatially uniform +; If the image contains both gaussian and photon noise due to non-smooth +; sources (e.g. stars), the noise standard deviation will be of course +; over-estimated. +; +; 2) A maximum number of bins is fixed when computing the histogram. +; This threshold is equal to number of elements in the data sub-sample, +; which corresponds to having (in the mean) one value per histogram bin! +; In practice the computation of the histogram starts with a minimum +; number of bins. The bin number is then iteratively increased (by a +; factor of 2 at every iteration) until there is a minimum number of bins +; (fixed by the keyword HIST_MINSIZE) in one histogram's HWHM or the +; maximum number of bins is reached. In the latter case the former +; condition may not be fulfilled. +; +; PROCEDURE: +; Extract a sample of spatially uniformly distributed image pixels, +; remove outliers and compute the sample histogram. When median removal +; is performed, the histogram bin corresponding to the data values around +; 0 may be strongly over-populated, due to the large number of small +; positive values produced by median subtraction. This bin is thus +; replaced with the mean of the two adjacent ones. The data histogram is +; then fit with a gaussian curve, whose sigma represents the standard +; deviation of the image gaussian noise. The fit is performed by means of +; the library routine GAUSSFIT. +; +; EXAMPLE: +; Compute the mean and standard deviation of an array of normally +; distributed pesudo random numbers. +; Noise = RANDOMN(Seed, 512, 512) +; GAUSSIAN_NOISE_STD, Noise, Mode, Std, H, V, Vmean, Hfit +; PRINT, Mode, Std +; PLOT, PSYM = 10, V, H & OPLOT, LINESTYLE = 1, V, Hfit +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + + + + + +;;; Auxiliary procedures / functions. + +; OPT_HISTO: auxiliary procedure. Compute the optimal histogram +; by adjusting iteratively the bin size so that the histogram FWHM +; contains at least a minimum number of bins. + +; INPUT +; data: data to compute histogram +; [HIST_MINSIZE = ]: min. no. of bins in histogram HWHM (default = 5) +; [HIST_MAXSIZE = ]: size of output histogram in FWHM units (default = 5) +; [/REPLACE_MAX]: replace most populated histogram bin, which may be +; an artifact (e.g. if the original data are median-subtracted) +; OUTPUT +; h: histogram +; x: center of each bin +; xmean: vector containing mean data value for each histogram bin + +; REMOVE_SPIKE: remove spike (corresponding to 0-bin). + +FUNCTION remove_spike, h + + on_error, 2 + m = max(h, w) + if w gt 0 and w lt n_elements(h)-1 then $ + h[w] = round(( h[w-1]+h[w+1] ) / 2.) + return, h +end + +PRO opt_histo, data, HIST_MINSIZE = min_size, HIST_MAXSIZE = max_size, $ + REPLACE_MAX = replace, h, v, vmean + + on_error, 2 + if n_elements(min_size) eq 0 then min_size = 5 ; bin units + if n_elements(max_size) eq 0 then max_size = 5 ; FWHM units + max_nbin = n_elements(data) ; max total no. of histogram bins + range = float(max([ abs(min(data)), abs(max(data)) ])) + dmin = -range & dmax = +range & range = 2*range & nbin = 1L + ; compute the histogram, adjusting iteratively the bin size + repeat begin + nbin = nbin * 2 & bin = range / nbin + histo, data, dmin, dmax, bin, h, v, vmean + if keyword_set(replace) then h = remove_spike(h) + l = histo_hwhm(h, -1, mode_sub) + r = histo_hwhm(h, +1, mode_sub) + endrep until min([l, r]) ge min_size or 2*nbin ge max_nbin + ; reduce histogram size + l = (mode_sub - max_size * l) > 0 + r = (mode_sub + max_size * r) < (n_elements(h) - 1) + h = h[l:r] & v = v[l:r] & vmean = vmean[l:r] + return +end + +;;; The main routine. + +PRO gauss_noise_std, data, PATCH = patch_, POINT_FRAC = point_frac, $ + NOSUB = nosub, N_STD = n_std, _EXTRA = extra, $ + MAXIT = maxit, TOL = tol, NTERMS_FIT = nterms, $ + mode, std, h, v, vmean, hfit, COEFF = c + + on_error, 2 + ; remove median + if n_elements(patch_) eq 0 then patch = 3 else patch = patch_ > 3 + d = data + if not keyword_set(nosub) then d = d - median_filter(d, patch) + ; extract a sample of uniformly distributed points + if n_elements(point_frac) eq 0 then point_frac = 1 + if point_frac ne 1 then begin + npoints = round(n_elements(d) / float(point_frac)) + s = float(size52(d, /DIM)) + nx = round(sqrt( s[0]/s[1] * npoints )) + ny = round(sqrt( s[1]/s[0] * npoints )) + x = round(sampling_grid(nx, s[0] / nx)) + y = round(sampling_grid(ny, s[1] / ny)) + x = x # (lonarr(ny) + 1) & y = (lonarr(nx) + 1) # y + d = d[x, y] + endif + ; extract data within suitable intensity range + if n_elements(n_std) eq 0 then n_std = 3 + d_med = median(d) & m = moment(d - d_med, SDEV = std) + w = where(abs(d - d_med) lt n_std*std) & d = d[w] + ; compute histogram with suitable bin size + opt_histo, d, h, v, vmean, _EXTRA = extra, $ + REPLACE_MAX = not keyword_set(no_sub) and 1B + ; iterative gaussian fit + if n_elements(maxit) eq 0 then maxit = 10 + if n_elements(tol) eq 0 then tol = 1e-2 + if n_elements(nterms) eq 0 then nterms = 3 + it = 0 & converging = 0B + m = max(h, mode) & mode = v[mode] + std = max([ mode-min(v), max(v)-mode ]) + while not converging and it lt maxit do begin +; w = where(v le mode + std) ; this line to fit left-tail + w = lindgen(n_elements(h)) ; this line to fit all histogram + hfit = gaussfit(vmean[w], h[w], NTERMS = nterms, c) + std0 = std & mode = c[1] & std = abs(c[2]) + converging = abs((std - std0) / std0) lt tol + it = it + 1 + endwhile + if not converging then std = -1 + return +end diff --git a/gaussian2d.pro b/gaussian2d.pro new file mode 100644 index 0000000000000000000000000000000000000000..1a341b8db9bd3baa74c8d07fe86ed37a3ab6ad3c --- /dev/null +++ b/gaussian2d.pro @@ -0,0 +1,67 @@ +; $Id: gaussian2d.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; GAUSSIAN2D +; +; PURPOSE: +; Compute bivariate gaussian function, normalized to unit maximum. +; +; CATEGORY: +; Models. +; +; CALLING SEQUENCE: +; Result = GAUSSIAN2D(X_size, Y_size, X_center, Y_center, $ +; Sigma_x, Sigma_y, Angle) +; +; INPUTS: +; X_size, Y_size: First and second size of output array +; +; X_center, Y_center: Coordinates of center, not necessarily integer +; +; Sigma_x, Sigma_y: Standard deviations of gaussian along principal axes +; +; OPTIONAL INPUTS: +; Angle: Position angle (radians) of the x- principal axis with respect +; to the horizontal axis of the array reference frame. +; The default is Angle = 0., i.e. the gaussian's principal axes are +; parallel to the array edges +; +; OUTPUTS: +; Result: 2D floating-point array +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +FUNCTION gaussian2d, x_size, y_size, x_center, y_center, sigma_x, sigma_y, angle + + on_error, 2 + ; check inputs + s = round([x_size, y_size]) + c = [x_center, y_center] + sigma = [sigma_x, sigma_y] + if n_elements(angle) eq 0 then a = 0. else a = angle + ; check sigma's + min_sigma = 1e-2 + if min(sigma) lt min_sigma then begin + gaussian = fltarr(s[0], s[1]) + gaussian[round(c[0]), round(c[1])] = 1 + return, gaussian + endif + w = sqrt(2)*sigma + if w[0] eq w[1] then a = 0. ; a circular gaussian has no position angle + ; define arrays of x- and y- centered coordinates + x = findgen(s[0]) - c[0] & y = findgen(s[1]) - c[1] + x = temporary(x) # make_array(s[1], VALUE = 1) + y = make_array(s[0], VALUE = 1) # temporary(y) + ; compute x- gaussian + if a ne 0 then $ + z = x * cos(a) + y * sin(a) else z = x + gaussian = exp(-(z/w[0])^2) + ; multiply by y- gaussian + if a ne 0 then $ + z = -x * sin(a) + y * cos(a) else z = y + gaussian = temporary(gaussian) * exp(-(z/w[1])^2) + return, gaussian +end diff --git a/get_max.pro b/get_max.pro new file mode 100644 index 0000000000000000000000000000000000000000..afdefaac140ca4126cd3b5872bf8cd25eb41d037 --- /dev/null +++ b/get_max.pro @@ -0,0 +1,49 @@ +; $Id: get_max.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; GET_MAX +; +; PURPOSE: +; Find coordinates of maximum intensity pixel(s) in a 2D array. +; +; CATEGORY: +; Array manipulation. +; +; CALLING SEQUENCE: +; Result = GET_MAX(Array) +; +; INPUTS: +; Array: 2D array +; +; KEYWORD PARAMETERS: +; ALL: If this keyword is set, all the pixels with maximum intensity +; are returned. Otherwise only the first pixel (according to how IDL +; access arrays in memory address order) is returned. +; +; OUTPUTS: +; Result: If ALL is not set, Result is a 2-components long integer vector, +; with the column and row coordinates of the maximum intensity pixel. +; If ALL is set, Result is a N*2 long integer array, where N (number of +; columns) is the number of maximum intensity pixels: the first row of +; Result will contain the x- coordinates of the pixels, the second row +; of Result will contain the y- coordinates. It N = 1 and ALL is set, +; Result is however a 2-components vector. +; A negative scalar is always returned if Array has not 2 dimensions. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +FUNCTION get_max, array, ALL = all + + on_error, 2 + s = size52(array) + if s[0] ne 2 then return, -1 + if keyword_set(all) then $ + index = where(array ge max(array)) else m = max(array, index) + subs_to_coord, index, s[1], x, y + if n_elements(x) eq 1 then $ + p = [x[0], y[0]] else p = transpose([transpose(x), transpose(y)]) + return, p +end diff --git a/ginv.pro b/ginv.pro new file mode 100644 index 0000000000000000000000000000000000000000..7e9b6a3a2b2844e672bee6d6e5736ead2732867f --- /dev/null +++ b/ginv.pro @@ -0,0 +1,115 @@ +; $Id: ginv.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; GINV +; +; PURPOSE: +; Compute the Moore-Penrose generalized inverse of a rectangular matrix +; by applying the Gram-Schmidt orthogonalization procedure to the columns +; of the input array. +; +; CATEGORY: +; Mathematics. Matrix inversion. +; +; CALLING SEQUENCE: +; Results = GINV(A) +; +; INPUTS: +; A: 2D array to be inverted (not necessarily square) +; +; OUTPUTS: +; Return generalized inverse of input array A. If A has size m*n, its +; generalized inverse will have size n*m. +; If A is a double precision floating-point array, the output is in +; double precision, otherwise it is in single precision +; +; OPTIONAL OUTPUTS: +; Rank: Rank of A, i.e. number of linearly indepent columns +; +; PROCEDURE: +; Apply the recursive Gram-Schmidt orthogonalization procedure to the +; columns of the input matrix A to compute the generalized inverse A'. +; We briefly recall the properties of the Moore-Penrose generalized +; inverse: +; TRANPOSE(A'A) = A'A +; TRANPOSE(AA') = AA' +; AA'A = A +; A'AA' = A' +; The generalized inverse conicides with the ordinary inverse in the +; case of a non-singular square matrix. +; When used to solved a linear system of "normal equations", the +; generalized inverse produces the so-called "minimum norm solution", +; which is particularly meaningful in the case of singular of nearly +; singular linear systems. For more details, see +; B.W.Rust, W.R.Burrus, "Mathematical programming and the numerical +; solution of linear equations", +; American Elsevier Publishing Company, 1972 +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +; Originally written in FORTRAN by S.Lorenzutta +;- + + + + +; GS_ORTH: recursive Gram-Schmidt orthogonalization of a set of n +; m-components vectors. The vectors are stored as a (m*n) array, +; to speed up the computation if virtual memory should be used. + +PRO gs_orth, v, u, lin_indep, n, m, tol + + on_error, 2 + this = n - 1 + if this gt 0 then begin + ; Induction case: apply the G-S orthogonalization to this vector. + ; Orthogonalize the first (n - 1) vectors before + gs_orth, v, u, lin_indep, n - 1, m, tol + norm = total(v[*,this] * v[*,this]) + prod = (v[*,this] # v )[0:this-1] + v[*,this] = v[*,this] - (prod * lin_indep[0:this-1]) # $ + transpose(v[*,0:this-1]) + u[*,this] = u[*,this] - prod # transpose(u[*,0:this-1]) + new_norm = total(v[*,this] * v[*,this]) + is_lin_indep = norm ne 0 + if is_lin_indep then is_lin_indep = new_norm / norm gt tol + if is_lin_indep then begin + ; this vector is linearly independent + lin_indep[this] = 1 & norm = 1 / sqrt(new_norm) + endif else begin + ; this vector is linearly dependent + prod = u[*,this] # u[*,0:this-1] + v[*,this] = - (prod * lin_indep[0:this-1]) # $ + transpose(v[*,0:this-1]) + lin_indep[this] = 0 + norm = 1 / sqrt(total(u[*,this] * u[*,this])) + endelse + endif else begin + ; Base case: one vector to normalize + norm = sqrt(total(v[*,this] * v[*,this])) + if norm ne 0 then begin + norm = 1 / norm & lin_indep[this] = 1 + endif else lin_indep[this] = 0 + endelse + v[*,this] = v[*,this] * norm & u[*,this] = u[*,this] * norm + return +end + + +FUNCTION ginv, a, rank + + on_error, 2 + s = size52(a, /DIM) & n = s[0] & m = s[1] + type_a = size52(a, /TYPE) + a_inv = transpose(double(a)) ; transpose the set of column + ; vectors to speed up the computation if virtual + ; memory should be used + u = dblarr(n, n) & diag = lindgen(n) & u[diag,diag] = 1 + lin_indep = lonarr(n) & tol = 2 * (machar(/DOUBLE)).eps + gs_orth, a_inv, u, lin_indep, n, m, tol + a_inv = temporary(a_inv) # transpose(u) + if type_a lt 5 then a_inv = float(a_inv) + rank = total(lin_indep) + return, a_inv +end diff --git a/halo_smooth.pro b/halo_smooth.pro new file mode 100644 index 0000000000000000000000000000000000000000..88503f30c7bbeed9ef08f8c2cfc9072290098a12 --- /dev/null +++ b/halo_smooth.pro @@ -0,0 +1,141 @@ +; $Id: halo_smooth.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; HALO_SMOOTH +; +; PURPOSE: +; Smooth the halo of a stellar image by means of a variable box size +; median filtering technique. The width of the smoothing box for a +; given pixel increases with the radial distance of the pixel itself +; from the center of the image. +; This routine may be used to smooth the halo of a PSF image, +; exctracted from a stellar field. +; +; CATEGORY: +; Signal processing. +; +; CALLING SEQUENCE: +; Result = HALO_SMOOTH(Image, R0) +; +; INPUTS: +; Image: 2D image to smooth +; +; R0: Scalar, indicates the radius of the region around the Image +; maximum which is not processed by median filtering. +; In practice the halo smoothing starts from this distance. +; +; KEYWORD PARAMETERS: +; R_WIDTH: Use this keyword to specify the radial width of the +; smoothing box at a distance of 2*R0 from the Image maximum. +; The default is R_WIDTH = FLOAT(R0)/2 pixels. +; +; A_WIDTH: Use this keyword to specify the azimuthal width of the +; smoothing box at a distance of 2*R0 from the Image maximum. +; The default is A_WIDTH = !pi/8 rad +; +; R_EXP, A_EXP: The radial and azimuthal widths of the smoothing box +; increase as power-law functions of the radial distance from the +; Image maximum. The keywords R_EXP and A_EXP allow the user to +; specify the power degree. +; The default values are R_EXP = 2, A_EXP = 3. +; +; PAD_0: Set this keyword to a nonzero value to have the Image padded +; with a frame of 0s before smoothing. The use of this strategy may +; prevent the onset of undesired edge effects and force the smoothed +; image to approach 0 at the edge. The default is no padding. +; +; SHOW: Set this keyword to a nonzero value to show the shape and +; area of the smoothing box for each radial distance from the Image +; maximum, along the main diagonal of the input array. +; In practice a binary image is displayed, equal to 1 on the support +; of the smoothing box. The image is displayed on the currently +; active graphic window. +; +; OUTPUTS: +; Result: Halo-smoothed image, with the same size as the input array +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + + + +;;; Auxiliary functions. + +;RAD_WIDTH: compute radial width of smoothing neighborhood for a given pixel. + +FUNCTION rad_width, r, r0, rad_coeff, rad_exp + + return, rad_coeff * (r / (2.* r0)) ^rad_exp +end + +;AZI_WIDTH: compute azimuthal width of smoothing neighborhood for a given pixel. + +FUNCTION azi_width, r, r0, azi_coeff, azi_exp + + return, azi_coeff * (r / (2.* r0)) ^azi_exp +end + + +;;; The main routine. + +FUNCTION halo_smooth, image, r0, PAD_0 = pad_0, SHOW = show, $ + R_WIDTH = rw, A_WIDTH = aw, R_EXP = re, A_EXP = ae + + on_error, 2 + if size52(image, /N_DIM) ne 2 then return, image + siz = size52(image, /DIM) & sx = siz[0] & sy = siz[1] + smooth_image = image + ; Pad with 0s? + if keyword_set(pad_0) then begin + siz = 2 * siz & sx = siz[0] & sy = siz[1] + smooth_image = extend_array(smooth_image, sx, sy) + endif + saved_image = smooth_image + ; Define default width of filtering box at 2*r0 from the center + if n_elements(rw) eq 0 then rw = r0 / 2. + if n_elements(aw) eq 0 then aw = !pi / 8 + if n_elements(re) eq 0 then re = 2 + if n_elements(ae) eq 0 then ae = 3 + rad_coeff = rw & azi_coeff = 2 * r0 * aw + ; For each pixel, compute distance from the center and azimuthal angle + m = get_max(smooth_image) & x0 = m[0] & y0 = m[1] + x = findgen(sx) # make_array(sy, VALUE = 1) + y = make_array(sx, VALUE = 1) # findgen(sy) + r_distance = round(distance(x0, y0, x, y)) + azi = angle(x0, y0, x, y) + outer_rad = max(r_distance) + ; Define parameters for display + if keyword_set(show) then begin + dsiz = min([!D.x_vsize, !D.y_vsize]) * siz/max(siz) + dxy = m[1] - m[0] + endif + ; Iterate on annular regions of increasing radius, from inner to outer + for r = r0, outer_rad do begin + ; Identify pixels at this distance from the center + wr = where(r_distance eq r, nr) + if nr ne 0 then begin + ; Compute radial and azimuthal width of smoothing box + w = rad_width(r, r0, rad_coeff, re) + l = azi_width(r, r0, azi_coeff, ae) / r + rad_range = abs(r_distance - r) le w/2 + for n = 0, nr - 1 do begin + ; Define the smoothing 'box' of each pixel + delta_azi = abs(azi - azi[wr[n]]) + azi_range = delta_azi le l/2 or 2*!pi - delta_azi le l/2 + neigh = where((rad_range and azi_range) and 1B) + if keyword_set(show) then $ + if y[wr[n]] eq (x[wr[n]] + dxy) and x[wr[n]] gt x0 then begin + b = bytarr(sx, sy) & b[neigh] = 1 + tvscl, congrid(b, dsiz[0], dsiz[1]) + endif + ; Compute median of box + smooth_image[x[wr[n]], y[wr[n]]] = median(saved_image[neigh], /EVEN) + endfor + endif + endfor + if keyword_set(pad_0) then $ + smooth_image = sub_array(smooth_image, sx/2, sy/2) + return, smooth_image +end \ No newline at end of file diff --git a/histo.pro b/histo.pro new file mode 100644 index 0000000000000000000000000000000000000000..b38ba3f43036af2a1a4c81cc7d7a5fa147233d7d --- /dev/null +++ b/histo.pro @@ -0,0 +1,62 @@ +; $Id: histo.pro, v 1.1 Jan 2000 e.d. $ +; +;+ +; NAME: +; HISTO +; +; PURPOSE: +; Compute histogram of input data and array of data values +; corresponding to histogram bins. +; +; CATEGORY: +; Mathematics. Statistics. +; +; CALLING SEQUENCE: +; HISTO, Data, Dmin, Dmax, Bin, H, X, Xmean +; +; INPUTS: +; Data: Data to compute histogram (any number of IDL dimensions) +; +; Dmin, Dmax: Min. and max. data value to be considered in the histogram +; +; Bin: Size of histogram bin +; +; OUTPUTS: +; H: Histogram +; +; X: 1D array of data values corresponding to the center of each bin +; +; Xmean: 1D array corresponding to the mean of the data values entering +; each histogram bin +; +; PROCEDURE: +; Compute the histogram with the IDL intrinsic function HISTOGRAM, using +; the input options specified by the parameters Dmin, Dmax, Bin. All +; the computations are performed in floating-point arithmetics. +; Then compute arrays of values corresponding to each histogram bin, +; useful for plots, fitting, etc. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +; Updates: +; 1) long integer loop variable (Emiliano Diolaiti, January 2000). +;- + +PRO histo, data, dmin, dmax, bin, h, x, xmean + + on_error, 2 + ; compute histogram + h = histogram(float(data), BINSIZE = float(bin), MIN = float(dmin), $ + MAX = float(dmax), REVERSE_INDICES = r) + ; compute center of each bin + range = float(dmax - dmin) & nbin = long(range/bin) + 1 + x = findgen(nbin) * bin + dmin + bin/2. + ; compute mean data value for each bin + n_el = n_elements(h) & xmean = fltarr(n_el) + for n = 0L, n_el - 1 do begin + lo = r[n] & up = r[n+1] - 1 + if lo lt up then $ + xmean[n] = mean(data[r[lo:up]]) else xmean[n] = x[n] + endfor + return +end diff --git a/histo_hwhm.pro b/histo_hwhm.pro new file mode 100644 index 0000000000000000000000000000000000000000..0ab27b8dae2402cefdcd2699e5edcc92f9c7988b --- /dev/null +++ b/histo_hwhm.pro @@ -0,0 +1,54 @@ +; $Id: histo_hwhm.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; HISTO_HWHM +; +; PURPOSE: +; Compute the Half Width at Half Maximum (HWHM) of an histogram. +; +; CATEGORY: +; Mathematics. Statistics. +; +; CALLING SEQUENCE: +; Result = HISTO_HWHM(H, Increment, Mode) +; +; INPUTS: +; H: Histogram +; +; Increment: +1 to compute the right tail HWHM, -1 to compute the left +; tail HWHM. If Increment is different from +/-1, the value -1 is +; used by default +; +; OUTPUTS: +; Return the (left- or right- tail) HWHM of the histogram, expressed +; in bin units. +; +; OPTIONAL OUTPUTS: +; Mode: subscript of the most populated bin in the input histogram. +; +; RESTRICTIONS: +; The algorithm assumes there are no secondary maxima in the histogram +; within a distance comparable to the HWHM from the most populated bin. +; Otherwise the estimated HWHM may be overestimated. +; +; PROCEDURE: +; Starting from the most populated bin, move leftwards (rightwards) +; until a bin with intensity equal or smaller than half the intensity +; of the initial bin is found. The distance (in bin units) between this +; bin and the initial one is an estimate of the histogram HWHM. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +FUNCTION histo_hwhm, h, increment, mode + + on_error, 2 + if abs(increment) ne 1 then incr = -1 else incr = increment + s = reverse(sort(h)) & mode = s[0] & threshold = 0.5 * h[mode] + l = mode & l_min = 0 & l_max = n_elements(h) - 1 + while h[l] gt threshold and l gt l_min and l lt l_max do l = l + incr + l = (l - incr) > l_min < l_max + return, abs(mode - l) +end diff --git a/image_background.pro b/image_background.pro new file mode 100644 index 0000000000000000000000000000000000000000..ec6f4af0e263e7f9378337eff23dc85853a9d021 --- /dev/null +++ b/image_background.pro @@ -0,0 +1,113 @@ +; $Id: image_background.pro, v 1.1 Apr 2000 e.d. $ +; +;+ +; NAME: +; IMAGE_BACKGROUND +; +; PURPOSE: +; Compute the intensity distribution of the background in a given image. +; Local measurements of the background are obtained after partitioning the +; input array; the sampled distribution is then magnified onto the same +; grid of the original image by interpolation. +; This method is especially useful when the image background is not +; spatially uniform. +; +; CATEGORY: +; Signal processing. +; +; CALLING SEQUENCE: +; Result = IMAGE_BACKGROUND(Image, Step) +; +; INPUTS: +; Image: 2D array +; +; Step: Size of sub-regions to measure local background +; +; KEYWORD PARAMETERS: +; SKY_MEDBOX: The array of local measurements is smoothed by median +; filtering. Use the keyword SKY_MEDBOX to fix a box size for median +; smoothing. In general, the larger the Step size the smaller should +; be the box for median filtering. The default is SKY_MEDBOX = 3, +; which represents also the minimum box size. +; +; CUBIC: Set this keyword to magnify the sampled background with the +; cubic convolution interpolation method implemented in the library +; routine CONGRID. The default is to use bilinear interpolation as; +; implemented in the IDL intrinsic function REBIN. +; +; OUTPUTS: +; Result: 2D array, having the same size as the input Image. +; +; RESTRICTIONS: +; 1) Bad pixels are assumed to have been corrected. +; 2) The background may be over-estimated in the presence of strong +; sources, even though the present method seems to be less biased +; than median smoothing. +; +; PROCEDURE: +; The input Image is partitioned into sub-frames defined by a rectangular +; grid. The mesh points coincide with the centers of the corresponding +; regions. For each sub-image a local background estimate is obtained by +; means of the IDL library routine SKY, written by W.Landsman and adapted +; from the DAOPHOT routine of the same name. If SKY doesn't converge, the +; local background is estimated as the median value of the sub-image. +; Each local background estimate is associated to the mesh point +; corresponding to the sub-image under examination. The sampled version of +; the background is smoothed by median filtering and magnified onto the +; same grid of the input Image by means of interpolation. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +; Updates: +; 1) Corrected edge effect due to 0-padding +; (Emiliano Diolaiti, October 1999). +; 2) Fixed bug on small arrays (Emiliano Diolaiti, April 2000). +; 3) Introduced error handler to handle run-time error generated by call +; to SKY (Emiliano Diolaiti, April 2000). +; 4) Local background estimate by median (E. D., August 2004) +;- + +FUNCTION image_background, image, sampling_step, $ + SKY_MEDBOX = med_box, CUBIC = cubic + + on_error, 2 + siz = size52(image, /DIM) & sx = siz[0] & sy = siz[1] + step = round(sampling_step) + if n_elements(med_box) ne 0 then box = med_box else box = 3 + box = box > 3 + ; Pad the image with 0s + sx = (sx / step + 1) * step & sy = (sy / step + 1) * step + ima = extend_array(image, sx, sy, OFFSET = off) + lo = off & up = lo + siz - 1 + ; Define the sampling grid + nx = sx / step & ny = sy / step + x = sampling_grid(nx, step, lx0, ux0) & ux0 = ux0 < sx + y = sampling_grid(ny, step, ly0, uy0) & uy0 = uy0 < sy + lx = round(x - step / 2.) > lo[0] & ux = round(x + step / 2.) < up[0] + ly = round(y - step / 2.) > lo[1] & uy = round(y + step / 2.) < up[1] + ; Background sampling + b = fltarr(nx, ny) + for i = 0L, ny - 1 do for j = 0L, nx - 1 do begin + catch, error + if error ne 0 then begin + local_back = median(ima[lx[j]:ux[j],ly[i]:uy[i]], /EVEN) + catch, /CANCEL + endif else begin +; sky, /SILENT, ima[lx[j]:ux[j],ly[i]:uy[i]], local_back, stdev +; if stdev lt 0 then $ + local_back = median(ima[lx[j]:ux[j],ly[i]:uy[i]], /EVEN) + endelse + b[j,i] = local_back + endfor + ; Sampled background smoothing + b = median_filter(b, box) + ; Background interpolation + siz = size52(b, /DIM) * step + if n_elements(b) gt 1 then begin + if keyword_set(cubic) then $ + b = congrid(b, siz[0], siz[1], CUBIC = -0.5) else $ + b = rebin(b, siz[0], siz[1]) + endif else b = make_array(siz, siz, VALUE = b) + b = b[lo[0]:up[0],lo[1]:up[1]] + return, b +end diff --git a/image_core.pro b/image_core.pro new file mode 100644 index 0000000000000000000000000000000000000000..be532967577eb75c028dce5b5a65fce3b6164525 --- /dev/null +++ b/image_core.pro @@ -0,0 +1,62 @@ +; $Id: image_core.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; IMAGE_CORE +; +; PURPOSE: +; Given an image, identify the component connected to a specified +; starting position and above a pre-fixed threshold. +; +; CATEGORY: +; Data subsetting. +; +; CALLING SEQUENCE: +; Result = IMAGE_CORE(Image, Threshold, Flag) +; +; INPUTS: +; Image: 2D array to be searched +; +; Threshold: Lower threshold for connected component identification +; +; KEYWORD PARAMETERS: +; X, Y: Use this keywords to specify the starting position. +; The default starting point is the maximum intensity pixel +; +; SUBTRACT: Set this keyword to a nonzero value to subtract the +; value set by Threshold after extraction of the connected component +; +; OUTPUTS: +; Result: 2D array, with the same size as the input Image, containing +; the extracted connected component. Return the input Image if an +; error occurs in SEARCH2D. +; +; OPTIONAL OUTPUTS: +; Flag: Logical value. It is set to true if the input Threshold is +; greater than the intensity of the starting pixel specified by +; the keywords X and Y. +; +; PROCEDURE: +; Use the library routine SEARCH2D to identify the component connected +; to the starting position and above the fixed Threshold. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +FUNCTION image_core, image, threshold, X = x, Y = y, SUBTRACT = subtract, flag + + on_error, 2 + flag = 0B + if n_elements(x) eq 0 or n_elements(y) eq 0 then begin + m = get_max(image) & x = m[0] & y = m[1] + endif + s = size52(image, /DIM) + ccc = make_array(s[0], s[1], TYPE = size52(image, /TYPE)) + flag = threshold gt image[x,y] + if flag then return, image + w = search2d(image, x, y, threshold, max(image)) + ccc[w] = image[w] + if keyword_set(subtract) then ccc[w] = ccc[w] - threshold + return, ccc +end diff --git a/image_help.txt b/image_help.txt new file mode 100644 index 0000000000000000000000000000000000000000..be5d73ba7995c6da45d7547891e78c53002570a4 --- /dev/null +++ b/image_help.txt @@ -0,0 +1,36 @@ + 'Image' menu help page + + + The 'Image' menu contains the following sub-menus: + + 'Load': + Load an image and/or the background emission from a FITS file. + + 'Noise': + Estimate the standard deviation of the noise in each pixel of + the image. The noise array can also be load from a FITS file. + + 'Bad Pixels': + Load a bad pixels mask from a FITS file and replace bad pixels + in the image. This task might be used to just read bad data + points without replacing them. + + 'Reference sources': + Select reference stars in the currently displayed image by + mouse click. The selected sources can be saved to a file and + used for instance to compare different lists of stars (see the + 'Compare Lists' button). + + 'Save': + Save to a FITS file the following items: + - processed image (e.g. after bad pixels repair) + - background array (after PSF extraction or stars detection) + - detected stars and synthetic stellar field + (after stars detection) + Save to an ASCII file the following item: + - list of detected stars, with position, flux, formal errors + and correlation coefficient. This operation may be done + also before quitting the stars detection widget application + (see 'Astrometry and Photometry' button in the main widget + on-line help). + diff --git a/image_model.pro b/image_model.pro new file mode 100644 index 0000000000000000000000000000000000000000..11c84b70747da180f4d18ac2aa18c4878a24c3e5 --- /dev/null +++ b/image_model.pro @@ -0,0 +1,437 @@ +; $Id: image_model.pro, v 2.4 May 2014 e.d. $ +; +;+ +; NAME: +; IMAGE_MODEL +; +; PURPOSE: +; Create a synthetic image given by a sum of shifted scaled replicas +; of a PSF. The PSF may either be a replica of the input template +; ("fixed PSF" option) or may be extracted from a set of local PSFs +; ("space-variant PSF" option) or computed for each location, according +; to some user-defined model ("PSF model" option). +; +; CATEGORY: +; Models. +; +; CALLING SEQUENCE: +; Result = IMAGE_MODEL(X, Y, F, X_size, Y_size, Psf, Data) +; +; INPUTS: +; X, Y: Vectors of stars positions +; +; F: Vector of stellar fluxes +; +; X_size, Y_size: First and second size of the output array +; +; Psf: When the "fixed PSF" option is used, Psf must be a 2D array +; containing the image of the PSF to be replicated in the output image +; model. +; When the "space-variant PSF" option is used, Psf must be a 3D stack +; of PSF images. In this case it is necessary to supply the bounds of +; image domain partition (see KEYWORDS LX, UX, LY, UY). +; When the "PSF model" option is used, Psf must be a string parameter +; with the name of the function used to computer the desired PSF model. +; The following PSF models are available: +; - Sum of Moffat functions elongated to a common point; the width in the +; elongated axis is described by a polynomial function. +; Set Psf = "mm_psf" +; - Airy diffraction pattern. Set Psf = "airy" +; - Resampled version of an input numerical PSF. Set Psf = "resampled". This +; model may be useful if a PSF is known on a fine pixel grid and it has to +; be resampled on a coarser grid. +; If the "PSF model" option is applied, the user may have to supply +; additional information with the parameter Data (see OPTIONAL INPUTS), +; depending on how the procedure which actually computes the PSF model +; is defined. +; +; OPTIONAL INPUTS: +; Data: Use this variable to provide a pointer to the additional information +; required by the "model" option. +; In general the heap variable pointed by Data is a structure, containing +; miscellaneous information. +; For the supported model types, the structure pointed by Data must be +; defined as follows: +; 1) Psf type = 'mm_psf' +; *Data = {Xref: xref, Yref: yref, Xori: xori, Yori: yori, $ +; C: c, Pow: k, Ncomp: ncomp, Nparcomp: nparcomp, $ +; X_size: x_size, Y_size: y_size} +; where +; Xref, Yref: (float) coordinates of reference position in the image +; Xori, Yori: (float) coordinates of pixel [0,0] of PSF model array with respect to +; image array +; C: (float) array of coefficients defining the multi-Moffat PSF model and its variation +; across the image. For more details, see 'mm_psf_setup.pro' +; Pow: vector with power law of Moffat functions +; Ncomp: number of Moffat components in the model +; Nparcomp: number of parameters per component (3) +; X_size, Y_size: size of the PSF model array +; 2) Psf type = 'airy' +; *Data = {X_size: x_size, Y_size: y_size, Sampling_factor: sampling_factor}, +; where +; X_size, Y_size: size of the PSF model array +; Sampling_factor: ratio of the actual sampling step to the critical step size +; 3) Psf type = 'resampled_psf' +; *Data = {X_size0: x_size0, Y_size0: y_size0, X_size: x_size, Y_size: y_size, $ +; Psf: psf, Xpsf: xpsf, Ypsf: ypsf} +; where +; X_size0, Y_size0: integer scalars, X and Y size of given PSF +; X_size, Y_size: integer scalars, X and Y size of resampled PSF +; Psf: 2D or 3D cube of PSFs; size x_size0*y_size0*npsf, where npsf is the number +; of input PSFs +; Xpsf, Ypsf: vectors of npsf elements representing the X and Y positions of the +; PSFs in the psf cube. +; +; When the "fixed PSF" options is chosen, the variable Data may also be +; used to provide additional information for the Psf shift, which has +; been released on output in a previous call to IMAGE_MODEL (see OPTIONAL +; OUTPUTS below). +; The parameter Data must NOT be supplied when a stack of space-variant +; PSFs is supplied. +; +; KEYWORD PARAMETERS: +; XPSF, YPSF: Vectors specifying the positions of the local PSFs in +; the image when the "space-variant PSF" option is used. +; +; LX, UX, LY, UY: Vectors specifying the bounds of the image domain +; partition when the "space-variant PSF" option is used. In this case +; the sub-domain [LX[j]: Ux[j], LY[i]: UY[i]] must correspond to the +; (i * X_size + j)-th PSF in the input stack. +; +; INTERP_TYPE: Set this keyword to a string identifying one of the +; interpolation techniques supported by the function IMAGE_SHIFT +; (for more details see the file 'image_shift.pro'). This keyword +; is neglected if the "PSF model" option is used. +; +; REFERENCE_PIX: Set this keyword to a two-elements integer vector with +; the coordinates of the reference pixel in the Psf array which must +; be placed at the positions (X, Y) in the output array. +; The default is the Psf maximum. +; +; OUTPUTS: +; Result: 2D array containing the synthetic model +; +; OPTIONAL OUTPUTS: +; Data: The heap variable pointed by Data may be modified by the +; procedures called by IMAGE_MODEL to shift the input Psf ("input PSF" +; option) or to compute a PSF model ("PSF model" option). +; This pointer can be used as a useful input/output variable to +; provide or retrieve information for subsequent calls to IMAGE_MODEL. +; Let us consider the following example: suppose IMAGE_MODEL is +; called the first time with the heap variable (*Data) undefined and +; using the "input PSF" option. The pointer variable Data released on +; output will reference an anonymous structure (created by IMAGE_SHIFT) +; with useful information which can be recycled in a further call to +; IMAGE_MODEL (provided all the options are the same). +; +; RESTRICTIONS: +; The "fixed" and "space-variant" PSF options are suited to a well sampled +; PSF: in this case it is possible to interpolate the template when a +; fractional shift is required for sub-pixel positioning. When the data are +; sub-sampled, interpolation errors may occur. In this case a PSF model should +; be used instead, if available (with the "PSF model" option). +; +; PROCEDURE: +; For each input position and stellar flux, put one PSF in the output image. +; The PSF may be simply a replica of the input template or may be extracted +; from an stack of local PSFs or even computed for each location according to +; a given model. In the first two cases, sub-pixel positioning is performed +; by interpolating the input PSF (see the function IMAGE_SHIFT in the file +; 'image_shift.pro' for more details). +; If the user wishes to define new model options, he/she must write a new +; procedure according to the following template: +; +; PRO model_psf, X, Y, Aux, Psf, Reference_pixel +; dx = X - round(X) & dy = Y - round(Y) +; "function call" to define the PSF model, +; having its maximum at (x_size/2,y_size/2) +; Psf = Psf / total(Psf) +; Reference_pixel = [Aux.x_size, Aux.y_size] / 2 +; return +; end +; +; where X and Y represent the location of a star in the image, Aux is a +; structure passed to IMAGE_MODEL through the pointer Data (see OPTIONAL +; INPUTS above), Psf is the output PSF model and Reference_pixel will be +; used by IMAGE_MODEL to position the computed PSF in the output array. +; The line "function call" in the above template is a call to some +; procedure/function which actually computes the PSF model. The maximum +; intensity pixel should lie at (x_size/2,y_size/2). For more details, +; see the procedures 'gaussian_psf' and 'airy_psf' in this file. +; +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +; Updates: +; 1) Added REFERENCE_PIX keyword (Emiliano Diolaiti, December 1999) +; 2) Space-Variant PSF option (Emiliano Diolaiti, January 2000) +; 3) Added MODEL keyword (Emiliano Diolaiti, December 2000) +; 4) Space-Variant PSF option: fixed error in call to PICK_REGION +; and adjusted call to IMAGE_SHIFT (Emiliano Diolaiti, December 2004) +; 5) Added "multigaussian_psf" and "powergau_psf" options +; (E. D., January 2005) +; 6) Added keywords XPSF, YPSF for space-variant option +; (E. D., May 2008) +; 7) Added MOFFAT_PSF (E. D., June 2008) +; 8) Added RESAMPLED_PSF (E. D., May 2009) +; 9) Removed "CASE" instruction in main routine. Now the value of the input +; Psf in the "PSF model" option must be equal to the name of the function +; used to compute the PSF model (E. D., January 2012). +; 10) Updated multi-Moffat PSF function, now called MMR_PSF (E. D., January 2012). +; 11) Updated multi-Moffat PSF function, now called MM_PSF and temporarly removed +; other PSF models that need verification and documentation (moved to block +; comment at the end of the file) (E. D., March 2012). +; 12) Updated documentation (E. D., May 2014). +;- + + +;;; Auxiliary procedures to compute PSF model. + + +; MM_PSF: Sum of N Moffat functions elongated to a common point. +; All Moffat functions have same center and position angle. + +PRO mm_psf, x, y, data, psf, ref_pix + + on_error, 2 + xglo = x + data.xori + yglo = y + data.yori + dx = x - round(x) & dy = y - round(y) + ref_pix = [data.x_size/2, data.y_size/2] + psf = fltarr(data.x_size, data.y_size) + r = distance(data.xref, data.yref, xglo, yglo) + if xglo ne data.xref then $ + tilt = atan(float(yglo - data.yref) / float(xglo - data.xref)) else tilt = !pi/2 + par = fltarr(data.nparcomp, data.ncomp) + for k = 0, data.ncomp-1 do $ + for j = 0, data.nparcomp-1 do $ + for d = 1, round(data.c[0,j,k])+1 do par[j,k] = par[j,k] + data.c[d,j,k] * r^(d-1) + for k = 0, data.ncomp-1 do $ + psf = temporary(psf) + par[0,k] * $ + moffat2d(data.x_size, data.y_size, ref_pix[0] + dx, ref_pix[1] + dy, $ + par[1,k], par[2,k], tilt, POWER = data.pow[k], /NORMAL) + + return +end + +; AIRY_PSF: Airy diffraction pattern. + +PRO airy_psf, x, y, data, psf, ref_pix + + on_error, 2 + dx = x - round(x) & dy = y - round(y) + psf = airy_pattern(data.x_size, data.y_size, $ + data.x_size/2+dx, data.y_size/2+dy, data.sampling_factor) + psf = psf / total(psf) + ref_pix = [data.x_size, data.y_size] / 2 + return +end + +; RESAMPLED_PSF: resample given PSF to desired size. + +PRO resampled_psf, x, y, data, psf, ref_pix + + on_error, 2 + dx = x - round(x) & dy = y - round(y) + dx = dx * float(data.x_size0) / float(data.x_size) + dy = dy * float(data.y_size0) / float(data.y_size) + m = min(distance(x, y, data.xpsf, data.ypsf), w) + psf = image_shift(data.psf[*,*,w], dx, dy) + norm = (float(data.x_size0) / data.x_size) * (float(data.y_size0) / data.y_size) + psf = norm * congrid(psf, data.x_size, data.y_size, CUBIC = -0.5) + ref_pix = [data.x_size, data.y_size] / 2 + return +end + +; TEMPLATE_PSF: template procedure for user-written PSF model procedures. + +PRO template_psf, x, y, data, psf, ref_pix + + on_error, 2 + dx = x - round(x) & dy = y - round(y) +; psf = "function call" + psf = psf / total(psf) + ref_pix = [data.x_size, data.y_size] / 2 + return +end + + + +;;; The main routine. + +FUNCTION image_model, x, y, f, x_size, y_size, psf, data, $ + REFERENCE_PIX = psf_ref_pix, _EXTRA = extra, $ + LX = lx, UX = ux, LY = ly, UY = uy, $ + XPSF = xpsf, YPSF = ypsf, MODEL = image + + on_error, 2 + ; Define output image model + if n_elements(image) eq 0 then image = fltarr(x_size, y_size) + nstar = n_elements(f) + ; Define PSF option ("fixed" or "space variant" or "model") + fixed_psf = size52(psf, /N_DIM) eq 2 + space_var = not fixed_psf + if not fixed_psf then begin + space_var = size52(psf, /N_DIM) eq 3 + if space_var then $ + space_var = (n_elements(lx) ne 0 and n_elements(ux) ne 0 and $ + n_elements(ly) ne 0 and n_elements(uy) ne 0) or $ + (n_elements(xpsf) ne 0 and n_elements(ypsf) ne 0) + endif + if fixed_psf or space_var then begin + psf_size = (size52(psf, /DIM))[0:1] + if n_elements(psf_ref_pix) eq 0 then $ + psf_ref_pix = get_max(psf[*, *, 0]) + endif else $ + psf_pro = strlowcase(psf) + ; If there are some additional data defined, de-reference them + if ptr_valid(data) then $ + if n_elements(*data) ne 0 then aux = *data + ; Compute image model + for n = 0L, nstar - 1 do begin + ix = round(x[n]) & iy = round(y[n]) + if fixed_psf then $ + psf_xy = image_shift(psf, x[n] - ix, y[n] - iy, _EXTRA = extra, aux) $ + else if space_var then $ + psf_xy = image_shift(psf[*,*,pick_region(lx, ux, ly, uy, x[n], y[n])], $ + x[n] - ix, y[n] - iy, _EXTRA = extra) $ + else $ + call_procedure, psf_pro, x[n], y[n], aux, psf_xy, psf_ref_pix + add_overlap, image, f[n] * psf_xy, [ix, iy], psf_ref_pix + endfor + ; Update additional data if necessary + if ptr_valid(data) then $ + if n_elements(aux) ne 0 then *data = aux + return, image +end + + + + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; MGR_PSF: Sum of N Gaussian functions elongated to a common point. +; All Gaussian functions have same center and position angle. + +;PRO mgr_psf, x, y, data, psf, ref_pix +; +; on_error, 2 +;; data.xglo = x + data.xori +;; data.yglo = y + data.yori +; xglo = x + data.xori +; yglo = y + data.yori +; dx = x - round(x) & dy = y - round(y) +; ref_pix = [data.dim/2, data.dim/2] +; psf = fltarr(data.dim, data.dim) +; r = distance(data.xref, data.yref, xglo, yglo) +; if x ne data.xref then $ +; tilt = atan(float(yglo - data.yref) / float(xglo - data.xref)) else tilt = !pi/2 +; npc = 3 +; par = fltarr(npc * data.ncomp) +; for k = 0, data.ncomp-1 do begin +; j = npc * k +; for d = 1, round(data.m[0, k]) + 1 do par[j] = par[j] + data.m[d, k] * r^(d-1) +; for d = 1, round(data.rx[0, k]) + 1 do par[j+1] = par[j+1] + data.rx[d, k] * r^(d-1) +; for d = 1, round(data.ry[0, k]) + 1 do par[j+2] = par[j+2] + data.ry[d, k] * r^(d-1) +; endfor +; k = lindgen(data.ncomp) * npc +; par[k] = par[k] / total(2 * !pi * par[k] * par[k+1] * par[k+2]) +; for k = 0, data.ncomp-1 do begin +; j = npc * k +; psf = temporary(psf) + par[j] * $ +; gaussian2d(data.dim, data.dim, ref_pix[0] + dx, ref_pix[1] + dy, $ +; par[j + 1], par[j + 2], tilt) +; endfor +; +; return +;end + + +;;;;; GAUSSIAN_PSF: 2D (elliptical) gaussian PSF. +;;;; +;;;;PRO gaussian_psf, x, y, data, psf, ref_pix +;;;; +;;;; on_error, 2 +;;;; dx = x - round(x) & dy = y - round(y) +;;;; psf = gaussian2d(data.x_size, data.y_size, $ +;;;; data.x_size/2+dx, data.y_size/2+dy, $ +;;;; data.sigma_x, data.sigma_y, data.angle) +;;;; psf = psf / total(psf) +;;;; ref_pix = [data.x_size, data.y_size] / 2 +;;;; return +;;;;end + +;;;;;; MULTIGAUSSIAN_PSF: Sum of 2D gaussians. +;;;;;; All gaussians have same center and position angle. +;;;;; +;;;;;PRO multigaussian_psf, x, y, data, psf, ref_pix +;;;;; +;;;;; on_error, 2 +;;;;; dx = x - round(x) & dy = y - round(y) +;;;;; ref_pix = [data.x_size/2, data.y_size/2] +;;;;; npar = data.ngau * data.npar_gau + 1 +;;;;; psf = fltarr(data.x_size, data.y_size) +;;;;; for g = 0, data.ngau-1 do begin +;;;;; k = g * data.npar_gau +;;;;; psf = temporary(psf) + data.p[k] * $ +;;;;; gaussian2d(data.x_size, data.y_size, ref_pix[0] + dx, ref_pix[1] + dy, $ +;;;;; data.p[k + 1], data.p[k + 2], data.p[npar - 1]) +;;;;; endfor +;;;;; return +;;;;;end +;;;;; +;;;;;; MULTIGAUSSIAN_PSF: Sum of 2D gaussians. +;;;;;; Each gaussian may have different center and position angle. +;;;;; +;;;;;PRO multigaussian_psf_, x, y, data, psf, ref_pix +;;;;; +;;;;; on_error, 2 +;;;;; psf = fltarr(data.x_size, data.y_size) +;;;;; dx = x - round(x) & dy = y - round(y) +;;;;; for g = 0, data.ngau-1 do begin +;;;;; k = g * data.npar_gau +;;;;; psf = temporary(psf) + data.p[k] * $ +;;;;; gaussian2d(data.x_size, data.y_size, $ +;;;;; data.x_size/2 + data.p[k + 3] + dx, data.y_size/2 + data.p[k + 4] + dy, $ +;;;;; data.p[k + 1], data.p[k + 2], data.p[k + 5]) +;;;;; endfor +;;;;; ref_pix = [data.x_size/2, data.y_size/2] +;;;;; return +;;;;;end +;;;;; +;;;;;; POWERGAU_PSF: Gaussian with power profile. +;;;;; +;;;;;PRO powergau_psf, x, y, data, psf, ref_pix +;;;;; +;;;;; on_error, 2 +;;;;; psf = fltarr(data.x_size, data.y_size) +;;;;; dx = x - round(x) & dy = y - round(y) +;;;;; psf = powergau(data.x_size, data.y_size, data.x_size/2 + dx, data.y_size/2 + dy, $ +;;;;; data.p[0], data.p[1], data.p[2], data.p[3]) +;;;;; psf = psf / total(psf) +;;;;; ref_pix = [data.x_size/2, data.y_size/2] +;;;;; return +;;;;;end +;;;;; +;;;;;; MULTIPOWERGAU_PSF: Sum of 2D gaussians with power profile. +;;;;;; All gaussians have same center and position angle. +;;;;; +;;;;;PRO multipowergau_psf, x, y, data, psf, ref_pix +;;;;; +;;;;; on_error, 2 +;;;;; dx = x - round(x) & dy = y - round(y) +;;;;; ref_pix = [data.x_size/2, data.y_size/2] +;;;;; npar = data.ngau * data.npar_gau + 2 +;;;;; phi = data.p[npar - 2] +;;;;; pow = data.p[npar - 1] +;;;;; psf = fltarr(data.x_size, data.y_size) +;;;;; for g = 0, data.ngau-1 do begin +;;;;; k = g * data.npar_gau +;;;;; psf = temporary(psf) + data.p[k] * $ +;;;;; powergau(data.x_size, data.y_size, ref_pix[0] + dx, ref_pix[1] + dy, $ +;;;;; data.p[k + 1], data.p[k + 2], phi, pow) +;;;;; endfor +;;;;; psf = psf / total(psf) +;;;;; return +;;;;;end diff --git a/image_shift.pro b/image_shift.pro new file mode 100644 index 0000000000000000000000000000000000000000..b6a491dde675eb658cc73dba0ae356819f5e4a08 --- /dev/null +++ b/image_shift.pro @@ -0,0 +1,121 @@ +; $Id: image_shift.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; IMAGE_SHIFT +; +; PURPOSE: +; Interpolate a 2D image in order to perform a fractional +; shift of the origin. +; +; CATEGORY: +; Mathematics. Interpolation. +; +; CALLING SEQUENCE: +; Result = IMAGE_SHIFT(Image, X_shift, Y_shift, Data) +; +; INPUTS: +; Image: 2D array to be shifted +; +; X_shift, Y_shift: Components of the fractional shift +; +; OPTIONAL INPUTS: +; Data: See OPTIONAL OUTPUTS +; +; KEYWORD PARAMETERS: +; INTERP_TYPE: Use this keyword to choose an interpolation technique. +; The supported options are +; 'F': interpolation by Fourier transform shift +; 'S': interpolation by spline functions +; 'I': interpolation by the IDL function INTERPOLATE with keyword_set CUBIC = -0.5 (default) +; 'B': interpolation by the IDL function INTERPOLATE +; +; OPTIONAL OUTPUTS: +; Data: Structure containing useful information to be used on input +; if another shift of the same Image array must be performed. +; This allows the user to save some computation time +; +; RESTRICTIONS: +; 1) Interpolation is suited to well-sampled data. For undersampled images, +; other techiques should be used. For this purpose, see the function +; STARS in the file 'stars.pro'. +; 2) This routine may perform a fractional shift, i.e. abs(X_shift) <= 0.5, +; abs(Y_shift) <= 0.5. When the fractional shift exceeds 0.5 pixels, strong +; edge effects might occur. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +FUNCTION image_shift, image, x_shift, y_shift, INTERP_TYPE = interp_type, data + +; on_error, 2 + if x_shift eq 0 and y_shift eq 0 then return, image + ; is the variable data defined? + no_data = n_tags(data) eq 0 + ; some operations if the auxiliary data are undefined + if no_data then begin + ; define interpolation type to use + if n_elements(interp_type) eq 0 then interp_type = 'I' + interp = strupcase(strmid(interp_type, 0, 1)) + if interp ne 'F' and interp ne 'S' and interp ne 'I' and interp ne 'B' then interp = 'I' + if interp eq 'S' and strlen(interp_type) eq 7 then $ + degree = fix(strmid(interp_type, 6, 1)) else degree = 3 + ; extend image to prevent edge effects + siz = size52(image, /DIM) & extsiz = siz + 2 + imag = extend_array(image, extsiz[0], extsiz[1], OFFSET = offset) + if interp eq 'F' then begin + imag = extend_array(imag, sx, sy, /POW2, OFFSET = add_off) + extsiz = [sx, sy] & offset = offset + add_off + endif + lo = offset & up = lo + siz - 1 + endif else interp = data.interp_type + + ; interpolate + case interp of + + 'S': begin ; SPLINE + if no_data then begin + spline_coeff, imag, DEGREE = degree, $ + coefficients, x_knots, y_knots, x, y + data = {coefficients: coefficients, $ + x_knots: x_knots, y_knots: y_knots, $ + x: x, y: y, degree: degree, lo: lo, up: up, $ + interp_type: interp} + endif + imag = spline_interp(data.coefficients, data.x_knots, $ + data.y_knots, DEGREE = data.degree, $ + data.x - x_shift, data.y - y_shift) + end + + 'F': begin ; Fourier Transform + if no_data then $ + data = {image_ft: fft(imag), extsiz: extsiz, $ + lo: lo, up: up, interp_type: interp} + phi = phase2d(x_shift, y_shift, data.extsiz[0], data.extsiz[1]) + imag = float(fft(data.image_ft * phi, /INVERSE)) + end + + 'I': begin ; IDL INTERPOLATE + if no_data then $ + data = {image: imag, $ + x: findgen(extsiz[0]), y: findgen(extsiz[1]), $ + lo: lo, up: up, interp_type: interp} + imag = interpolate(data.image, data.x - x_shift, data.y - y_shift, $ + /GRID , CUBIC = -0.5, MISSING = 0.) + end + + 'B': begin ; IDL INTERPOLATE + if no_data then $ + data = {image: imag, $ + x: findgen(extsiz[0]), y: findgen(extsiz[1]), $ + lo: lo, up: up, interp_type: interp} + imag = interpolate(data.image, data.x - x_shift, data.y - y_shift, $ + /GRID , MISSING = 0.) + end + + + endcase + + return, imag[data.lo[0]:data.up[0],data.lo[1]:data.up[1]] +end diff --git a/instr_noise.pro b/instr_noise.pro new file mode 100644 index 0000000000000000000000000000000000000000..1c1b6d6f31defd943360a47deab2802b9e210838 --- /dev/null +++ b/instr_noise.pro @@ -0,0 +1,52 @@ +; $Id: instr_noise, v 1.0 May 2000 e.d. $ +; +;+ +; NAME: +; INSTR_NOISE +; +; PURPOSE: +; Evaluate the overall standard deviation of the normally-distributed noise +; in an image, given by the sum or the mean of many exposures. +; Normally-distributed noise sources are read-out, dark current, thermal +; background and sky background. +; Most of these sources are instrumental. +; +; CATEGORY: +; Signal processing. +; +; CALLING SEQUENCE: +; Result = INSTR_NOISE(Ron, Dark, Therm, Sky, El_per_adu, Nexp) +; +; INPUTS: +; Ron: Read-out-noise standard deviation in electron (el.) units +; +; Dark: Dark current level in el. +; +; Therm: Thermal background in el. +; +; Sky: Sky background in el. +; +; El_per_adu: Conversion constant from el. to ADU +; (No. of ADU = No. of el. / El_per_adu) +; +; Nexp: Number of exposures (with the same exposure time) +; +; KEYWORD PARAMETERS: +; AVG: Set this keyword to specify that the image is the average of +; Nexp exposures. If AVG is not set, the routine assumes the image +; is the sum of Nexp exposures. +; +; OUTPUTS: +; Result: Scalar, representing the noise standard deviation in ADU +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, May 2000. +;- + + +FUNCTION instr_noise, ron, dark, therm, sky, el_per_adu, nexp, AVG = avg + + on_error, 2 + norm = 1. & if keyword_set(avg) then norm = 1. / nexp + return, norm / el_per_adu * sqrt(nexp * (float(ron)^2 + dark + therm + sky)) +end diff --git a/list_of_modules.txt b/list_of_modules.txt new file mode 100644 index 0000000000000000000000000000000000000000..4ec80cd2b24a55f5b1a34df82e14344b76676e8f --- /dev/null +++ b/list_of_modules.txt @@ -0,0 +1,389 @@ +StarFinder v 1.8.2a: list of modules + + +ADD_OVERLAP +Find and add the overlap region of two 2D arrays. + +ADD_SUBSCRIPT +STARFINDER auxiliary routine to handle internal data structure. + +AIRY_PATTERN +Compute an Airy pattern. + +ALL_MAX +Find relative maxima in a 2D array. + +ANGLE +Compute the position angles of a set of points on a plane. + +ARRAY_OVERLAP +Find bounds of overlap region of two 2D arrays. + +ARRAY_PARTITION +Define bounds to partition an array into a pre-fixed +number of parts along one dimension. + +B_SPLINES +Compute the 1-D observation matrix to determine the +coefficients of an interpolating spline in the B-splines +representation. + +BINARY_ARRAY +Transform a 2D array to binary by thresholding. + +CENTER_ARRAY +Shift a 2D array in order to put its maximum at a given +specified position. + +CENTROID +Compute the centroid of a 2D array. + +CENTROIDER +Sub-pixel centering of an image by iterative fractional +shift. + +CHECK_BORDER +Check overlapping region of two 2D arrays. + +CIRC_MASK +Apply a circular mask to a 2D array. + +CLICK_ON_MAX +Select local maxima in an image by mouse click. + +COMPARE_LISTS +Find coincidences between two sets of points on a plane. + +CONVERGENCE +Check convergence condition between two variables. + +COORD_TO_SUBS +Convert pixel coordinates in a 2D array to subscripts. + +CORRELATION_COEFF +Compute the correlation coefficient of two 2D patterns. + +CORRELATE_MAX +Estimate the position of a feature in an image by +maximizing its correlation with a reference template. + +CREATE_ELEMENT +STARFINDER auxiliary routine to handle internal data structure. + +CROSSES +Mark interesting points in an image with crosses. + +DEFAULT_DISPLAY_OPT +Define default options to display a 2D image. + +DELETE_ELEMENT +STARFINDER auxiliary routine to handle internal data structure. + +DIAG_MULT +Multiply a square array by a diagonal matrix. + +DISPLAY_IMAGE +Display a 2D image according to a set of options. + +DISTANCE +Compute the euclidean distance of a set of points on a +plane from an origin. + +ESTIMATE_BACKGROUND +Estimate the background emission in an image by means +of IMAGE_BACKGROUND or MEDIAN_FILTER. + +EXTEND_ARRAY +Pad 2D array with 0s. + +EXTEND_SHIFT +Shift a 2D array without edge effects. + +EXTRACT_OVERLAP +Extract the overlap region of two 2D arrays. + +EXTRACT_STARS +STARFINDER auxiliary routine to handle internal data structure. + +FILE_NAME +Return the complete name of a file in a directory +whose path is among the IDL search paths. +Operating system dependencies are taken into account. + +FIND_ROT_TRANS +Find translation and rotation angle between two reference +frames, given the coordinates of a set of points in the +two reference frames. + +FITSTARS +Multi-component PSF-fitting of one or more stellar images. + +FITTING_ERRORS +Estimate formal errors on the solution of a linear +algebraic system. + +FREQUENCY +Compute a 1D vector of Fourier frequencies. + +FWHM +Compute the FWHM of a peak in an image. + +GAUSS_NOISE_STD +Compute standard deviation of normally distributed +noise in an image. + +GAUSSIAN2D +Compute bivariate gaussian function. + +GET_MAX +Find coordinates of maximum intensity pixel in a +2D array. + +GINV +Compute the Moore-Penrose generalized inverse of a +rectangular matrix. + +HALO_SMOOTH +Smooth the halo of a stellar image by means of a +variable box size median filtering technique. + +HISTO +Compute histogram. + +HISTO_HWHM +Compute the Half Width at Half Maximum of an histogram. + +IMAGE_BACKGROUND +Compute the background emission in a given image. + +IMAGE_CORE +Identify the component connected to a specified +starting position and above a pre-fixed threshold +in a 2D array. + +IMAGE_MODEL +Create a synthetic image given by a sum of shifted +scaled replicas of a given template. + +IMAGE_SHIFT +Apply a fractional shift to an image by interpolation. + +INSTR_NOISE +Evaluate overall standard deviation due to normally +distributed noise sources in an image. + +LOG2 +Compute the base 2 logarithm of an integer number. + +LS_SYS +Put a linear algebraic system into normal form. + +MATCH_COORD +Given two sets of coordinates on a plane, representing +the same points in reciprocally translated and rotated +reference frames, map one of them onto the other. + +MAX_SEARCH +Identify relative maxima in a 2D array, according to a +specified set of options. + +MEDIAN_FILTER +Extension of the IDL function MEDIAN: apply median +smoothing to a 2D array, using a reduced box size at the +array edge. + +MERGE_LIST +STARFINDER auxiliary routine to handle internal data structure. + +MIN_NORM_INVERSION +Compute the minimum norm solution of an algebraic system +of linear equations. + +MM_PSF_SETUP +Define auxiliary data of multi-Moffat parametric PSF model +used by "starfinder.pro". + +MMFIT +Fit 2D data array with parametric model made of a sum of +Moffat functions. + +MOFFAT2D +Define a 2-dim Moffat function. + +NEWTON_GAUSS +Fit a set of data with a parametric model. + +PEAK_AREA +Estimate the area of a peak in an image. + +PEAK_FWHM +Compute the FWHM of a peak in an image. + +PEAK_WIDTH +Estimate the width of a peak in an image. + +PHASE2D +Compute the phase array for a 2D delta-function. + +PHOTON_NOISE +Evaluate photon noise standard deviation in an image. + +PICK_REGION +Given a partition of a planar domain, find the +sub-region containing an assigned point. + +PLANE +Compute a plane surface. + +PSF_EXTRACT +Estimate the PSF in a stellar field image by combination +of a stack of stars. + +RADIAL_DIST +Compute a 2D array of radial distances from an origin. + +READ_FLOAT_DATA +Read a Free Format file containing floating-point +numbers, ordered by columns. + +RECIPROCAL_DISTANCE +Compute the euclidean distance for each couple of +points in a set of given points on a plane. + +RELATIVE_ERROR +Compute the relative error between two variables. + +REMOVE_COINCIDENT +Remove multiple occurences from a set of points on +a plane. + +REPAIR_SATURATED +Repair saturated stars in an stellar field image. + +REPLACE_PIX +Replace known bad pixels in a 2D array by local median. + +RESAMPLE +Resample a bivariate function defined on a rectangular +domain by integrating it over square regions. + +REVERSE_CLASS +STARFINDER auxiliary routine to handle internal data structure. + +ROT_TRANS +Rotate and translate a set of points on a plane. + +SAMPLING_GRID +Define a set of equally-spaced points along an axis. + +SCALE_LS_SYS +Scale a linear system of normal equations to improve +the eigenvalue ratio. + +SEARCH_OBJECTS +Search meaningful maxima in a given image. + +SHIFTED_TEMPLATES +Shift a template image by an integer multiple of a +pre-fixed sub-pixel shift. + +SIMPSON_PIX_INTEGRAL +Resample a bivariate function defined on a rectangular +domain by integration over square regions with the +Simpson rule. + +SIZE52 +Alias of IDL 5.2 intrinsic SIZE function for previous +versions. + +SORT_LIST +STARFINDER auxiliary routine to handle internal data structure. + +SPLINE_COEFF +Compute the coefficients of the interpolating spline +to a set of observations on a rectangular grid. + +SPLINE_INTERP +Evaluate a spline function on a rectangular grid. + +STACK_COMBINE +Combine frames in a 3D stack. + +STAR +STARFINDER auxiliary routine to handle internal data structure. + +STAR_PARAM +STARFINDER auxiliary routine to handle internal data structure. + +STARFINDER +Stars detection, astrometry and photometry in a stellar field. + +STARLIST +STARFINDER auxiliary routine to handle internal data structure. + +SUB_ARRAY +Extract sub-array from a 2D array. + +SUB_ARRAYS +Given a 2D array, extract a stack of sub-arrays +centered at specified positions. + +SUBS_TO_COORD +Convert vector of array subscripts to pixel coordinates. + +SUPERPOSE_STARS +Compute stack median of a set of stars in an image. + +SVD_INV_MATRIX +Compute the inverse of a rectangular matrix by Singular Value +Decomposition. + +SVPSF_EXTRACT +Partition an image into a grid of sub-regions and extract +a local PSF estimate for each region. + +UPDATE_LIST +STARFINDER auxiliary routine to handle internal data structure. + +WHERE_STARS +STARFINDER auxiliary routine to handle internal data structure. + +XCOMPARE_LISTS +Compare two lists of objects. Widget interface. + +XDISPFILE +Read an ASCII file and display its content on a text widget. + +XDISPLAYOPT +Modify the display options of an image. Widget interface. + +XIMAGE_SUPPORT +Modify the support of an image. Widget interface. + +XMATCH_COORD +Widget interface for the MATCH_COORD procedure. + +XNOISE +Widget application to compute the noise standard +deviation for each pixel in a given image. + +XNOISE_STDEV +Widget interface for the GAUSS_NOISE_STD procedure. + +XPLOT +Execute simple plots. Widget interface. + +XPSF_EXTRACT +Widget interface for the PSF_EXTRACT procedure. + +XPSF_SMOOTH +Widget interface for the HALO_SMOOTH function. + +XREPLACE_PIX +Widget interface for the REPLACE_PIX function. + +XSTARFINDER +Package for stellar fields analysis. Widget interface. + +XSTARFINDER_RUN +Widget interface for the STARFINDER procedure. diff --git a/log2.pro b/log2.pro new file mode 100644 index 0000000000000000000000000000000000000000..a19af0e586a261adc647adbae7da5fba65c32f5b --- /dev/null +++ b/log2.pro @@ -0,0 +1,33 @@ +; $Id: log2.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; LOG2 +; +; PURPOSE: +; Compute the base 2 logarithm of an integer number. +; +; CATEGORY: +; Mathematics. +; +; CALLING SEQUENCE: +; Result = LOG2(N) +; +; INPUTS: +; N: Integer number +; +; OUTPUTS: +; Result: Base 2 logarithm of N +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +FUNCTION log2, n + + on_error, 2 + if n/2 le 1 then l = 1 else l = 1 + log2(n/2) + return, l +end + + diff --git a/ls_sys.pro b/ls_sys.pro new file mode 100644 index 0000000000000000000000000000000000000000..df6e5aa82798d0783a65a994dd935d5c704a94da --- /dev/null +++ b/ls_sys.pro @@ -0,0 +1,65 @@ +; $Id: ls_sys.pro, v 1.1 Jan 2000 e.d. $ +; +;+ +; NAME: +; LS_SYS +; +; PURPOSE: +; Put the algebraic system of linear equations +; Ax = b +; in the least squares form +; (A'A)x = A'b, +; where A' is the transpose of A. +; +; CATEGORY: +; Mathematics. Linear systems. +; +; CALLING SEQUENCE: +; LS_SYS, A, B, A_ls, B_ls +; +; INPUTS: +; A: 2D array of size n*m (i.e. n columns and m rows), representing +; the linear system matrix +; +; B: 1D array of size m, representing a set of measurements +; +; KEYWORD PARAMETERS: +; WEIGHTS: 1D array of size m, representing squared weights to be +; applied to the measurements vector B. In this case the least +; squares form of the input system is +; (A'WA)x = A'Wb, +; where W is the vector of squared weights +; +; MASK: 1D array of valid subscripts, indicating the components of the +; mesurements array B which must be masked, i.e. excluded +; +; OUTPUTS: +; A_ls: Same as A'A in PURPOSE above +; +; B_ls: Same as A'b in PURPOSE above +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +; Updates: +; 1) Fixed bug on transposition of b, when it is a scalar +; (Emiliano Diolaiti, January 2000) +;- + +PRO ls_sys, a, b, WEIGHTS = w, MASK = pix, a_ls, b_ls + + on_error, 2 + s = size52(a, /DIM) & n_col = s[0] & n_rows = s[1] + a_ls = a + if n_elements(w) eq n_rows then $ + a_ls = diag_mult(a_ls, w, /PRE) + if n_elements(pix) ne 0 then $ + if min(pix) ge 0 and max(pix) lt n_rows then begin + m = make_array(n_rows, VALUE = 1.) & m[pix] = 0 + a_ls = diag_mult(a_ls, m, /PRE) + endif + a_ls = transpose(a_ls) + if n_col gt 1 then b_ls = b # a_ls else b_ls = total(b * a_ls) + a_ls = a # a_ls & if n_col gt 1 then b_ls = transpose(b_ls) +; a_ls = a # a_ls & b_ls = transpose(b_ls) + return +end diff --git a/match_coord.pro b/match_coord.pro new file mode 100644 index 0000000000000000000000000000000000000000..c621f8c16ff63db5a5fc82e504402ee4a5e5ed3c --- /dev/null +++ b/match_coord.pro @@ -0,0 +1,115 @@ +; $Id: match_coord.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; MATCH_COORD +; +; PURPOSE: +; Given two sets of coordinates on a plane, representing the same points +; in two different reference frames supposed to be reciprocally translated +; and rotated, find the optimal transformation between the two sets and +; map one of them onto the other. +; The transformation between the two reference frames is defined by the +; position of the origin of frame 2 in frame 1 and the angle between the +; x- axes of frames 1 and 2, measured counter-clockwise from 1 to 2. +; +; CATEGORY: +; Mathematics. Spatial transformations. +; +; CALLING SEQUENCE: +; MATCH_COORD, X1, Y1, X2, Y2, X_ref, Y_ref, X2rt, Y2rt, Origin, Angle +; +; INPUTS: +; X1, Y1: Coordinates of points in reference frame no. 1 +; +; X2, Y2: Coordinates of points in reference frame no. 2 +; +; X_ref, Y_ref: Coordinates of reference points, used to find the +; optimal transformation. X_ref, Y_ref may be a subset of X1, Y1 +; +; KEYWORD PARAMETERS: +; ORIGIN_0: 2-components vector, representing a guess of the position +; of the origin of reference frame 2 in reference frame 1. The +; default is ORIGIN_0 = [0., 0.] +; +; ANGLE_0: Scalar, guess of the angle (in radians) between the x- axis +; of reference frame 1 and the x- axis of reference frame 2, measured +; counter-clockwise from 1 to 2. The default is ANGLE_0 = 0. rad +; +; _EXTRA: Optional input keywords of FIND_ROT_TRANS (see the file +; 'find_rot_trans.pro') +; +; OUTPUTS: +; X2rt, Y2rt: Coordinates (X2, Y2) mapped onto reference frame 1. +; These coordinates may be directly compared to (X1, Y1). +; A negative scalar value indicates an error condition +; +; OPTIONAL OUTPUTS: +; Origin: 2-components vector, representing the estimated position of +; the origin of reference frame 2 in reference frame 1 +; +; Angle: Scalar, estimated angle (in radians) between the x- axis of +; reference frame 1 and the x- axis of reference frame 2, measured +; counter-clockwise from 1 to 2 +; +; RESTRICTIONS: +; 1) The procedure assumes the two reference frames are reciprocally rotated +; and translated. Other kinds of spatial transformation, e.g. those due to +; geometric distorsions, are not considered and should be negligible. +; 2) The set (X_ref, Y_ref) of reference positions may be a subset of +; (X1, Y1), even though this is not necessary. What's essential is that, +; after applying an approximate inverse transformation to (X2, Y2) using +; the initial guesses ORIGIN_0 and ANGLE_0, it is possible to match each +; point in the reference set (X_ref, Y_ref) with one and only one point in +; both the sets (X1, Y1) and (X2, Y2). This occurs if +; a) the initial guesses ORIGIN_0 and ANGLE_0 are accurate enough +; b) the reference points are sufficiently isolated +; +; PROCEDURE: +; Apply an inverse transformation to (X2, Y2) using the initial guesses. +; Then match the reference points (X_ref, Y_ref) with their counterparts +; in the sets (X1, Y1) and (X2, Y2) by a minimum distance criterion. +; Use these subsets of (X1, Y1) and (X2, Y2) to find the transformation +; between the reference frames 1 and 2 and then apply it to (X2, Y2), in +; order to map this set of coordinates onto the reference frame of (X1, Y1). +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +PRO match_coord, x1, y1, x2, y2, x_ref, y_ref, x2rt, y2rt, origin, angle, $ + ORIGIN_0 = origin_0, ANGLE_0 = angle_0, _EXTRA = extra + + on_error, 2 + ; default values of reciprocal shift and rotation + if n_elements(origin_0) eq 0 then origin_0 = [0., 0.] + if n_elements(angle_0) eq 0 then angle_0 = 0. + ; approximate inverse transformation of (x2,y2) using origin_0 and angle_0 + rot_trans, x2 + origin_0[0], y2 + origin_0[1], [0., 0.], -angle_0, $ + x2_temp, y2_temp + ; match reference positions with both the sets of coordinates + n_ref = n_elements(x_ref) + x1_ref = fltarr(n_ref) & y1_ref = x1_ref + x2_ref = fltarr(n_ref) & y2_ref = x2_ref + for n = 0L, n_ref - 1 do begin + m = min(distance(x_ref[n], y_ref[n], x1, y1), w) + x1_ref[n] = x1[w] & y1_ref[n] = y1[w] + m = min(distance(x_ref[n], y_ref[n], x2_temp, y2_temp), w) + x2_ref[n] = x2_temp[w] & y2_ref[n] = y2_temp[w] + endfor + ; check if the couples of reference coordinates are all distinct + x2rt = -1 & y2rt = -1 ; set error condition + remove_coincident, x1_ref, y1_ref, x1_ref, y1_ref + remove_coincident, x2_ref, y2_ref, x2_ref, y2_ref + if n_elements(x1_ref) eq n_ref and n_elements(x2_ref) eq n_ref then begin + ; direct transformation of (x2_ref, y2_ref) with origin_0 and angle_0 + rot_trans, x2_ref, y2_ref, origin_0, angle_0, x2_temp, y2_temp + ; estimate optimal translation and rotation + find_rot_trans, x1_ref, y1_ref, x2_temp, y2_temp, origin_0, angle_0, $ + origin, angle, found, _EXTRA = extra + ; apply optimal inverse transformation to original set (x2, y2) + if found then $ + rot_trans, x2 + origin[0], y2 + origin[1], [0, 0], -angle, x2rt, y2rt + endif + return +end diff --git a/max_search.pro b/max_search.pro new file mode 100644 index 0000000000000000000000000000000000000000..181dc1d5d2b5e7bb9fdae19d19a6e9e5e02caf1f --- /dev/null +++ b/max_search.pro @@ -0,0 +1,120 @@ +; $Id: max_search.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; MAX_SEARCH +; +; PURPOSE: +; Given a 2D array, search for relative maxima above a fixed threshold +; and fulfilling a set of optional conditions. +; A given pixel is considered a relative maximum if it is brighter +; than its 8-neighbors or 4-neighbors. +; +; CATEGORY: +; Signal processing. +; +; CALLING SEQUENCE: +; MAX_SEARCH, Array, Threshold, N, X, Y, I +; +; INPUTS: +; Array: 2D array to be searched +; +; Threshold: lower detection threshold; it may be either a scalar or +; a 2D array, with the same size as Array +; +; KEYWORD PARAMETERS: +; X0, Y0: Coordinates of reference pixel, representing an origin to +; compute distances of detected maxima. +; +; CHECK_DIST: Set this keyword to a scalar value representing the maximum +; allowed distance of an acceptable local max. from the reference +; pixel defined by X0 and Y0. This keyword has no effect if X0 and Y0 +; are undefined. +; +; NEAREST: Set this keyword to select the maximum lying next to the +; reference position defined by X0 and Y0. If there are more than +; one maximum at the same distance, return all. This keyword has no +; effect if X0 and Y0 are undefined. +; +; MAXIMUM: Set this keyword to select the brightest maximum +; in the input array. If there are more than one maximum with the +; same value, return all. +; +; FOUR: Set this keyword to identify relative maxima as pixels +; brighter than their 4-neighbors. The default is to use +; 8-neighbors. +; +; SORTED: Set this keyword to have the output maxima sorted by +; decreasing intensity +; +; OUTPUTS: +; N: Number of detected maxima +; +; X, Y: Long integer coordinates of detected maxima. If no maximum is +; found, X and Y are set to negative scalars +; +; I: Intensities of detected maxima. The type of I is the same as that of +; Array. If no maximum is found, I is set to a negative scalar +; +; RESTRICTIONS: +; The optional conditions specified by the keywords CHECK_DIST, NEAREST, +; MAXIMUM are checked in the same order they have been presented. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +PRO max_search, array, threshold, X0 = x0, Y0 = y0, CHECK_DIST = max_dist, $ + NEAREST = nearest, MAXIMUM = maximum, _EXTRA = extra, $ + SORTED = sorted, n, x, y, i + + on_error, 2 + ; define search options + check_dist = n_elements(max_dist) ne 0 and $ + n_elements(x0) ne 0 and n_elements(y0) ne 0 + select_nearest = keyword_set(nearest) and $ + n_elements(x0) ne 0 and n_elements(y0) ne 0 + select_maximum = keyword_set(maximum) + ; find local maxima + all_max, array, x, y, n, _EXTRA = extra + found = n ne 0 + if found then i = array[x,y] + ; select detected maxima according to options + if found then begin + if check_dist or select_nearest then begin + d = distance(x0, y0, x, y) + if check_dist then begin + w = where(d le max_dist, n) & found = n ne 0 + if found then begin + d = d[w] & x = x[w] & y = y[w] & i = i[w] + endif + endif + if found and select_nearest then begin + w = where(d le min(d), n) & found = n ne 0 + if found then begin + d = d[w] & x = x[w] & y = y[w] & i = i[w] + endif + endif + endif + if found and select_maximum then begin + w = where(i ge max(i)) + x = x[w] & y = y[w] & i = i[w] + endif + endif + if found then begin + n = n_elements(i) + if size52(threshold, /N_DIM) eq 2 then $ + w = where(i gt threshold[x,y], n) else $ + w = where(i gt threshold[0], n) + if n ne 0 then begin + x = x[w] & y = y[w] & i = i[w] + endif + endif + if n eq 0 then begin + x = -1 & y = -1 & i = -1 + endif else $ + if keyword_set(sorted) then begin + s = reverse(sort(i)) & x = x[s] & y = y[s] & i = i[s] + endif + return +end diff --git a/median_filter.pro b/median_filter.pro new file mode 100644 index 0000000000000000000000000000000000000000..634c8cf684302f00368d47db88a50866ff02f89b --- /dev/null +++ b/median_filter.pro @@ -0,0 +1,63 @@ +; $Id: median_filter.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; MEDIAN_FILTER +; +; PURPOSE: +; Apply median smoothing to a 2D array, using the intrinsic function +; MEDIAN. In the inner portion of the array, the result is the same as +; that of MEDIAN. At the array edge, perform a median smoothing with +; reduced box size. +; +; CATEGORY: +; Signal processing. +; +; CALLING SEQUENCE: +; Result = MEDIAN_FILTER(Array, Box) +; +; INPUTS: +; Array: 2D array to smooth +; +; Box: Box size for smoothing +; +; KEYWORD PARAMETERS: +; EVEN: If the EVEN keyword is set when Array contains an even number +; of points (i.e. there is no middle number), MEDIAN returns the +; average of the two middle numbers. +; +; OUTPUTS: +; Result: 2D median smoothed array. +; Return the input array if an error occurs. +; +; RESTRICTIONS: +; Apply only to 2D arrays. +; +; PROCEDURE: +; Apply MEDIAN on the inner part of the array. At the edge perform +; smoothing on an asymmetric box of reduced size. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +FUNCTION median_filter, array, box, _EXTRA = extra + + catch, error + if error ne 0 then return, array + s = size52(array, /DIM) & sx = s[0] & sy = s[1] + b = round(box) > 1 + j = lindgen(sx) & i = lindgen(sy) & w = b / 2 + lo_j = (j - w) > 0 & up_j = (j + w) < (sx - 1) + lo_i = (i - w) > 0 & up_i = (i + w) < (sy - 1) + filtered = median(array, 2*w + 1, _EXTRA = extra) + for i = 0, w - 1 do for j = 0, sx - 1 do $ + filtered[j,i] = median(array[ lo_j[j]:up_j[j],lo_i[i]:up_i[i] ], _EXTRA = extra) + for i = sy - w, sy - 1 do for j = 0, sx - 1 do $ + filtered[j,i] = median(array[ lo_j[j]:up_j[j],lo_i[i]:up_i[i] ], _EXTRA = extra) + for i = w, sy - w - 1 do for j = 0, w - 1 do $ + filtered[j,i] = median(array[ lo_j[j]:up_j[j],lo_i[i]:up_i[i] ], _EXTRA = extra) + for i = w, sy - w - 1 do for j = sx - w, sx - 1 do $ + filtered[j,i] = median(array[ lo_j[j]:up_j[j],lo_i[i]:up_i[i] ], _EXTRA = extra) + return, filtered +end diff --git a/merge_list.pro b/merge_list.pro new file mode 100644 index 0000000000000000000000000000000000000000..313806387cfedd0f65f30144e7010b9db0df7633 --- /dev/null +++ b/merge_list.pro @@ -0,0 +1,43 @@ +; $Id: merge_list.pro, v 1.1 Mar 2001 e.d. $ +; +;+ +; NAME: +; MERGE_LIST +; +; PURPOSE: +; Merge two lists of stars. +; +; CATEGORY: +; STARFINDER auxiliary procedures. +; +; CALLING SEQUENCE: +; Result = MERGE_LIST(L1, L2) +; +; INPUTS: +; L1, L2: lists to merge. If L1 is empty, return L2. +; +; OUTPUTS: +; Return merged list +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, June 2001. +; 1) Created this file (E. D., March 2012). +; 2) Fixed bug when input list L1 is empty (E. D., April 2012). +;- + +FUNCTION merge_list, l1, l2 + + on_error, 2 + if n_tags(l1) eq 0 then $ + l = l2 else $ + begin + n1 = n_elements(l1) +; if n1 eq 0 then l = l2 else $ +; begin + n2 = n_elements(l2) + l = starlist(n1 + n2) + l[0] = l1 & l[n1] = l2 +; endelse + endelse + return, l +end diff --git a/min_norm_inversion.pro b/min_norm_inversion.pro new file mode 100644 index 0000000000000000000000000000000000000000..1f00d0c6ba788f1677f1981b29f509ca289fbf16 --- /dev/null +++ b/min_norm_inversion.pro @@ -0,0 +1,69 @@ +; $Id: min_norm_inversion.pro, v 1.2 Mar 2012 e.d. $ +; +;+ +; NAME: +; MIN_NORM_INVERSION +; +; PURPOSE: +; Compute the minimum norm solution of an algebraic system +; of linear equations +; +; CATEGORY: +; Mathematics. Linear systems. +; +; CALLING SEQUENCE: +; Result = MIN_NORM_INVERSION(A, B) +; +; INPUTS: +; A: matrix of linear system (n columns, m rows) +; +; B: vector of measurements (m rows) +; +; KEYWORD PARAMETERS: +; SCALING: If the linear system has been previously scaled (see the +; routine SCALE_LS_SYS in the file 'scale_ls_sys.pro'), the solution +; must be multiplied by the scaling factors. Set the keyword SCALING +; to the vector of scaling factors to do this +; +; SVDINV: Set this keyword parameter to invert the matrix A by +; singular value decomposition. +; +; OUTPUTS: +; Return n-components vector, representing the minimum norm solution +; of the linear system, defined as A'b, where A' is the generalized +; inverse of A computed by GINV (see the file 'ginv.pro') or by SVD +; (see the file 'svd_inv_matrix.pro'). +; If the first input A has just one element (i.e. A represents a scalar), +; just divide B by A. +; +; OPTIONAL OUTPUTS: +; INVERSE: Use this output keyword to retrieve the generalized inverse +; of the input matrix A +; +; RESTRICTIONS: +; If an error occur (A is a scalar equal to 0 or the size of the input +; arrays are not correct), return to caller +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +; Updates: +; 1) Fixed bug on output keyword INVERSE when A is a scalar +; (Emiliano Diolaiti, August 2000). +; 2) Added SVD inversion option (E. D., March 2012). +;- + +FUNCTION min_norm_inversion, a, b, SCALING = scaling, $ + SVDINV = svdinv, INVERSE = inv_a + + on_error, 2 + if n_elements(a) eq 1 then begin + inv_a = 1. / a & x = b * inv_a + endif else begin + if keyword_set(svdinv) then $ + inv_a = svd_inv_matrix(a) else $ + inv_a = ginv(a) + x = b # inv_a + endelse + if n_elements(scaling) eq n_elements(x) then x = x * scaling + return, x +end diff --git a/mm_psf_setup.pro b/mm_psf_setup.pro new file mode 100644 index 0000000000000000000000000000000000000000..4abc0f82dab51b7a5daa9a11324af31791ea9ad6 --- /dev/null +++ b/mm_psf_setup.pro @@ -0,0 +1,155 @@ +; $Id: mm_psf_setup.pro, v 1.0 March 2012 e.d. $ +; +;+ +; NAME: +; MM_PSF_SETUP +; +; PURPOSE: +; Define auxiliary PSF data to be used by main routine of "starfinder.pro" +; when a parametric space-variant PSF model called is adopted. +; The PSF model defined here is based on a sum of Moffat functions. +; The relative weight, width and position angle of the Moffat components are +; assumed to change across the image which the PSF model refers to. +; The variation model makes the following assumptions: +; - all elongated Moffat components point to the same reference position +; in the image +; - relative weight and width of Moffat components change across the image +; according to a polynomial law depending on the distance of the considered +; location in the image from the reference position. +; +; CATEGORY: +; Stellar fields astrometry and photometry. +; +; CALLING SEQUENCE: +; MM_PSF_SETUP, C, Pow, Xref, Yref, Xsize_ima, Ysize_ima, Nx, Ny, $ +; Xsize_psf, Ysize_psf, Xsize_svpsf, Ysize_svpsf, $ +; Psf_data, Psf, Sv_par +; +; INPUTS: +; C: 2-dim or 3-dim floating-point array of coefficients defining the multi-Moffat PSF +; model and its variation across the image. The variation of each parameter is +; assumed to be described by a polynomial function of the distance of the PSF +; location in the image from the reference position. +; The array C is defined as follows: +; C[i,j,k], 0 <= i <= maxdeg+2, 0 <= j <= Nparcomp-1, 0 <= k <= Ncomp-1, where +; - Ncomp: number of Moffat functions in the PSF model +; - Nparcomp: number of parameters per Moffat component (3) +; - maxdeg: maximum degree among polynomials defining parameters variations across the image +; It is assumed that each Moffat function is defined by 3 parameters: +; - relative weight (scale factor) +; - half-light radius along elongated axis +; - half-light radius along axis perpendicular to elongation +; The elongation axis is defined as the line connecting the PSF position in the image +; and the reference position. +; The element C[0,j,k] is the degree of the polynomial associated to parameter j +; of Moffat function k. +; The elements C[i,j,k] with 1 <= i <= C[0,j,k]+1 are the coefficients of the +; polynomial associated to parameter j of Moffat function k. +; +; Pow: scalar or floating-point array with power law of Moffat functions. If scalar, +; the same power law is assumed for all components. If array, the number of elements +; must be the same as the number of components. +; +; Xref, Yref: pixel coordinates of reference position in the image. +; +; Xsize_ima, Ysize_ima: integers, number of columns and rows of image array. +; +; Nx, Ny: number of regions along X and Y axes of image to determine cube of +; piecewise constant PSFs. +; +; Xsize_psf, Ysize_psf: integers, number of columns and rows of PSF array to be +; computed at any desired location in the image according to the continuous +; variation model. +; +; Xsize_svpsf, Ysize_svpsf: integers, number of columns and rows of PSF arrays +; in the cube of piecewise constant PSFs (see Nx, Ny above). +; +; OUTPUTS: +; Psf_data: pointer to structure of PSF model parameters used to compute the PSF +; at any position in the image. +; +; Psf: floating-point array of piece-wise constant PSFs; cube of size +; Xsize_svpsf * Ysize_svpsf * (Nx*Ny). +; +; Sv_par: structure defining the boundaries of the Nx*Ny sub-regions into which the +; image is virtually partitioned and over which the piece-wise constant PSFs are +; defined. +; +; EXAMPLE: +; 1) Define PSF model based on 2 Moffat components with following parameters: +; +; +; IDL> c=[[[0,0.5,0,0],[2,1.0,1e-3,1e-4],[1,1.0,1e-3,0.0]],$ +; [[0,0.5,0,0],[0,4.0,0,0],[0,4.0,0,0]]] +; IDL> help,c +; C FLOAT = Array[4, 3, 2] +; IDL> pow=[1.5,2.0] +; IDL> xref=128.0 & yref=128.0 +; IDL> xsize_ima=256 +; IDL> ysize_ima=256 +; IDL> nx=4 +; IDL> ny=4 +; IDL> xsize_psf=64 +; IDL> ysize_psf=64 +; IDL> xsize_svpsf=32 +; IDL> ysize_svpsf=32 +; IDL> mm_psf_setup, c, pow, xref, yref, xsize_ima, ysize_ima, nx, ny, $ +; xsize_psf, ysize_psf, xsize_svpsf, ysize_svpsf, psf_data, psf, sv_par +; IDL> help,*psf_data,/STRUCTURE +; ** Structure <1374398>, 10 tags, length=132, data length=130, refs=1: +; XREF FLOAT 128.000 +; YREF FLOAT 128.000 +; XORI FLOAT 0.000000 +; YORI FLOAT 0.000000 +; C FLOAT Array[4, 3, 2] +; POW FLOAT Array[2] +; NCOMP LONG 2 +; NPARCOMP INT 3 +; X_SIZE INT 64 +; Y_SIZE INT 64 +; IDL> help,psf +; PSF FLOAT = Array[32, 32, 16]; +; IDL> help,sv_par,/STRUCTURE +; ** Structure <181f8a0>, 4 tags, length=64, data length=64, refs=1: +; LX LONG Array[4] +; UX LONG Array[4] +; LY LONG Array[4] +; UY LONG Array[4] +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, March 2012. +;- + +PRO mm_psf_setup, c, pow, xref, yref, xsize_ima, ysize_ima, nx, ny, $ + xsize_psf, ysize_psf, xsize_svpsf, ysize_svpsf, $ + psf_data, psf, sv_par + +catch, error +if error ne 0 then begin + if ptr_valid(psf_data) then ptr_free, psf_data + return +endif + +if size(c, /N_DIM) gt 2 then ncomp = (size(c, /DIM))[2] else ncomp = 1 +if n_elements(pow) eq ncomp then k = pow else k = replicate(pow[0], ncomp) +nparcomp = 3 ; number of parameters per component +psf_data = {xref: float(xref), yref: float(yref), xori: 0.0, yori: 0.0, $ + c: c, pow: k, ncomp: ncomp, nparcomp: nparcomp, $ + x_size: xsize_psf, y_size: ysize_psf} +psf_data = ptr_new(psf_data) + +array_partition, xsize_ima, nx, lx, ux, /OVERLAP +array_partition, ysize_ima, ny, ly, uy, /OVERLAP +sv_par = {lx: lx, ux: ux, ly: ly, uy: uy} +psf = fltarr(xsize_svpsf, ysize_svpsf, nx*ny) +for j = 0L, nx-1 do for i = 0L, ny-1 do begin + (*psf_data).xori = (lx[j] + ux[j]) / 2 - xsize_svpsf/2 + (*psf_data).yori = (ly[i] + uy[i]) / 2 - ysize_svpsf/2 + psf[*,*,i*nx+j] = $ + image_model(xsize_svpsf/2, ysize_svpsf/2, 1.0, xsize_svpsf, ysize_svpsf, "mm_psf", psf_data) +endfor +(*psf_data).xori = 0.0 +(*psf_data).yori = 0.0 + +return +end diff --git a/mmfit.pro b/mmfit.pro new file mode 100644 index 0000000000000000000000000000000000000000..6246445043fd4f94f4fa055d9c79057feb8cceaf --- /dev/null +++ b/mmfit.pro @@ -0,0 +1,328 @@ +; $Id: mmfit.pro, v 1.0 March 2012 e.d. $ +; +;+ +; NAME: +; MMFIT +; +; PURPOSE: +; Fit a 2D data array defined over a regular (X,Y) grid with a parametric +; model given by the sum of 2D Moffat functions. The Moffat functions have +; all the same position angle with respect to the X axis and have all the +; same center. One of the Moffat functions (typically the wider one) may +; have round simmetry. The fit is carried out by the iterative +; Newton-Gauss method. +; +; CATEGORY: +; Fitting. +; +; CALLING SEQUENCE: +; MMFIT, Image, Ncomp, P, Sigma_p, Model, Converged +; +; INPUTS: +; Image: 2D floating-point array with data to be fitted (e.g. Point +; Spread Function of an optical system). +; +; Ncomp: Integer, number of Moffat components. +; +; KEYWORD PARAMETERS: +; NOISE: Scalar or 2D array with the same size as Image with an estimate +; of the 1-sigma uncertainties on the data. This keyword parameter is +; used to perform a weighted least-squares fit to the data. If missing, +; the fit is unweighted. +; +; P0: Floating point vector of optional initial values of model parameters. +; For Ncomp components the vector has the following structure: +; P0 = [f_1, r_1_1, r_1_2, ...., f_Ncomp, r_1_Ncomp, r_2_Ncomp, $ +; x0, y0, phi, b, bx, by] +; where +; f_n: scale factor of the n-th component (n = 1, ..., Ncomp) +; r_1_n, r_2_n: radius along major and minor axes of n-th component +; x0, y0: coordinates in pixel units of the center of the model +; phi: angle of major axis of Moffat components with respect to X axis; +; it is measured counter-clockwise in radians +; b, bx, by: background terms (constant, X-gradient, Y-gradient). +; +; PVAR: Integer vector with the same length as P0 defined as follows: +; PVAR[n] = 1, if n-th parameter has to be optimized +; PVAR[n] = 0, if n-th parameter has to be kept fixed to its initial value +; By default, all parameters are optimized with the following exceptions: +; * background parameters (see b, bx, by under P0 above) +; * position angle phi (see P0 above) when only one component is used +; and it is round +; +; POWER: Vector of Ncomp elements describing the power law of each +; Moffat component (see procedure 'moffat2d.pro' for details). +; If undefined, it is assumed that all components follow the same +; power 1.5. +; +; LMROUND: Set this binary keyword to consider the last component in +; the model round. By default all Moffat functions are elongated. +; +; MAXIT: Maximum number of iterations allowed. The default is 30. +; +; TOL: Floating-point tolerance used as convergence criterion in +; iterative fitting process. The tolerance is absolute for all the +; parameters, with the exception of the scaling factors of the Moffat +; components (see parameters f_n under P0 above) for which it is +; relative. Default TOL = 1e-2. +; +; OUTPUTS: +; P: Floating-point vector of best fit model parameters. Same length and +; structure as P0. If fit does not converge, the output value of P is +; set to the initial estimates P0. +; +; Sigma_p: Vector with estimates of errors on model parameters P. +; Available only if keyword NOISE is set. +; +; Model: 2D array with the best fit model. Same size as input Image. +; The model is a sum of scaled Moffat functions, as defined by the procedure +; 'moffat2d.pro' (NOTE: without /NORMAL keyword). +; +; Converged: Byte. True if the fit has converged. +; +; RESTRICTIONS: +; +; PROCEDURE: +; Find initial estimate of model parameters (if not given on input) and +; refine this estimate by the iterative Newton-Gauss method (see +; 'newton_gauss.pro'). +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, March 2012. +;- + + + +;;; Auxiliary procedures/functions. + +FUNCTION mmfit_model, p, DATA = data + + on_error, 2 + if (*data).lmround then begin + k = ((*data).ncomp - 1) * (*data).npc + p[k + 2] = p[k + 1] + endif + npar = n_elements(p) + k = (*data).ncomp * (*data).npc + x0 = p[k] + y0 = p[k + 1] + phi = p[k + 2] + (*data).model = fltarr((*data).siz[0], (*data).siz[1]) + for n = 0, (*data).ncomp - 1 do begin + k = (*data).npc * n + (*data).mof[*,*,n] = moffat2d((*data).siz[0], (*data).siz[1], x0, y0, $ + p[k + 1], p[k + 2], phi, POWER = (*data).pow[n]) + (*data).model = (*data).model + p[k] * (*data).mof[*,*,n] + endfor + k = (*data).ncomp * (*data).npc + 3 + (*data).model = (*data).model + p[k] + $ + plane(0.0, p[k + 1], p[k + 2], (*data).siz[0], (*data).siz[1]) + return, (*data).model +end + +FUNCTION mmfit_iacobi, p, PVAR = pvar, DATA = data + + on_error, 2 + if (*data).lmround then begin + k = ((*data).ncomp - 1) * (*data).npc + p[k + 2] = p[k + 1] + endif + npix = (*data).siz[0] * (*data).siz[1] + npar = total(pvar) + k = (*data).ncomp * (*data).npc + x0 = p[k] + y0 = p[k + 1] + phi = p[k + 2] + x = rebin(findgen((*data).siz[0]) - x0, (*data).siz[0], (*data).siz[1]) + y = rebin(transpose(findgen((*data).siz[1])) - y0, (*data).siz[0], (*data).siz[1]) + rot_trans, x, y, [0, 0], phi, xr, yr + + iacobi = fltarr(npix, npar) + dmdx0 = fltarr((*data).siz[0], (*data).siz[1]) + dmdy0 = fltarr((*data).siz[0], (*data).siz[1]) + dmdphi = fltarr((*data).siz[0], (*data).siz[1]) + k = -1 + j = 0 + for n = 0, (*data).ncomp - 1 do begin + k = k + 1 + norm = p[k] + deriv = (*data).mof[*,*,n] + if pvar[k] eq 1 then begin + iacobi[*, j] = reform(deriv, npix) + j = j + 1 + endif + k = k + 1 + rx = p[k] + ry = p[k + 1] + rone = 1 + (xr / rx)^2 + (yr / ry)^2 + deriv = 2 * (*data).pow[n] * norm * rone^(-(*data).pow[n] - 1.0) * xr^2 / rx^3 + if pvar[k] eq 1 then begin + iacobi[*, j] = reform(deriv, npix) + if n lt (*data).ncomp - 1 or not (*data).lmround then j = j + 1 + endif + k = k + 1 + ;ry = p[k] + deriv = 2 * (*data).pow[n] * norm * rone^(-(*data).pow[n] - 1.0) * yr^2 / ry^3 + if pvar[k] eq 1 then begin + iacobi[*, j] = reform(deriv, npix) + j = j + 1 + endif else $ + if n eq (*data).ncomp - 1 and (*data).lmround then begin + iacobi[*, j] = iacobi[*, j] + reform(deriv, npix) + j = j + 1 + endif + dmdx0 = temporary(dmdx0) + 2 * (*data).pow[n] * norm * $ + rone^(-(*data).pow[n] - 1.0) * (xr*cos(phi)/rx^2-yr*sin(phi)/ry^2) + dmdy0 = temporary(dmdy0) + 2 * (*data).pow[n] * norm * $ + rone^(-(*data).pow[n] - 1.0) * (xr*sin(phi)/rx^2+yr*cos(phi)/ry^2) + dmdphi = temporary(dmdphi) + 2 * (*data).pow[n] * norm * $ + rone^(-(*data).pow[n] - 1.0) * xr * yr * (1/ry^2 - 1/rx^2) + endfor + k = k + 1 + if pvar[k] eq 1 then begin + iacobi[*, j] = reform(dmdx0, npix) + j = j + 1 + endif + k = k + 1 + if pvar[k] eq 1 then begin + iacobi[*, j] = reform(dmdy0, npix) + j = j + 1 + endif + k = k + 1 + if pvar[k] eq 1 then begin + iacobi[*, j] = reform(dmdphi, npix) + j = j + 1 + endif + k = k + 1 + if pvar[k] eq 1 then begin + iacobi[*, j] = 1.0 + j = j + 1 + endif + k = k + 1 + if pvar[k] eq 1 then begin + iacobi[*, j] = reform(plane(0, 1, 0, (*data).siz[0], (*data).siz[1]), npix) + j = j + 1 + endif + k = k + 1 + if pvar[k] eq 1 then begin + iacobi[*, j] = reform(plane(0, 0, 1, (*data).siz[0], (*data).siz[1]), npix) + j = j + 1 + endif + + return, transpose(iacobi) +end + +FUNCTION mmfit_converg, p0, p1, DATA = data + + on_error, 2 + if (*data).lmround then begin + k = ((*data).ncomp - 1) * (*data).npc + p0[k + 2] = p0[k + 1] + p1[k + 2] = p1[k + 1] + endif + ;print, p0 + type_p = (*data).type_p + wr = where(type_p, nr) + wa = where(not type_p, na) + test = 1B + if nr ne 0 then test = test and convergence(p0[wr], p1[wr], (*data).tol) + if na ne 0 then test = test and convergence(p0[wa], p1[wa], (*data).tol, /ABSOLUTE) + return, test +end + + + +;;; The main routine. + +PRO mmfit, image, NOISE = noise, ncomp, P0 = p0_in, PVAR = pvar_in, $ + POWER = power, LMROUND = lmround, TOL = tol, _EXTRA = extra, $ + p, sigma_p, model, converged + + catch, error + if error ne 0 then begin + ;msg = dialog_message(/ERROR, !err_string) + converged = 0B + heap_gc + return + endif + ; Setup + if n_elements(tol) eq 0 then tol = 1e-2 + siz = size(image, /DIM) + if n_elements(noise) ne 0 then begin + var = noise + w = where(var gt 0) + var[w] = 1 / (var[w])^2 + endif + npc = 3 + ; Parameter guess + if n_elements(p0_in) eq 0 then begin + peak = max(image) + rc = fwhm(image) / 2 + m = get_max(image) & x0 = m[0] & y0 = m[1] + npar = npc * ncomp + p0 = fltarr(npar) + for n = 0, ncomp - 1 do begin + k = npc * n + p0[k] = peak / (n + 1) + p0[k + 1] = rc * (n + 1) + p0[k + 2] = rc * (n + 1) + endfor + p0 = [p0, x0, y0, 0.0, replicate(0.0, 3)] + endif else $ + p0 = p0_in + npar = n_elements(p0) + type_p = bytarr(npar) + k = lindgen(ncomp) * npc + type_p[k] = 1B + if n_elements(pvar_in) eq 0 then $ + pvar = [replicate(1, npar-3), 0, 0, 0] else $ + pvar = pvar_in + k = (ncomp - 1) * npc + if keyword_set(lmround) then begin + pvar[k + 2] = 0 + if ncomp eq 1 then pvar[k + 5] = 0 + endif + ; Power law of Moffat functions + if n_elements(power) eq 0 then $ + pow = replicate(1.5, ncomp) else pow = power + + ; Iterative optimization + data = {ncomp: ncomp, siz: siz, $ + model: fltarr(siz[0], siz[1]), tol: tol, type_p: type_p, $ + npc: npc, mof: fltarr(siz[0], siz[1], ncomp), $ + pow: pow, lmround: keyword_set(lmround) and 1B} + data = ptr_new(data, /NO_COPY) + newton_gauss, "mmfit_model", "mmfit_iacobi", "mmfit_converg", $ + p0, PVAR = pvar, image, converged, p, sigma_p, model, $ + DATA = data, _EXTRA = extra, INVERSE_DATA_VAR = var + if not converged then begin + p = p0 + model = fltarr(siz[0], siz[1]) + endif + if n_elements(sigma_p) ne 0 then begin + sigma_p[where(pvar eq 0)] = 0.0 + if keyword_set(lmround) then sigma_p[k + 2] = sigma_p[k + 1] + endif + u = lindgen(ncomp) * npc + 1 + v = lindgen(ncomp) * npc + 2 + p[u] = abs(p[u]) + p[v] = abs(p[v]) + k = npc * ncomp + 2 + if pvar[k] eq 1 then begin + if abs(p[1]) lt abs(p[2]) then begin + temp = p[u] + p[u] = p[v] + p[v] = temp + p[k] = (p[k] + !pi/2) mod !pi + if n_elements(sigma_p) ne 0 then begin + temp = sigma_p[u] + sigma_p[u] = sigma_p[v] + sigma_p[v] = temp + endif + endif + p[k] = ((p[k] mod !pi) + 2*!pi) mod !pi + endif + + ptr_free, data + return +end diff --git a/moffat2d.pro b/moffat2d.pro new file mode 100644 index 0000000000000000000000000000000000000000..365d661256c2d1c50d00fbd81764ad639fdd154e --- /dev/null +++ b/moffat2d.pro @@ -0,0 +1,73 @@ +; $Id: moffat2d.pro, v 1.0 March 2012 e.d. $ +; +;+ +; NAME: +; MOFFAT2D +; +; PURPOSE: +; Compute a 2-dim Moffat function on a 2-dim (X, Y) reference frame. +; +; CATEGORY: +; Models. +; +; CALLING SEQUENCE: +; Result = MOFFAT2D(Nx, Ny, Cx, Cy, R1, R2, A) +; +; INPUTS: +; Nx, Ny: Size of output array (Nx columns * Ny rows). +; +; Cx, Cy: Coordinates of center of Moffat function. +; +; R1, R2: Radius of Moffat function along principal axes. +; +; OPTIONAL INPUTS: +; A: Angle of first axis (see R1) with respect to X axis; it is +; measured counter-clockwise in radians. If undefined, it is set to 0. +; +; KEYWORD PARAMETERS: +; POWER: Power law of Moffat function. Default POWER = 1.5. +; +; NORMAL: Set this keyword to normalize the output Moffat function +; to unit energy over the whole (X, Y) plane. +; Note: setting /NORMAL does not mean that the function is normalized +; to unit energy over the output array. +; +; OUTPUTS: +; Result: 2D floating-point array containing the Moffat function. +; +; PROCEDURE: +; Moffat function is defined by +; z(x,y) = 1 / (1 + (xr/R1)^2 + (yr/R2)^2)^k, +; where +; xr and yr are coordinates in the reference frame defined by the +; principal axes of the Moffat function (rotated by an angle A with +; respect to the X, Y reference frame); +; k is the power law defined by keyword POWER. +; The normalization coefficient is +; (k-1) / (!pi*R1*R2) +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, March 2012. +;- + +FUNCTION moffat2d, nx, ny, cx, cy, r1, r2, a, POWER = k, NORMAL = normal + +on_error, 2 + +if n_elements(a) eq 0 then a = 0.0 +if n_elements(k) eq 0 then k = 3.0/2.0 + +x = findgen(nx) - cx +y = findgen(ny) - cy +x = temporary(x) # make_array(ny, VALUE = 1) +y = make_array(nx, VALUE = 1) # temporary(y) +xtemp = x +ytemp = y +x = xtemp * cos(a) + ytemp * sin(a) +y = -xtemp * sin(a) + ytemp * cos(a) +z = 1 / (1 + (x / r1)^2 + (y / r2)^2)^k + +if keyword_set(normal) then z = z * (k-1) / (!pi*r1*r2) + +return, z +end diff --git a/newton_gauss.pro b/newton_gauss.pro new file mode 100644 index 0000000000000000000000000000000000000000..30de43d402fe27bd18f0f9f84c079854b3c9228e --- /dev/null +++ b/newton_gauss.pro @@ -0,0 +1,203 @@ +; $Id: newton_gauss.pro, v 1.2 Mar 2012 e.d. $ +; +;+ +; NAME: +; NEWTON_GAUSS +; +; PURPOSE: +; Optimize the parameters of parametric model in order to fit a set +; of measurements, by means of the Newton-Gauss iterative algorithm. +; This method requires the evaluation of both the model and its +; derivatives with respect to the parameters. +; +; CATEGORY: +; Mathematics. Optimization. +; +; CALLING SEQUENCE: +; NEWTON_GAUSS, Model_fun, Iacobi_fun, Converg_fun, Ini_x, Data, $ +; Converging, X, Sigma_x, Model +; +; INPUTS: +; Model_fun: Name of a function to compute the parametric model. +; It must be defined as follows: +; FUNCTION Model_fun, X, KEYWORDS = keywords +; where X is a set of parameters and KEYWORDS represents a list +; of input keyword parameters. +; The result of Model_fun must have the same size as the data to be +; approximated (see below) +; +; Iacobi_fun: Name of a function to compute the derivatives of the model. +; It must be defined as follows: +; FUNCTION Iacobi_fun, X, KEYWORDS = keywords +; where X is a set of parameters and KEYWORDS represents a list +; of input keyword parameters. Among the KEYWORDS, also the keyword +; PVAR should be defined if applicable (see KEYWORD PARAMETERS below). +; The result of Iacobi_fun must be an array of size N*M, where N (number +; of columns) is the number of parameters and M (size of rows) is the +; number of elements in the input data. The element [n,m] of the result +; is the derivative of the model with respect to the n-th parameter, +; computed at the m-th point +; +; Converg_fun: Name of a function to check the convergence condition +; between to sets of parameters, corresponding to successive iterations. +; It must be defined as follows: +; FUNCTION Converg_fun, X0, X, KEYWORDS = keywords +; where X0 and X are two sets of parameters and KEYWORDS represents a +; list of input keyword parameters. It must return a logical value +; +; Ini_x: Initial guess of the set of parameters +; +; Data: Data to be approximated with the parametric model +; +; KEYWORD PARAMETERS: +; BAD_DATA: Array of subscripts (compatible with the input Data), +; representing Data values to be masked +; +; MASK: Set this keyword to a nonzero value to apply the data masking +; mode no. 2 described in PROCEDURE description, provided an estimate +; of the background noise standard deviation is supplied on input +; (see next keyword) +; +; NOISE_STD: Estimate of the noise standard deviation. It may be a scalar +; ("white noise" case) or an array, having the same size as Data +; +; NOISE_TOL: Fix a threshold for bad data identification in data masking +; mode no. 2. See PROCEDURE description. The default is NOISE_TOL = 5 +; +; PVAR: Vector of integers with the same length as the input parameter Ini_x. +; The i-th value of PVAR is +; 1, if the i-th parameter has to be considered variable in the optimization +; 0, if the i-th parameter has to be kept fixed (to the initial value). +; By default all parameters are considered variable. +; +; WHEN: Iteration number when to identify bad data in data masking mode +; no. 2. See PROCEDURE description. The default is WHEN = 2 (i.e. +; 2nd iteration) +; +; SCALE: Set this keyword to a nonzero value to scale the set of variables +; (model parameters), in order to prevent noise amplification +; +; MAXIT: Maximum number of iteration allowed. The default is 30. +; +; INVERSE_DATA_VAR: Inverse noise variance on input Data. This parameter +; may be either a scalar ("white noise" case) or an array, having the +; same size as Data. +; +; _EXTRA: Extra keywords which are accepted by NEWTON_GAUSS and passed +; directly to the functions Model_fun, Iacobi_fun and Converg_fun. +; These extra keywords may be used to handle global data, as an +; alternative to COMMON blocks. +; Extra keywords (such as SVDINV) may be also passed to Min_Norm_Inversion. +; +; OUTPUTS: +; Converging: Logical value, true if the algorithm has converged, +; according to the convergence condition defined by the function +; Converg_fun +; +; X: Optimal set of parameters +; +; OPTIONAL OUTPUTS: +; Sigma_x: Formal errors (at "1 sigma level") on the estimated +; parameters. Available only if INVERSE_DATA_VAR has been provided +; +; Model: Best fit model +; +; IT: Use this output keyword to monitor the actual number of iterations +; performed +; +; W_BAD: Use this output keyword to retrieve the subscripts of the bad +; data which have been masked. The value of this keyword concide with +; the input BAD_DATA if only data masking mode no. 1 is performed +; +; PROCEDURE: +; The problem may be expressed mathematically as a set of non-linear +; algebraic equations, in the form +; Model(X) - Data = 0. +; The algebraic system is solved by means of the iterative Newton-Gauss +; scheme +; M Dx = Data - Model(X), X' = X + Dx +; where M is the Iacobi matrix of the model with respect to the parameters, +; X is the old set of parameters, Dx a correction and X' a new estimate. +; At every iteration the correction vector Dx is found as the minimum norm +; solution of the linear system +; M Dx = Data - Model +; The vector of parameters X may be scaled (see keyword SCALE), in order to +; prevent noise amplification in the inversion of the matrix, due to ill +; conditioning. +; The algorithm requires an initial estimate of the solution (Ini_x) and a +; stopping criterion (e.g. variation of parameters smaller than a pre-fixed +; tolerance or upper threshold on number of iterations). +; Bad data masking may be performed by: +; 1) providing an array of subscripts for the points to be masked +; 2) providing an estimate of the background noise standard deviation (see +; keyword NOISE_STD), used by the algorithm to identify the points where +; the error between the Data and the Model (computed at the iteration +; specified by the keyword WHEN) is larger than NOISE_TOL * NOISE_STD. +; The two data masking mode may be used together. +; It is possible to weigh the data by the inverse standard deviation of +; the error on each value (see the keyword INVERSE_DATA_VAR) +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +; 1) Added keyword PVAR (E. D., March 2008) +; 2) Added keyword _EXTRA in call to MIN_NORM_INVERSION (E.D., March 2012). +; 3) Positional parameter Pvar turned into keyword in call to Iacobi_fun; +; removed from calls to Model_fun and Converg_fun (E.D., March 2012). +;- + +PRO newton_gauss, model_fun, iacobi_fun, converg_fun, ini_x, data, $ + PVAR = pvar_in, BAD_DATA = bad_data, MASK = mask, NOISE_STD = noise_std, $ + NOISE_TOL = noise_tol, WHEN = mask_now, SCALE = scale, $ + MAXIT = maxit, INVERSE_DATA_VAR = inverse_var, $ + converging, x, sigma_x, model, IT = it, W_BAD = w_bad, $ + _EXTRA = extra + + on_error, 2 + if n_elements(bad_data) ne 0 then w_bad = bad_data + mask = keyword_set(mask) and n_elements(noise_std) ne 0 + if mask then begin + if n_elements(mask_now) eq 0 then mask_now = 2 + if n_elements(noise_tol) eq 0 then noise_tol = 5 + endif + if n_elements(maxit) eq 0 then maxit = 30 + n_data = n_elements(data) + if n_elements(inverse_var) eq 1 then $ + inverse_var = replicate(inverse_var, n_data) + it = 0L & x = ini_x + npar = n_elements(x) + if n_elements(pvar_in) eq 0 then $ + pvar = make_array(npar, VALUE = 1) else $ + pvar = pvar_in + model = call_function(model_fun, x, _EXTRA = extra) + converging = 0B + + while it lt maxit and not converging do begin + + it = it + 1 + if mask then if it eq mask_now then w_bad = $ + append_elements(w_bad, where(abs(data - model) gt noise_tol*noise_std)) + if n_elements(pvar_in) ne 0 then $ + iacobi = call_function(iacobi_fun, x, PVAR = pvar_in, _EXTRA = extra) else $ + iacobi = call_function(iacobi_fun, x, _EXTRA = extra) + ls_sys, iacobi, reform(data - model, n_data), MASK = w_bad, $ + WEIGHTS = inverse_var, lin_hessian, grad + if keyword_set(scale) then $ + scale_ls_sys, lin_hessian, grad, lin_hessian, grad, s, $ + NOCOMP = (it gt 1) and 1B + dx = min_norm_inversion(lin_hessian, grad, SCALING = s, $ + INVERSE = inv_hessian, _EXTRA = extra) + dxv = fltarr(npar) + dxv[where(pvar eq 1)] = dx + x0 = x & x = x + dxv + model = call_function(model_fun, x, _EXTRA = extra) + converging = call_function(converg_fun, x0, x, _EXTRA = extra) + + endwhile + + if converging then if n_elements(inverse_var) ne 0 then begin + sigma_x = fltarr(npar) + sigma_x[where(pvar eq 1)] = fitting_errors(inv_hessian, SCALING = s) + endif + + return +end diff --git a/noise.fits b/noise.fits new file mode 100644 index 0000000000000000000000000000000000000000..76bcc714639afd2e8955d18858f2f385b96ed208 Binary files /dev/null and b/noise.fits differ diff --git a/peak_area.pro b/peak_area.pro new file mode 100644 index 0000000000000000000000000000000000000000..9b9edbd8fe1108cc16bba9b6636744822215db50 --- /dev/null +++ b/peak_area.pro @@ -0,0 +1,90 @@ +; $Id: peak_area.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; PEAK_AREA +; +; PURPOSE: +; Estimate the area of a peak in a given image. +; +; CATEGORY: +; Signal processing. +; +; CALLING SEQUENCE: +; Result = PEAK_AREA(Image) +; +; INPUTS: +; Image: Image containing the peak whose area has to be estimated +; +; KEYWORD PARAMETERS: +; X, Y: Coordinates of the peak. The default is the Image absolute maximum +; +; ABS_THRESHOLD: Threshold above which the area must be estimated. +; No default value is fixed +; +; REL_THRESHOLD: Relative threshold with respect to the peak value, +; used to fix the threshold above which the area must be estimated. +; The default is REL_THRESHOLD = 0.5, i.e. the area is estimated at +; half maximum. REL_THRESHOLD is neglected when ABS_THRESHOLD is +; defined and is used by default when ABS_THRESHOLD is undefined +; +; MAG: Integer magnification factor for array magnification. It may be +; useful to improve the accuracy of the measurement. The default is +; MAG = 1, i.e. no magnification. +; +; CUBIC: Set this keyword to a nonzero value to magnify the image with +; the cubic convolution interpolation implemented in the library +; routine CONGRID. This keyword has effect only if MAG is defined. +; If CUBIC is not set, the array magnification is performed with REBIN +; (bilinear interpolation). +; +; OUTPUTS: +; Result: Area of the Image component connected to the peak and above the +; fixed threshold. The type of the result is long-integer if MAG = 1, +; float if MAG > 1. +; Return 0 if an error occurs in the extraction of the connected +; component. +; +; RESTRICTIONS: +; The input Image is supposed to have been background-subtracted. +; +; PROCEDURE: +; Magnify the Image if requested, threshold it with BINARY_ARRAY and use +; IMAGE_CORE to identify the central connected component of the resulting +; binary array. The area of the central connected component is an estimate +; of the area of the peak. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +FUNCTION peak_area, image, X = x, Y = y, MAG = mag_fac, CUBIC = cubic, $ + ABS_THRESHOLD = at, REL_THRESHOLD = rt + + on_error, 2 + if n_elements(mag_fac) eq 0 then mag_fac = 1 + mag = round(mag_fac) > 1 + if n_elements(x) * n_elements(y) eq 0 then begin + m = get_max(image) & x = m[0] & y = m[1] + endif + ; Image magnification + siz = size52(image, /DIM) * mag & ima = image + if mag gt 1 then $ + if keyword_set(cubic) then $ + ima = congrid(ima, siz[0], siz[1], CUBIC = -0.5) else $ + ima = rebin(ima, siz[0], siz[1]) + xm = round(x) * mag & ym = round(y) * mag + ; Thresholding and central component extraction + if n_elements(at) ne 0 then threshold = at $ + else begin + if n_elements(rt) eq 0 then rt = 0.5 + threshold = rt * ima[xm,ym] + endelse + ima = float(binary_array(ima, threshold)) + ima = image_core(ima, 0.5, X = xm, Y = ym, error_flag) + if error_flag then ima = 0. + ; Compute area + area = total(ima) / float(mag)^2 + if mag eq 1 then area = round(area) + return, area +end diff --git a/peak_fwhm.pro b/peak_fwhm.pro new file mode 100644 index 0000000000000000000000000000000000000000..500ee06cfb22fef055c203a67432211ea5274906 --- /dev/null +++ b/peak_fwhm.pro @@ -0,0 +1,52 @@ +; $Id: peak_fwhm.pro, v 1.0 Sep 1999 e.d. $ +; +;+ +; NAME: +; PEAK_FWHM +; +; PURPOSE: +; Compute the FWHM (Full Width at Half Maximum) of a peak in an image. +; +; CATEGORY: +; Signal processing. +; +; CALLING SEQUENCE: +; Result = PEAK_FWHM(Array) +; +; INPUTS: +; Same as FWHM (see the file 'fwhm.pro') +; +; KEYWORD PARAMETERS: +; Same as FWHM (see the file 'fwhm.pro') +; +; OUTPUTS: +; Same as FWHM (see the file 'fwhm.pro') +; +; RESTRICTIONS: +; Same as FWHM (see the file 'fwhm.pro') +; +; PROCEDURE: +; Fix a guess of the FWHM. Extract a sub-array centered on the peak +; of size equal to 3 times the guess of the FWHM. Compute again the +; FWHM and repeat until the size of the sub-array is greater or equal +; to 3 times the current FWHM. +; Compute the final estimate of the FWHM applying all the detailed +; input options supported by the routine FWHM. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, September 1999. +;- + +FUNCTION peak_fwhm, array, X = x, Y = y, _EXTRA = extra + + on_error, 2 + if n_elements(x) * n_elements(y) ne 0 then $ + ref = round([x, y]) else ref = get_max(array) + fw = 3. + repeat begin + siz = round(3 * fw) + peak = sub_array(array, siz, REF = ref) + fw = fwhm(peak) + endrep until siz ge round(3 * fw) + return, fwhm(peak, _EXTRA = extra) +end diff --git a/peak_width.pro b/peak_width.pro new file mode 100644 index 0000000000000000000000000000000000000000..9c5f7ffc522c51abc094a0d878e34aa394135af0 --- /dev/null +++ b/peak_width.pro @@ -0,0 +1,60 @@ +; $Id: peak_width.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; PEAK_WIDTH +; +; PURPOSE: +; Estimate the width of a peak in a given image. +; +; CATEGORY: +; Signal processing. +; +; CALLING SEQUENCE: +; Result = PEAK_WIDTH(Image) +; +; INPUTS: +; Image: Image containing the peak whose width must be measured +; +; KEYWORD PARAMETERS: +; X, Y: Coordinates of the peak. The default is the Image absolute maximum +; +; ABS_THRESHOLD: Threshold above which the peak must be measured. +; No default value is fixed +; +; REL_THRESHOLD: Relative threshold with respect to the peak value, +; used to fix the threshold above which the peak must be measured. +; The default is REL_THRESHOLD = 0.5, i.e. the width is estimated at +; half maximum. REL_THRESHOLD is neglected when ABS_THRESHOLD is +; defined and is used by default when ABS_THRESHOLD is undefined +; +; MAG: Integer magnification factor for array magnification. It may be +; useful to improve the accuracy of the measurement. The default is +; MAG = 1, i.e. no magnification. +; +; CUBIC: Set this keyword to a nonzero value to magnify the image with +; the cubic convolution interpolation implemented in the library +; routine CONGRID. This keyword has effect only if MAG is defined. +; If CUBIC is not set, the array magnification is performed with REBIN +; (bilinear interpolation). +; +; OUTPUTS: +; Result: Width of the Image component connected to the peak and above the +; fixed threshold. +; +; RESTRICTIONS: +; The input Image is supposed to have been background-subtracted. +; +; PROCEDURE: +; Call PEAK_AREA to estimate the area of the peak. Estimate the width +; as the diameter of a circle having the same section of the peak. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +FUNCTION peak_width, image, _EXTRA = extra + + on_error, 2 + return, sqrt(4 * peak_area(image, _EXTRA = extra) / !pi) +end diff --git a/phase2d.pro b/phase2d.pro new file mode 100644 index 0000000000000000000000000000000000000000..6dbee98ec22ac4b6058b304c6d16c52b92642afc --- /dev/null +++ b/phase2d.pro @@ -0,0 +1,47 @@ +; $Id: phase2d.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; PHASE2D +; +; PURPOSE: +; Compute the phase array for a 2D delta-function. +; +; CATEGORY: +; Mathematics. Transforms. +; +; CALLING SEQUENCE: +; Result = PHASE2D(X, Y, Sx, Sy) +; +; INPUTS: +; X, Y: x- and y- position of delta-function (not necessarily integer +; coordinates) +; Sx, Sy: x- and y- size of phase array +; +; KEYWORD PARAMETERS: +; DOUBLE_P: Set this keyword to compute a double precision phase array +; +; OUTPUTS: +; Return a 2D complex phase array. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +FUNCTION phase2d, x, y, sx, sy, DOUBLE_P = double_p + + on_error, 2 + if keyword_set(double_p) then pi = !Dpi else pi = !pi + arg = frequency(sx) * 2 * pi / sx + arg_u = arg * x + if sy ne sx then arg = frequency(sy) * 2 * pi / sy + arg_v = arg * y + if keyword_set(double_p) then begin + uphase = dcomplex(cos(arg_u),-sin(arg_u)) + vphase = dcomplex(cos(arg_v),-sin(arg_v)) + endif else begin + uphase = complex(cos(arg_u),-sin(arg_u)) + vphase = complex(cos(arg_v),-sin(arg_v)) + endelse + return, uphase # vphase +end diff --git a/photon_noise.pro b/photon_noise.pro new file mode 100644 index 0000000000000000000000000000000000000000..0b521259eb32e392e19312455429a7db922f9448 --- /dev/null +++ b/photon_noise.pro @@ -0,0 +1,47 @@ +; $Id: photon_noise, v 1.2 May 2014 e.d. $ +; +;+ +; NAME: +; PHOTON_NOISE +; +; PURPOSE: +; Evaluate the standard deviation of the photon noise in an image +; given by the sum or the mean of many exposures. +; +; CATEGORY: +; Signal processing. +; +; CALLING SEQUENCE: +; Result = PHOTON_NOISE(Data, El_per_adu, Nexp) +; +; INPUTS: +; Data: Array of data affected by photon noise (in ADU) +; +; El_per_adu: Conversion constant from el. to ADU +; (No. of ADU = No. of el. / El_per_adu) +; +; Nexp: Number of exposures (with the same exposure time) +; +; KEYWORD PARAMETERS: +; AVG: Set this keyword to specify that the image is the average of +; Nexp exposures. If AVG is not set, the routine assumes the image +; is the sum of Nexp exposures. +; +; OUTPUTS: +; Result: Array with the same size as the input Data, representing the +; photon noise standard deviation in ADU +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, May 2000. +; 1) Set to 0 negative data before computing SQRT! +; (Emiliano Diolaiti, August 2000). +; 2) Updated documentation (E. D., May 2014). +;- + + +FUNCTION photon_noise, data, el_per_adu, nexp, AVG = avg + + on_error, 2 + norm = 1. & if keyword_set(avg) then norm = 1. / nexp + return, sqrt(norm / el_per_adu * (data > 0)) +end diff --git a/pick_region.pro b/pick_region.pro new file mode 100644 index 0000000000000000000000000000000000000000..af308de63d3c54222c138e3196cc7440cb82911a --- /dev/null +++ b/pick_region.pro @@ -0,0 +1,56 @@ +; $Id: pick_region.pro, v 1.2 Mar 2012 e.d. $ +; +;+ +; NAME: +; PICK_REGION +; +; PURPOSE: +; Given the bounds of a partition of a rectangular domain on a plane +; and the coordinates of a point on the same plane, compute the +; subscript of the sub-region containing the specified point. +; +; CATEGORY: +; Array processing. +; +; CALLING SEQUENCE: +; Result = PICK_REGION(Lx, Ux, Ly, Uy, X, Y) +; +; INPUTS: +; Lx, Ux: Vectors of bounds of the partition along the x-axis. +; +; Ly, Uy: Vectors of bounds of the partition along the y-axis. +; +; X, Y: Coordinates of the point falling on some sub-region. +; +; OUTPUTS: +; Result: Scalar subscript of the sub-region containing the point +; of coordinates (X, Y). +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, January 2000. +; 1) Fixed bug on output Result, that was NOT scalar (E.D. August 2005). +; 2) Modified condition for WHERE: check includes left boundary and +; excludes right boundary (E.D., March 2012). +;- + +FUNCTION pick_region, lx, ux, ly, uy, x, y + + on_error, 2 + nx = n_elements(lx) & x_rep = replicate(x, nx) + ny = n_elements(ly) & y_rep = replicate(y, ny) + wx = where(x_rep ge lx and x_rep lt ux) + wy = where(y_rep ge ly and y_rep lt uy) + return, wy[0] * nx + wx[0] +end + + + + +; on_error, 2 +; nx = n_elements(lx) & x_rep = replicate(round(x), nx) +; ny = n_elements(ly) & y_rep = replicate(round(y), ny) +; wx = where(x_rep ge lx and x_rep le ux) +; wy = where(y_rep ge ly and y_rep le uy) +; return, wy[0] * nx + wx[0] +;end + diff --git a/plane.pro b/plane.pro new file mode 100644 index 0000000000000000000000000000000000000000..016cdbea948b03980ec6406b9c637cc7bcab0300 --- /dev/null +++ b/plane.pro @@ -0,0 +1,39 @@ +; $Id: plane.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; PLANE +; +; PURPOSE: +; Compute a 3D plane, defined as +; P(x, y) = C + Cx * x + Cy * y +; +; CATEGORY: +; Models. +; +; CALLING SEQUENCE: +; Result = PLANE(C, Cx, Cy, Sx, Sy) +; +; INPUTS: +; C, Cx, Cy: Coefficients of the plane +; +; Sx: First size of the output array +; +; OPTIONAL INPUTS: +; Sy: Second size of the output array. The default is Sy = Sx +; +; OUTPUTS: +; Result: 2D floating-point array, containing the values of the plane +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +FUNCTION plane, c, cx, cy, sx, sy + + on_error, 2 + if n_params() eq 4 then sy = sx + p = c + cx * (findgen(sx) # (fltarr(sy) + 1)) + p = temporary(p) + cy * ((fltarr(sx) + 1) # findgen(sy)) + return, p +end diff --git a/psf.fits b/psf.fits new file mode 100644 index 0000000000000000000000000000000000000000..528c6ff6e2efb43e4c4188db14b1ffc9c68a2174 Binary files /dev/null and b/psf.fits differ diff --git a/psf_extract.pro b/psf_extract.pro new file mode 100644 index 0000000000000000000000000000000000000000..16ddc704f3dc072b346304e22c86963ab970e61c --- /dev/null +++ b/psf_extract.pro @@ -0,0 +1,299 @@ +; $Id: psf_extract.pro, v 1.3 Aug 2004 e.d. $ +; +;+ +; NAME: +; PSF_EXTRACT +; +; PURPOSE: +; Estimate the PSF in a stellar field image by combination of a stack +; of user-selected stars. +; The "PSF stars" are cleaned from the contamination of secondary sources, +; background-subtracted, centered with sub-pixel accuracy, normalized and +; combined by median superposition. +; It is possible to weigh the frames in the stack before superposition and +; mask sub-regions above a pre-fixed threshold, which may suitably coincide +; with the detector saturation level. Saturated stars are approximately +; repaired, by replacing their core with a preliminary estimate of the PSF. +; +; CATEGORY: +; Signal processing. Stellar photometry. +; +; CALLING SEQUENCE: +; PSF_EXTRACT, X, Y, X_secondary, Y_secondary, $ +; Image, Background, Psf_size, Psf, Psf_fwhm +; +; INPUTS: +; X, Y: Coordinates of stars to superpose +; +; X_secondary, Y_secondary: Coordinates of secondary sources to subtract +; +; Image: Image of the stellar field +; +; Psf_size: Size of output PSF in pixels +; +; OPTIONAL INPUTS: +; Background: Array, with the same size as Image, containing an estimate of the +; background emission. +; +; KEYWORD PARAMETERS: +; NOBACKGROND: Set this keyword to specify that the image has no +; background (or the background in the input Image has already been +; subtracted). +; +; N_FWHM_BACK: Size of box for background sampling (see IMAGE_BACKGROUND +; for details) in units of the PSF FWHM. The default is N_FWHM_BACK = 5. +; For other keywords related to background estimation (e.g. SKY_MEDBOX) +; see the function IMAGE_BACKGROUND in the file "image_background.pro". +; This keyword is applied only if NOBACKGROUND is not set and the optional +; input Background is undefined. +; +; SKY_MEDIAN: Set this keyword to estimate the background by median +; smoothing of the input Image, instead of using IMAGE_BACKGROUND. +; +; ITER: The secondary sources around the PSF stars are fit and subtracted +; using a preliminary estimate of the PSF. This procedure may be repeated +; any number of times, as indicated by the keyword ITER. The default is +; ITER = 2. Set ITER = 0 to avoid subtracting the secondary sources. +; +; N_FWHM_FIT: Width of box used to fit secondary sources. +; The box size must be specified in units of the PSF FWHM. +; The default value is N_FWHM_FIT = 2. +; +; INTERP_TYPE: Set this keyword to a string identifying the interpolation +; technique to use for sub-pixel centering. For details, see the function +; IMAGE_SHIFT in the file "image_shift.pro". Recommended values for the +; PSF extraction procedure are INTERP_TYPE = "S" or INTERP_TYPE = "I". +; +; UPPER_LEVEL: Use this keyword to provide the saturation threshold which +; is used to mask the core of bright saturated stars when performing the +; median superposition. Though saturated, these stars should not be +; rejected, because they provide useful information on the PSF halo. +; The value of the keyword UPPER_LEVEL may be: +; a) a scalar, representing the unique threshold to be used all over +; the frame; +; b) a 2D array, having the same size as the input Image, when the +; saturation level is not spatially uniform. This happens for +; example when the input Image has been background subtracted: +; if the removed contribution is not negligible with respect to +; the stellar peaks, the saturation threshold will be affected and +; should be defined as the original threshold minus the removed +; background contribution. +; +; N_FWHM_MATCH: Size of "search box", expressed in units of the PSF FWHM, +; where the PSF must moved to optimize the correlation with a given +; saturated star to repair. Notice that the correlation is maximized +; after masking the pixels in the saturated core. +; The default is N_FWHM_MATCH = 1. +; +; N_WIDTH: Size of the inner portion of a saturated star to replace. +; It must be expressed in units of the saturated core diameter, which +; is computed automatically by the program. +; The default is N_WIDTH = 3. +; +; MAG_FAC: Integer representing the fractional sub-pixel step for +; accurate positioning of the PSF estimate on the core of a saturated +; star. This "magnification factor" is also used to optimize the +; correlation. +; The default is MAG_FAC = 2, corresponding to a positioning accuracy +; of 1/2 pixel. +; +; MAX_NORM: Set this keyword to normalize the stars to unitary maximum +; before combining them in a single frame. The default is to normalize +; each sub-image to total unit flux. +; +; RAD_NORM: Set this keyword to normalize the stars using a circular +; region centered on the stellar peak. The value of this keyword is the +; radius of the normalization region. The default is to normalize +; each sub-image to total unit flux. +; +; AVGTYPE: Set this keyword to choose a combination algorithm among +; the following possibilities: +; AVGTYPE = 0 [default]: pixel-by-pixel average +; = 1: average with sigma-rejection of outliers +; = 2: median +; +; WEIGHTED: Set this keyword to weigh the stellar images, before averaging, +; according to their signal-to-noise ratio in the photon noise case. +; +; NONORM: Set this keyword to avoid normalizing the final PSF estimate. +; +; OUTPUTS: +; Image: Same as input Image, with corrected saturated stars +; +; Psf: 2D array of size Psf_size*Psf_size with the estimated PSF, +; normalized to unit total flux +; +; Psf_fwhm: FWHM of estimated PSF +; +; Background: Estimate of the background emission +; +; X, Y: If there are saturated stars among the ones to superpose, their +; position might slightly change after repair. +; +; SIDE EFFECTS: +; 1) May create modal widgets by means of DIALOG_MESSAGE. +; 2) Modify the input parameters Image, Clean_image, X, Y if there are +; saturated stars to repair among the stars to superpose. +; +; RESTRICTIONS: +; 1) The PSF_EXTRACT routine is useful to extract an estimate of the PSF +; provided the field of view is reasonably isoplanatic. +; If this condition is not fulfilled, PSF_EXTRACT may still be used to +; extract a PSF estimate from isoplanatic sub-sections in the image. +; 2) Centering and fitting stellar images is based on data interpolation. +; Interpolation techniques are not suited to undersampled data. +; 3) Saturated stars may be useful to form a PSF estimate because they +; provide information on the PSF halo. If there are saturated stars in the +; Image, they should be repaired, to ensure proper normalization. +; 4) Saturated stars are identified as those peaks among the selected PSF +; stars with an intensity greater than the saturation threshold. +; 5) Secondary sources around PSF stars are supposed to be unsaturated. +; +; PROCEDURE: +; Before calling PSF_EXTRACT, the user has to select the candidate +; "PSF stars" and the contaminating sources around each of them. +; A preliminary estimate of the PSF is obtained superposing the unsaturated +; stars with the routine SUPERPOSE_STARS (see the file "superpose_stars.pro"), +; and is used to fit and subtract the secondary sources, thus cleaning the +; PSF stars. This process may be repeated iteratively: at every step the +; accuracy of the "fit + subtract" sequence is supposed to increase, even +; though in practice one iteration is often enough. +; A shifted scaled replica of the current PSF estimate is used to replace +; the core of bright saturated stars: the optimal match is found by +; cross-correlation maximization for the relative shift and by a least +; squares fitting of the PSF wings for the scaling factor. +; Finally all the candidate PSF stars, including the repaired ones, are +; superposed to form the last PSF estimate, suitably masking the core of +; saturated stars. +; In every phase described above, the stellar images are background- +; subtracted, centered with sub-pixel accuracy and normalized before +; combination. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August-September 1999. +; Updates: +; 1) Added keyword REFERENCE_PIX in call to IMAGE_MODEL +; (Emiliano Diolaiti, December 1999). +; 2) Modified some keywords, required by called routine SUPERPOSE_STARS +; (Emiliano Diolaiti, September 2001). +; 3) Added keyword NOBACKGROUND +; (Emiliano Diolaiti, September 2001). +; 4) Background turned to optional input: if it is set, it is not estimated +; by PSF_EXTRACT (E. D., August 2004). +;- + + + +;;; Auxiliary procedures/functions. + +; PSF_CLEAN: Subtract secondary sources around PSF stars. + +FUNCTION psf_clean, image, psf, x, y, fitbox, _EXTRA = extra + + on_error, 2 + clean_image = image & siz = size52(image, /DIM) + fitting_psf = sub_array(psf, 2*fitbox) + reference_pix = size52(psf, /DIM) / 2 + fit_data = ptr_new(/ALLOCATE) & model_data = ptr_new(/ALLOCATE) + nstars = n_elements(x) + for n = 0, nstars - 1 do begin + sub_image = sub_array(clean_image, fitbox, $ + REFERENCE = [x[n], y[n]], LX = lx, LY = ly) + fitstars, sub_image, fitting_psf, x[n] - lx, y[n] - ly, $ + PSF_DATA = fit_data, _EXTRA = extra, xn, yn, fn, b, fit_error + if fit_error ge 0 then begin + xn = xn + lx & yn = yn + ly + clean_image = temporary(clean_image) - $ + image_model(xn, yn, fn, siz[0], siz[1], psf, model_data, $ + REFERENCE_PIX = reference_pix, _EXTRA = extra) + endif + endfor + ptr_free, fit_data, model_data + return, clean_image +end + + + +;;; The main routine. + +PRO psf_extract, $ + x, y, x_secondary, y_secondary, $ + image, psf_size, psf, psf_fwhm, background, $ + NOBACKGROUND = nobackground, N_FWHM_BACK = n_fwhm_back, $ + N_FWHM_FIT = n_fwhm_fit, ITER = iter, UPPER_LEVEL = upper_lev, $ + RAD_NORM = norm_rad, NONORM = nonorm, _EXTRA = extra + + on_error, 2 + + ; Define unsaturated stars + x_unsat = x & y_unsat = y & peaks = image[x, y] + if n_elements(upper_lev) ne 0 then begin + w = where(peaks lt upper_lev, count) + if count eq 0 then begin + id = dialog_message(/ERROR, "Please select at least one unsaturated star.") + return + endif + x_unsat = x_unsat[w] & y_unsat = y_unsat[w] & peaks = peaks[w] + endif + ; Select the brightest unsaturated star + m = max(peaks, w) & x0 = x_unsat[w] & y0 = y_unsat[w] + ; Use this star to estimate the PSF FWHM + psf_fwhm = peak_fwhm(image, X = x0, Y = y0, MAG = 3, /CUBIC, _EXTRA = extra) + ; Estimate the image background, if necessary + if keyword_set(nobackground) then begin + siz = size52(image, /DIM) + background = fltarr(siz[0], siz[1]) + endif else $ + if n_elements(background) eq 0 then begin + if n_elements(n_fwhm_back) eq 0 then n_fwhm_back = 5 + backbox = round(n_fwhm_back * psf_fwhm) + background = estimate_background(image, backbox, /CUBIC, _EXTRA = extra) + endif + if not keyword_set(nobackground) then $ + psf_fwhm = peak_fwhm(image - background, X = x0, Y = y0, MAG = 3, /CUBIC) + + ; Compute preliminary estimate of the PSF using unsaturated stars + if n_elements(norm_rad) ne 0 then rad = norm_rad * psf_fwhm + psf = superpose_stars(image - background, x_unsat, y_unsat, psf_size, $ + RAD_NORM = rad, CENTROID_BOX = round(psf_fwhm), _EXTRA = extra) + + ; Fit and subtract secondary sources and refine PSF using unsaturated stars + if n_elements(iter) eq 0 then iter = 2 + if n_elements(x_secondary) eq 0 or n_elements(y_secondary) eq 0 then iter = 0 + if n_elements(n_fwhm_fit) eq 0 then n_fwhm_fit = 2 + fitbox = round(n_fwhm_fit * psf_fwhm) ; fitting box + if iter eq 0 then clean_image = image + for it = 0L, iter - 1 do begin + clean_image = psf_clean(image, psf, x_secondary, y_secondary, $ + fitbox, _EXTRA = extra) + psf = superpose_stars(clean_image - background, x_unsat, y_unsat, $ + psf_size, RAD_NORM = rad, CENTROID_BOX = round(psf_fwhm), $ + _EXTRA = extra) + endfor + psf_fwhm = fwhm(psf, MAG = 3, /CUBIC) + + ; Repair saturated stars and compute last estimate of the PSF, + ; using all the selected stars + n_satur = 0 + if n_elements(upper_lev) ne 0 then begin + w = where(image[x, y] ge upper_lev, n_satur) + if n_satur ne 0 then begin + x_satur = x[w] & y_satur = y[w] + endif + endif + if n_satur ne 0 then begin + repair_saturated, image, clean_image, background, psf, psf_fwhm, $ + x_satur, y_satur, upper_lev, _EXTRA = extra + x = [x_satur, x_unsat] + y = [y_satur, y_unsat] + psf = superpose_stars(clean_image - background, x, y, psf_size, $ + SATURATION = upper_lev - background, $ + RAD_NORM = rad, CENTROID_BOX = round(psf_fwhm), $ + _EXTRA = extra) + psf_fwhm = fwhm(psf, MAG = 3, /CUBIC) + endif + + if not keyword_set(nonorm) then psf = psf / total(psf) + + return +end diff --git a/psf_help.txt b/psf_help.txt new file mode 100644 index 0000000000000000000000000000000000000000..7e4b7e653f693875b1441c644bf3ee22a0dc4b4f --- /dev/null +++ b/psf_help.txt @@ -0,0 +1,27 @@ + 'PSF' menu help page + + + The 'PSF' menu contains the following sub-menus: + + 'Load': + Load a PSF array from a FITS file. + + 'Extract from image': + Extract the PSF from the stellar field image, by superposition + of a set of user-selected stars (referred to as 'PSF stars'). + This task may be used also to repeat the PSF extraction procedure + after analyzing the image, in order to improve the PSF estimate + using the available knowledge of the background emission and of + the secondary sources that contaminate the 'PSF stars'. + + 'Post process': + Modify the support of the retrieved PSF and smooth the PSF halo. + + 'Normalize': + Normalize the PSF array to total flux = 1. This task may be useful + to normalize an external PSF, loaded from a file. Notice that the + PSF loaded, extracted or processed within XStarFinder is always + normalized automatically. + + 'Save': + Save to a FITS file the current estimate of the PSF. diff --git a/psfstars.txt b/psfstars.txt new file mode 100644 index 0000000000000000000000000000000000000000..5ff352614f8d9c90a42a88c955578fe1b3a6e916 --- /dev/null +++ b/psfstars.txt @@ -0,0 +1,4 @@ + 141 239 + 264 245 + 231 322 + 238 89 diff --git a/radial_dist.pro b/radial_dist.pro new file mode 100644 index 0000000000000000000000000000000000000000..1927b070bb170c18cd049763eb563abc6cf3224c --- /dev/null +++ b/radial_dist.pro @@ -0,0 +1,39 @@ +; $Id: radial_dist.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; RADIAL_DIST +; +; PURPOSE: +; Compute a 2D array of radial distances from an origin. +; +; CATEGORY: +; Models. +; +; CALLING SEQUENCE: +; Result = RADIAL_DIST(X_size, Y_size, X_center, Y_center) +; +; INPUTS: +; X_size, Y_size: X- and y- size of output array +; +; X_center, Y_center: Coordinates of origin, not necessarily integer +; +; OUTPUTS: +; Result: 2D array of radial distances from (X_center, Y_center) +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +FUNCTION radial_dist, x_size, y_size, x_center, y_center + + on_error, 2 + z = findgen(x_size) - x_center & aux = make_array(y_size, VALUE = 1) + z = temporary(z) # aux + d = z^2 + z = findgen(y_size) - y_center & aux = make_array(x_size, VALUE = 1) + z = aux # temporary(z) + d = temporary(d) + z^2 + d = sqrt(temporary(d)) + return, d +end diff --git a/read_float_data.pro b/read_float_data.pro new file mode 100644 index 0000000000000000000000000000000000000000..25db04fa3d25b95e8c4fe445e2858205d74d65d0 --- /dev/null +++ b/read_float_data.pro @@ -0,0 +1,73 @@ +; $Id: read_float_data, v 1.2 Aug 2008 e.d. $ +; +;+ +; NAME: +; READ_FLOAT_DATA +; +; PURPOSE: +; Read a Free Format file containing floating-point numbers, +; ordered by columns. +; +; CATEGORY: +; Input/Output. +; +; CALLING SEQUENCE: +; Result = READ_FLOAT_DATA(File, Ncolumns) +; +; INPUTS: +; File: File name (string) +; +; Ncolumns: Number of columns in the input file +; +; KEYWORD PARAMETERS: +; MSG: Set this keyword to display a dialog message in case +; of input error. +; +; OUTPUTS: +; Result: Floating point array of size Ncolumns * Nrows, where Nrows +; is the number of rows in the input file, determined by the routine. +; If an error occurs, a scalar string is returned, with the message +; 'I/O error'. +; +; RESTRICTIONS: +; The numbers in the input file MUST be written in rows of Ncolumns +; elements each. After the last number, only a 'carriage return' or no +; characters at all are allowed. If the last number in the last line is +; followed by one or more lines with one or more blank spaces, an error +; occurs. +; In practice this routine may be used to read data previously written by +; the IDL intrinsic procedure PRINTF. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, September 1999. +; Updates: +; 1) Added Free_Lun instruction (Emiliano Diolaiti, March 2000). +; 2) Modified algorithm (now faster): count line first, then read array +; of data (E. D., August 2008). +;- + +FUNCTION read_float_data, file, ncolumns, MSG = msg + + catch, i_error + if i_error ne 0 then begin + if keyword_set(msg) then $ + m = dialog_message(/ERROR, 'Input error') + if n_elements(u) ne 0 then free_lun, u + return, 'I/O error' + endif + openr, u, file, /GET_LUN + data = fltarr(ncolumns) + n = 0L + while not eof(u) do begin + readf, u, data + n = n + 1 + endwhile + if n ne 0 then begin + close, u + data = fltarr(ncolumns, n) + openr, u, file + readf, u, data + endif + close, u & free_lun, u + return, data +end diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000000000000000000000000000000000000..01de6068d6ed03c23aa9761e8c79949930e08aa3 --- /dev/null +++ b/readme.txt @@ -0,0 +1,67 @@ +StarFinder v 1.8.2a + + +WEB PAGE + +http://davide2.bo.astro.it/?page_id=5128 + + +ARCHIVE CONTENT + +This archive includes the following files: ++) *.pro: + IDL source code (tested under IDL v 8.3) ++) *_help.txt: + ASCII files containing the help pages of the + IDL Graphical User Interface ++) list_of_modules.txt: + list of IDL source files with a short description of + each routine ++) other files (*.fits, *stars.txt): + simulated stellar field data to be used as a tutorial + example. The files are + synfield.fits: noisy image of the stellar field + psf.fits: PSF used to create the stellar field + background.fits: background added to the stellar field + noise.fits: array of noise standard deviation for + each pixel in the image + stars.txt: list of positions and fluxes of the stars + in the simulated stellar field + psfstars.txt: list of positions of the stars to be + used for PSF estimation + + +INSTALLATION + +Open the compressed archive StarFinder.zip and put all its +content in a new directory named 'starfinder'. Then modify the +IDL system variable !Path including the path to the new directory. + +StarFinder requires some routines of +THE IDL ASTRONOMY USER'S LIBRARY (http://idlastro.gsfc.nasa.gov). + + +INFO / BUGS REPORT + +Please send information requests and bug reports to: +Emiliano Diolaiti (emiliano.diolaiti@oabo.inaf.it) +Laura Schreiber (laura.schreiber@oabo.inaf.it) + + +COPYRIGHT + +The StarFinder software is provided "as is" without express or implied warranty. +It can be used freely for research and educational purposes. + +Please reference the authors in any publication resulting from +the use of the StarFinder code. The references are: + +Diolaiti E., Bendinelli O., Bonaccini D., Close L.M., Currie D.G., +Parmeggiani G., Astronomy & Astrophysics Supplement Series, 147, +335, 2000 + +Schreiber L., Diolaiti E., Bellazzini M., Ciliegi P., Foppiani I., +Greggio L., Lanzoni B., Lombini M., Second International Conference +on Adaptive Optics for Extremely Large Telescopes, +Online at http://ao4elt2.lesia.obspm.fr, id.P57, +Bibliographic Code 2011aoel.confP..57S, 2011 diff --git a/reciprocal_distance.pro b/reciprocal_distance.pro new file mode 100644 index 0000000000000000000000000000000000000000..02b131655c593d32a7af28df34041a5324392c3b --- /dev/null +++ b/reciprocal_distance.pro @@ -0,0 +1,44 @@ +; $Id: reciprocal_distance.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; RECIPROCAL_DISTANCE +; +; PURPOSE: +; Compute the euclidean distance for each couple of points +; in a set of given points on a plane. +; +; CATEGORY: +; Mathematics. +; +; CALLING SEQUENCE: +; Result = RECIPROCAL_DISTANCE(X, Y) +; +; INPUTS: +; X, Y: Coordinates of N points +; +; OUTPUTS: +; Result: (N-1)*(N-1) floating-point array of reciprocal distances. +; The elements of the array are defined as follows: +; Result[j,*] = vector of distances between the j-th point and +; the others, for j = 0, ..., N - 2. +; Result is 0 if N = 1. It is set to a negative scalar if X and Y +; have different size. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +FUNCTION reciprocal_distance, x, y + + on_error, 2 + n = n_elements(x) + if n_elements(y) ne n then return, -1 + if n eq 1 then return, 0 + d = fltarr(n - 1, n - 1) & s = lindgen(n) + for i = 0L, n - 2 do begin + other = (shift(s, -i))[1:n-1] & other = shift(other, +i) + d[*,i] = distance(x[i], y[i], x[other], y[other]) + endfor + return, transpose(d) +end diff --git a/relative_error.pro b/relative_error.pro new file mode 100644 index 0000000000000000000000000000000000000000..86f9169ae26f7115595f99fe8adb3ba78da1b803 --- /dev/null +++ b/relative_error.pro @@ -0,0 +1,40 @@ +; $Id: relative_error.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; RELATIVE_ERROR +; +; PURPOSE: +; Compute the relative difference between two IDL variables. +; +; CATEGORY: +; Tools. +; +; CALLING SEQUENCE: +; Result = RELATIVE_ERROR(Var1, Var2) +; +; INPUTS: +; Var1: First IDL variable +; +; Var2: Second IDL variable +; +; OUTPUTS: +; Return the relative error of Var2 with respect to Var1. +; +; PROCEDURE: +; Compute the quantity +; (Var2 - Var1) / Var1 +; over the support of Var1 (set of pixels where Var1 is not 0). +; Outside the support of Var1, i.e. where Var1 is 0, the relative +; error is 1 (100%) by definition. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +FUNCTION relative_error, var1, var2 + + error = float(var1 - var1) + 1 & w = where(var1 ne 0, count) + if count ne 0 then error[w] = (var2 - var1)[w] / var1[w] + return, error +end diff --git a/remove_coincident.pro b/remove_coincident.pro new file mode 100644 index 0000000000000000000000000000000000000000..237a83b93ef1705ee15a9da0bb12e72fcc6ac908 --- /dev/null +++ b/remove_coincident.pro @@ -0,0 +1,67 @@ +; $Id: remove_coincident.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; REMOVE_COINCIDENT +; +; PURPOSE: +; Given a set of points on a plane, remove multiple occurences of +; coincident points. +; +; CATEGORY: +; Array manipulation. +; +; CALLING SEQUENCE: +; REMOVE_COINCIDENT, X, Y, X_distinct, Y_distinct +; +; INPUTS: +; X, Y: X- and y- coordinates of points +; +; OUTPUTS: +; X_distinct, Y_distinct: Coordinates of distinct points. The same +; variables used on input may be used for the output +; +; RESTRICTIONS: +; Apply only to points on a plane. +; +; PROCEDURE: +; Recursive procedure: given a subset made of N-1 distinct points, +; consider the next point in the original list and append it if +; distinct from the first N-1. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +; 1) Removed call to obsolete routine APPEND_ELEMENTS +; (Emiliano Diolaiti, June 2001). + + + +; ADD_DISTINCT: auxiliary procedure called by REMOVE_COINCIDENT. + +PRO add_distinct, x, y, x_out, y_out, n + + on_error, 2 + if n eq 0 then begin + ; base case + x_out = x[n] & y_out = y[n] + endif else begin + ; induction case + add_distinct, x, y, x_out, y_out, n - 1 + if min(distance(x[n], y[n], x_out, y_out)) ne 0 then begin + x_out = [x_out, x[n]] + y_out = [y_out, y[n]] + endif + endelse + return +end + + +PRO remove_coincident, x, y, x_distinct, y_distinct + + on_error, 2 + npt = n_elements(x) + if npt eq 0 or n_elements(y) ne npt then return + add_distinct, x, y, x_out, y_out, npt - 1 + x_distinct = x_out & y_distinct = y_out + return +end diff --git a/repair_saturated.pro b/repair_saturated.pro new file mode 100644 index 0000000000000000000000000000000000000000..b60017c9d1c530ffb645e9d094b307361601445b --- /dev/null +++ b/repair_saturated.pro @@ -0,0 +1,185 @@ +; $Id: repair_saturated.pro, v 1.1 Jul 2000 e.d. $ +; +;+ +; NAME: +; REPAIR_SATURATED +; +; PURPOSE: +; Repair saturated stars in an image, by replacing their core with a +; template representing an estimate of the PSF. Accurate positioning +; of the template onto each star is performed by correlation +; maximization, whereas the scaling factor is computed by fitting the +; wings of the saturated source. +; This procedure gives acceptable results when the size of the +; saturated region of a given star does not exceed the diameter of +; the central core of the PSF. +; +; CATEGORY: +; Signal processing. Stellar photometry. +; +; CALLING SEQUENCE: +; REPAIR_SATURATED, Image, Clean_image, Background, $ +; Psf, Psf_fwhm, X, Y, Upper_lev +; +; INPUTS: +; Image: Stellar field image containing saturated stars. +; +; Clean_image: Stellar field image, after subtraction of non-saturated +; sources. It may coincide with Image, if there are no important +; sources other than the saturated ones. +; +; Background: 2D array, with the same size as Image, containing an +; estimate of the background emission. +; +; Psf: 2D array, containing an estimate of the PSF, to be used as a +; template to repair the core of the saturated stars. +; +; Psf_fwhm: FWHM of the PSF. +; +; X, Y: X- and Y- coordinates of saturated stars. +; +; Upper_lev: Scalar, representing the presumed saturation threshold. +; +; KEYWORD PARAMETERS: +; N_FWHM_MATCH: Size of "search box", expressed in units of the PSF FWHM, +; where the PSF must moved to optimize the correlation with a given +; saturated star to repair. Notice that the correlation is maximized +; after masking the pixels in the saturated core. +; The default is N_FWHM_MATCH = 1. +; +; N_WIDTH: Size of the inner portion of a saturated star to replace. +; It must be expressed in units of the saturated core diameter, which +; is computed automatically by the program. +; The default is N_WIDTH = 3. +; Notice that the box size for repair is limited by the size of the +; Psf array. +; +; MAG_FAC: Integer representing the fractional sub-pixel step for +; accurate positioning of the PSF estimate on the core of a saturated +; star. This "magnification factor" is also used to optimize the +; correlation. +; The default is MAG_FAC = 2, corresponding to a positioning accuracy +; of 1/2 pixel. +; +; INTERP_TYPE: Use this keyword to choose an interpolation technique +; for the PSF fractional shift when MAG_FAC > 1. See IMAGE_SHIFT in +; the file "image_shift.pro" for more details. The default is to +; use cubic convolution interpolation. +; +; OUTPUTS: +; Image: Image array with repaired saturated stars. +; +; Clean_image: Input Clean_image with repaired saturated stars. +; +; X, Y: Coordinates of saturated stars after repair. +; +; SIDE EFFECTS: +; The input variables Image, Clean_image, X and Y are overwritten. +; +; RESTRICTIONS: +; It is assumed that the saturated stars are separated by a distance +; at least (N_WIDTH * Width), where N_WIDTH is the input keyword described +; above and Width is the maximum diameter in pixels of a saturated core. +; +; PROCEDURE: +; The input image is temporarily cleaned from the contamination of +; secondary sources and background emission. Then the saturated sources +; are isolated and repaired with a scaled replica of the PSF template. +; Accurate positioning of the template onto each star is performed by +; correlation maximization, whereas the scaling factor is computed by +; fitting the wings of the saturated source. Of course the saturated core +; is masked when computing the correlation coefficient and the scaling +; factor. +; When all the saturated sources specified on input have been repaired, +; the image is restored adding the previously subtracted stars and +; the background emission. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August-September 1999. +; Updates: +; 1) Fixed bug on correlation maximization in module MATCH_REPLACE +; (Emiliano Diolaiti, July 2000). +;- + + + + +;;; Auxiliary routines. + +; MATCH_REPLACE: repair the core of a saturated star, +; given an unsaturated template. + +PRO match_replace, image, template, x, y, box, search_box, $ + upper_surface, MAG_FAC = mag_fac, _EXTRA = extra + + on_error, 2 + ; Define saturated star, template, upper surface for matching + star = sub_array(image, box + 2*(search_box/2), REF = [x, y], $ + LX = lx, UX = ux, LY = ly, UY = uy) + star_ref = [x - lx, y - ly] & temp_ref = get_max(template) + upper = upper_surface[lx:ux,ly:uy] + if n_elements(mag_fac) eq 0 then mag_fac = 2 + mag = round(mag_fac > 1) + if mag gt 1 then $ + shifted_templates, template, mag, _EXTRA = extra, templates, dx, dy + ; Find optimal position by cross-correlation maximization + w = where(star ge upper, count) + subs_to_coord, w, (size52(star, /DIM))[0], x_core, y_core + correlate_max, star, template, star_ref[0], star_ref[1], search_box, $ + XT = temp_ref[0], YT = temp_ref[1], $ + X_BAD = x_core, Y_BAD = y_core, $ + TEMPLATES = templates, DX = dx, DY = dy, c, x, y + ; Compute optimal scaling factor + if mag gt 1 then begin + dx_opt = round(mag * (x - round(x))) + dy_opt = round(mag * (y - round(y))) + w = where(round(mag * dx) eq dx_opt and round(mag * dy) eq dy_opt) + temp = templates[*,*,w] + endif else temp = template + extract_overlap, star, temp, round([x, y]), temp_ref, $ + star, temp, lxs, uxs, lys, uys + x = x + lx & y = y + ly + lx = lx + lxs & ly = ly + lys & upper = upper[lxs:uxs,lys:uys] + w = where(star lt upper) + scale = total(temp[w]*star[w])/total(temp[w]^2) + ; Repair image + w = where(star ge upper) + star[w] = scale * temp[w] + image[lx,ly] = star + return +end + + + +;;; The main routine. + +PRO repair_saturated, image, clean_image, background, psf, psf_fwhm, x, y, $ + upper_lev, N_FWHM_MATCH = n_fwhm, N_WIDTH = n_width, $ + _EXTRA = extra + + on_error, 2 + ; The image to use to repair saturated stars must be: + ; 1) background removed + ; 2) cleaned from secondary stars around saturated ones. + sec_sources = image - clean_image + clean_image = temporary(clean_image) - background + upper_surf = upper_lev - background - sec_sources + ; Match and repair saturated stars in clean_image + n_satur = n_elements(x) + if n_elements(n_fwhm) eq 0 then n_fwhm = 1 + if n_elements(n_width) eq 0 then n_width = 3 + for n = 0L, n_satur - 1 do begin + width = peak_width(clean_image, MAG = 1, X = x[n], Y = y[n], $ + ABS_THRESH = upper_surf) + box = round(n_width * width) < min(size52(psf, /DIM)) + search_box = round(n_fwhm * psf_fwhm) + x_n = x[n] & y_n = y[n] + match_replace, clean_image, psf, x_n, y_n, box, search_box, upper_surf, _EXTRA = extra + x[n] = x_n & y[n] = y_n + endfor + ; Define clean_image = input image with repaired stars - secondary sources + clean_image = temporary(clean_image) + background + ; Define image = original image with repaired stars + image = clean_image + sec_sources + return +end diff --git a/replace_pix.pro b/replace_pix.pro new file mode 100644 index 0000000000000000000000000000000000000000..4ad1e083fee945229e73045a6a944ac6b789bafe --- /dev/null +++ b/replace_pix.pro @@ -0,0 +1,72 @@ +; $Id: replace_pix, v 1.0 Sep 1999 e.d. $ +; +;+ +; NAME: +; REPLACE_PIX +; +; PURPOSE: +; Given a 2D array and a set of "bad" pixels, replace each bad data +; point with the median of the "good" data in a suitable box around. +; The box size is adjusted in order to have a minimum number of good +; points to compute the median. +; +; CATEGORY: +; Signal processing. +; +; CALLING SEQUENCE: +; Result = REPLACE_PIX(Data, X, Y) +; +; INPUTS: +; Data: 2D data array +; +; X, Y: Coordinates of pixels to replace +; +; KEYWORD PARAMETERS: +; BOXSIZE: Initial value of the box size to compute the local median. +; +; NGOOD_MIN: Minimum number of good points in the box to compute the +; local median. The default is 3. +; +; BOX_INCR: If the box of size BOXSIZE centered on a bad pixel does +; not contain the minimum number of good points specified by the +; keyword NGOOD_MIN, its size is iteratively increased by the amount +; BOX_INCR until the condition is fulfilled. The default is 2. +; +; MAXIT: Maximum number of iterations to increase the box size. +; The default is 5. +; +; OUTPUTS: +; Result: Array with replaced pixels +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, September 1999. +;- + +FUNCTION replace_pix, data, x, y, BOXSIZE = boxsize, NGOOD_MIN = ngood_min, $ + BOX_INCR = box_incr, MAXIT = maxit + + on_error, 2 + if n_elements(boxsize) eq 0 then boxsize = 3L + if n_elements(ngood_min) eq 0 then ngood_min = 3 + if n_elements(box_incr) eq 0 then box_incr = 2 + if n_elements(maxit) eq 0 then maxit = 5 + data_out = data + npix = n_elements(x) + for n = 0L, npix - 1 do begin + ; Adjust box size to have enough good points + incr = 0L & it = 0L + repeat begin + box = sub_array(data, boxsize + incr, REF = [x[n],y[n]], $ + LX = lx, UX = ux, LY = ly, UY = uy) + w = where(x ge lx and x le ux and y ge ly and y le uy, nbad) + it = it + 1 & incr = incr + box_incr + large_enough = (n_elements(box) - nbad) ge ngood_min or it eq maxit + endrep until large_enough + ; Replace n-th bad pixel with median of good points around + bin = make_array(ux - lx + 1, uy - ly + 1, VALUE = 1B, /BYTE) + bin[x[w] - lx, y[w] - ly] = 0 + w = where(bin eq 1, ngood) + if ngood ne 0 then data_out[x[n],y[n]] = median(box[w], /EVEN) + endfor + return, data_out +end diff --git a/resample.pro b/resample.pro new file mode 100644 index 0000000000000000000000000000000000000000..6abaf8e786f1739d109b43026a7f8d8b6a9cdb6d --- /dev/null +++ b/resample.pro @@ -0,0 +1,107 @@ +; $Id: resample.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; RESAMPLE +; +; PURPOSE: +; Resample a bivariate function defined on a rectangular domain by +; integrating it over square regions, defining pixels of the resampled +; array. The integration is performed by means of IDL REBIN or with +; the Simpson rule. The size of the input array need not be an integer +; multiple of the resampling factor. +; This routine is particularly useful to integrate over pixels an +; oversampled PSF. +; +; CATEGORY: +; Mathematics. Integration routines. +; +; CALLING SEQUENCE: +; Result = RESAMPLE(F, N_sub) +; +; INPUTS: +; F: Sampled bivariate function to be resampled +; +; N_sub: Resampling factor +; +; KEYWORD PARAMETERS: +; X_SHIFT, Y_SHIFT: X- and Y- fractional offsets, expressed in pixel +; units after resampling. The default is X_SHIFT = Y_SHIFT = 0. +; +; X_REF, Y_REF: Use this keyword to provide the position of a pixel +; in the input array to be used as a reference for centering. +; In general the pixel of interest is the array maximum. +; Suppose that X_SHIFT = 0. and Y_SHIFT = 0. and that the keywords +; X_REF and Y_REF are not supplied: the maximum of the resampled array +; may appear "not well centered", i.e. it may present a visual +; off-centering different from X_SHIFT, Y_SHIFT. +; In practice all the relative positions in the output array are always +; preserved. X_REF and Y_REF should be provided when the visual +; appearance of the resampled array is important. +; +; SIMP: Set this keyword to a nonzero value to perform the integration +; by means of the Simpson rule instead of REBIN (neighbor averaging). +; The results is much more accurate. +; +; OUTPUTS: +; Result: Resampled array +; +; RESTRICTIONS: +; 1) When the input array has to be shifted to realize the desired offset, +; missing values at the array edges are actually replaced with 0s. +; 2) The actual off-centering of the resampled array can only be an +; integer multiple of the smallest "theoretical" shift, given by +; (1. / N_sub). +; In practice the input offsets X_SHIFT and Y_SHIFT are rounded. +; For example, if N_sub = 4 and X_SHIFT = 0.4, the actual shift will be +; 0.5; if N_sub = 5 and X_SHIFT = 0.5, the actual shift will be 0.6 +; pixels of the resampled array. +; Of course this rounding effect is more and more negligible as the +; oversampling factor of the input array and the resampling factor +; N_sub increase. +; +; PROCEDURE: +; Suitably resize the input array, according to the initial size and the +; resampling factor. Apply the desired shift and then resample with REBIN +; or SIMPSON_PIX_INTEGRAL. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +FUNCTION resample, f, n_sub, X_SHIFT = x_shift, Y_SHIFT = y_shift, $ + SIMP = simp, X_REF = x_ref, Y_REF = y_ref + + on_error, 2 + nsub = round(n_sub) + if n_elements(x_shift) eq 0 then x_shift = 0. + if n_elements(y_shift) eq 0 then y_shift = 0. + xshift = round(x_shift*nsub) & yshift = round(y_shift*nsub) + ; Shift the array in order to: + ; 1) apply the requested shift (x_shift, y_shift) + ; 2) prevent spurious off-centering due to resampling + ; when a reference pixel is provided + size_f = size52(f, /DIM) & e = 1 - nsub mod 2 + if n_elements(x_ref) * n_elements(y_ref) ne 0 then begin + xr = round(x_ref) & yr = round(y_ref) + xshift = xshift + (xr/n_sub) * nsub + (nsub + e)/2 - xr + yshift = yshift + (yr/n_sub) * nsub + (nsub + e)/2 - yr + endif + a = extend_shift(f, xshift, yshift) + ; Resize the array, if necessary + if not keyword_set(simp) then e = 0 + size_a = (size_f / nsub) * nsub + e + if size_a[0] lt size_f[0] then $ + a = a[0:size_a[0]-1,*] else $ + if size_a[0] gt size_f[0] then $ + a = extend_array(a, size_a[0], size_f[1], /NO_OFF) + if size_a[1] lt size_f[1] then $ + a = a[*,0:size_a[1]-1] else $ + if size_a[1] gt size_f[1] then $ + a = extend_array(a, size_a[0], size_a[1], /NO_OFF) + ; Integrate over pixels + if keyword_set(simp) then $ + a = simpson_pix_integral(a, nsub) else $ + a = rebin(a, size_f[0]/nsub, size_f[1]/nsub) + return, a +end diff --git a/reverse_class.pro b/reverse_class.pro new file mode 100644 index 0000000000000000000000000000000000000000..e1ec2bdfd5809d23547a984c49227b783d8972f2 --- /dev/null +++ b/reverse_class.pro @@ -0,0 +1,40 @@ +; $Id: reverse_class.pro, v 1.1 Mar 2012 e.d. $ +; +;+ +; NAME: +; REVERSE_CLASS +; +; PURPOSE: +; Reverse classification (star/not star) of an element in a list. +; +; CATEGORY: +; STARFINDER auxiliary procedures. +; +; CALLING SEQUENCE: +; Result = REVERSE_CLASS(List, SUBSCRIPTS = S) +; +; INPUTS: +; List: list of stars and presumed stars +; +; KEYWORD PARAMETERS: +; SUBSCRIPTS: subscripts of elements whose classification +; must be reversed. If undefined, reverse classification +; of all elements in the list +; +; OUTPUTS: +; Return list where the classification of the subscripted elements; +; is reversed +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, June 2001. +; 1) Created this file (E.D., March 2012). +;- + +FUNCTION reverse_class, list, SUBSCRIPTS = s + + on_error, 2 + if n_elements(s) eq 0 then s = lindgen(n_elements(list)) + l = list + l[s].is_a_star = (not l[s].is_a_star) and 1B + return, l +end diff --git a/rot_trans.pro b/rot_trans.pro new file mode 100644 index 0000000000000000000000000000000000000000..c22a20469257e25e389a8a8a8fbaef08f1bcf93b --- /dev/null +++ b/rot_trans.pro @@ -0,0 +1,45 @@ +; $Id: rot_trans.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; ROT_TRANS +; +; PURPOSE: +; Rotate and translate a set of points on a plane. +; +; CATEGORY: +; Mathematics. +; +; CALLING SEQUENCE: +; ROT_TRANS, X, Y, Origin, Angle, Rtx, Rty +; +; INPUTS: +; X: X- coordinates of points +; +; Y: Y- coordinates of points +; +; Origin: 2-components vector, containing the x- and y- +; coordinates of the origin of the new set of coordinates +; with respect to the old one +; Angle: Angle (in radians) between the x-axes of the old and the +; new reference frames +; +; OUTPUTS: +; Rtx, Rty: X- and y- coordinates of points in the translated +; rotated reference frame +; +; PROCEDURE: +; Transform the input coordinates according to the following rules: +; Rtx = -Origin[0] + X * cos(angle) + Y * sin(angle) +; Rty = -Origin[1] - X * sin(angle) + Y * cos(angle) +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +PRO rot_trans, x, y, origin, angle, rtx, rty + + on_error, 2 + rtx = -origin[0] + x * cos(angle[0]) + y * sin(angle[0]) + rty = -origin[1] - x * sin(angle[0]) + y * cos(angle[0]) + return +end diff --git a/sampling_grid.pro b/sampling_grid.pro new file mode 100644 index 0000000000000000000000000000000000000000..9517406ef5cdd6e08a8c8d89edd1ade909f0e9df --- /dev/null +++ b/sampling_grid.pro @@ -0,0 +1,45 @@ +; $Id: sampling_grid.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; SAMPLING_GRID +; +; PURPOSE: +; Define a set of sampling points along an axis. +; +; CATEGORY: +; Mathematics. Interpolation. +; +; CALLING SEQUENCE: +; Result = SAMPLING_GRID(N, Dx, Lx, Ux) +; +; INPUTS: +; N: Number of sampling points +; +; Dx: Sampling step +; +; OUTPUTS: +; Result: N-components vector of equispaced points +; +; OPTIONAL OUTPUTS: +; Lx, Ux: Lower and upper bounds of sampling region, useful for +; spline interpolation +; +; PROCEDURE: +; Call FINDGEN to define a set of pixels along a 1D axis. Each pixel +; has a physical step size and corresponds to a sampling point, ideally +; located in the middle of the pixel itself. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +FUNCTION sampling_grid, n, dx, lx, ux + + on_error, 2 + x = findgen(n) * dx + dx / 2. + lx = min(x) - dx/2. & ux = max(x) + dx/2. + return, x +end + + diff --git a/scale_ls_sys.pro b/scale_ls_sys.pro new file mode 100644 index 0000000000000000000000000000000000000000..7d00f1b0b8765b69364c59d663bbda07c5dd52de --- /dev/null +++ b/scale_ls_sys.pro @@ -0,0 +1,79 @@ +; $Id: scale_ls_sys.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; SCALE_LS_SYS +; +; PURPOSE: +; Scale a linear system of normal equations in order to improve the +; eigenvalue ratio. +; +; CATEGORY: +; Mathematics. Linear systems. +; +; CALLING SEQUENCE: +; SCALE_LS_SYS, A, B, A_scaled, B_scaled, Scaling +; +; INPUTS: +; A: Linear system matrix. It must be symmetric +; +; B: Right-hand side term of linear system +; +; OPTIONAL INPUTS +; Scaling: Vector of scaling factors released on output in a +; previous call. Used with the keyword NOCOMP indicates that +; the scaling factors must not be computed again: nly the +; scaling operations must be performed +; +; KEYWORD PARAMETERS: +; NOCOMP: Set this keyword to a nonzero value to indicate that +; the scaling factors must not be computed because they are +; being passed on input as Scaling +; +; OUTPUTS: +; A_scaled: Scaled matrix. Its diagonal elements are equal to 1 +; +; B_scaled: Scaled right-hand side term +; +; Scaling: Vector of scaling factors +; +; SIDE EFFECTS: +; Scaling the linear system +; Ax = b +; involves scaling the vector of variables x. +; After inversion of the system, the solution vector must be multiplied +; component-wise (e.g. using the * operator) by the output vector Scaling. +; +; RESTRICTIONS: +; The system matrix A must be symmetric and positive (semi-) definite. +; No check on the properties of the input array A is performed. +; In practice the procedure SCALE_LS_SYS can be safely applied to the +; output of LS_SYS (see the file 'ls_sys.pro'). +; +; PROCEDURE: +; Compute scaling factors in order to have the diagonal elements of the +; scaled matrix A all equal to 1. If s is the vector of scaling factors, +; let's define S = diag{s} the diagonal matrix having these factors on +; the main diagonal. The scaled version of the linear system +; Ax = b is +; (SAS)x = Sb. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +PRO scale_ls_sys, a, b, a_scaled, b_scaled, scaling, NOCOMP = nocomp + + on_error, 2 + if not keyword_set(nocomp) then begin + ; compute scaling factors + n = size52(a, /DIM) & n = n[0] + d = lindgen(n) & scaling = make_array(n, /FLOAT, VALUE = 1.) + w = where(a[d,d] ne 0, n) + if n ne 0 then scaling[w] = 1 / sqrt(a[d[w],d[w]]) + endif + ; scale a and b + a_scaled = diag_mult(diag_mult(a, scaling), scaling, /PREMULT) + b_scaled = b * scaling + return +end diff --git a/search_objects.pro b/search_objects.pro new file mode 100644 index 0000000000000000000000000000000000000000..4ed3bd2ecdbb78fb94dcd8ddcc95e2b144f8ed35 --- /dev/null +++ b/search_objects.pro @@ -0,0 +1,100 @@ +; $Id: search_objects.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; SEARCH_OBJECTS +; +; PURPOSE: +; Search objects in a given image as relative maxima above a pre-fixed +; threshold. The image can be suitably smoothed to reduce the number +; of spurious detections, due to noise spikes. +; +; CATEGORY: +; Signal processing. +; +; CALLING SEQUENCE: +; SEARCH_OBJECTS, Image, Threshold, N, X, Y, I +; +; INPUTS: +; Image: 2D array to search +; +; Threshold: Scalar or array threshold for detection. +; It must be of the same type as Image. +; +; KEYWORD PARAMETERS: +; LOW_SURFACE: 2D array, with the same size as Image, containing the +; background emission. If this keyword is defined, a lower detection +; surface is defined, as the sum of the background emission and the +; detection Threshold. In this case, Threshold may suitably represent +; a multiple of the noise standard deviation. +; +; PRE_SMOOTH: Set this keyword to smooth the image before searching for +; relative maxima. +; +; MINIF: Integer resampling factor, used to minify the Image with the +; IDL function REBIN. This will smooth many noise spikes. +; This keyword has effect only if PRE_SMOOTH is set. +; If PRE_SMOOTH is set but MINIF is undefined, a standard smoothing +; is applied, by convolving the input Image with the low-pass filter +; 1 2 1 +; 2 4 2 +; 1 2 1 +; Notice that when the keyword BACKGROUND is defined, the same type of +; smoothing is applied to the lower detection surface. +; +; FOUR: Set this keyword to identify relative maxima as pixels brigher +; than their 4-neighbors. The default is to use 8-neighbors. +; +; OUTPUTS: +; N: Number of detected objects +; +; X, Y: Coordinates of objects +; +; I: Central intensity of detected objects in the background-removed +; Image. If the keyword BACKGROUND is undefined, this output variable +; represent the intensity of the objects in the input Image. +; The detected objects are sorted in order of decreasing intensity +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +PRO search_objects, image, LOW_SURFACE = background, threshold, $ + PRE_SMOOTH = pre_smooth, MINIF = minif, $ + _EXTRA = extra, n, x, y, i + + on_error, 2 + ima = image + ; Define lower surface for detection + if n_elements(background) eq 0 then background = 0 + low = background + threshold + ; Smooth image and lower surface + if keyword_set(pre_smooth) then $ + if n_elements(minif) ne 0 then begin + ima = resample(ima, minif) + if size52(low, /N_DIM) eq 2 then $ + low = resample(low, minif) + endif else begin + mask = float([[1, 2, 1], [2, 4, 2], [1, 2, 1]]) + mask = mask / total(mask) + ima = convol(ima, mask, /EDGE_TRUNCATE) + if size52(low, /N_DIM) eq 2 then $ + low = convol(low, mask, /EDGE_TRUNCATE) + endelse + ; Search image maxima above lower surface + all_max, ima, x, y, n, _EXTRA = extra + if n eq 0 then return + if size52(low, /N_DIM) eq 2 then $ + w = where(ima[x, y] gt low[x, y], n) else $ + w = where(ima[x, y] gt low, n) + if n eq 0 then return + x = x[w] & y = y[w] + if keyword_set(pre_smooth) then $ + if n_elements(minif) ne 0 then begin + x = x * round(minif) & y = y * round(minif) + endif + i = (image - background)[x,y] + sorted = reverse(sort(i)) + x = x[sorted] & y = y[sorted] & i = i[sorted] + return +end diff --git a/shifted_templates.pro b/shifted_templates.pro new file mode 100644 index 0000000000000000000000000000000000000000..1321063cc4462b72480f803509f598cfa9dda819 --- /dev/null +++ b/shifted_templates.pro @@ -0,0 +1,69 @@ +; $Id: shifted_templates.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; SHIFTED_TEMPLATES +; +; PURPOSE: +; Shift a template image by an integer multiple of pre-fixed sub-pixel +; shift. The basic fractional offset is defined by a magnification factor +; (> 1). All the possible fractional shifts whose absolute value is < 1/2 +; pixel are applied to the input array and the resulting shifted templates +; are collected into an output stack. +; +; CATEGORY: +; Image processing, spatial transformations +; +; CALLING SEQUENCE: +; SHIFTED_TEMPLATES, Template, Magfac, Templates, Dx, Dy +; +; INPUTS: +; Template: image to be shifted +; +; Magfac: magnification factor (integer, > 1) +; +; EDGE: width (in pixel units) of a frame around the shifted template +; which is excluded after applying the shift. This is useful to +; reject possible edge effects due to interpolation. +; All the shifted templates will have x- and y- size given by +; (sx - 2*edge) and (sy - 2*edge) respectively. +; The default is EDGE = 0. +; +; _EXTRA: keyword parameters of IMAGE_SHIFT (see file 'image_shift.pro') +; +; OUTPUTS: +; Templates: stack of shifted templates +; +; Dx, Dy: 1D arrays of fractional shifts. Templates[*,*,n] is the +; input template shifted by (Dx[n], Dy[n]) +; +; RESTRICTIONS: +; The shift of the input template is performed by IMAGE_SHIFT applying +; some interpolation techniques. Undersampled data should not be shifted +; by interpolation. For details, see IMAGE_SHIFT +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999 +;- + +PRO shifted_templates, template, magfac, _EXTRA = extra, EDGE = edge, $ + templates, dx, dy + + on_error, 2 + mag = round(magfac) > 1 + ; define shifts + nshift = mag + 1L - mag mod 2 & one = make_array(nshift, VALUE = 1) + dx = ((findgen(nshift) - nshift/2) / mag) # one + dy = one # ((findgen(nshift) - nshift/2) / mag) + nshift = nshift^2 & dx = reform(dx, nshift) & dy = reform(dy, nshift) + ; define stack of templates + if n_elements(edge) eq 0 then edge = 0 + s = size52(template, /DIM) - 2*edge & l = [edge, edge] & u = l + s - 1 + templates = make_array(s[0], s[1], nshift, TYPE = size52(template, /TYPE)) + ; compute shifted templates + for n = 0L, nshift - 1 do begin + template_n = image_shift(template, dx[n], dy[n], aux, _EXTRA = extra) + templates[*,*,n] = template_n[l[0]:u[0],l[1]:u[1]] + endfor + return +end diff --git a/simpson_pix_integral.pro b/simpson_pix_integral.pro new file mode 100644 index 0000000000000000000000000000000000000000..0691d67b0fa444fd873cefd866b455a9bdf7c810 --- /dev/null +++ b/simpson_pix_integral.pro @@ -0,0 +1,118 @@ +; $Id: simpson_pix_integral.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; SIMPSON_PIX_INTEGRAL +; +; PURPOSE: +; Resample a bivariate function defined on a rectangular domain by +; integrating it over square regions. The integration is performed +; by means of the Simpson rule. +; +; CATEGORY: +; Mathematics. Integration routines. +; +; CALLING SEQUENCE: +; Result = SIMPSON_PIX_INTEGRAL(F, Nsteps) +; +; INPUTS: +; F: 2D array containing the values of the bivariate function +; +; Nsteps: Number of samples / size of integration interval. The following +; relationship must exist between Nsteps and the size S of the input +; array (we consider only one dimension for simplicity): +; S = FIX(S / Nsteps) * Nsteps + e, where +; e = 1 - Nsteps MOD 2. +; +; KEYWORD PARAMETERS: +; PIX_SIZE: Set this keyword to the physical value of the pixel size. +; The default is PIX_SIZE = 1. +; +; OUTPUTS: +; Result: 2D array containing the input function integrated +; over square sub-regions. +; +; PROCEDURE: +; Integrate the input function over square sub-domains of N*N points, where +; N is related to the input parameter Nsteps by the following relation: +; N = Nsteps, if Nsteps is odd +; N = Nsteps + 1, if Nsteps is even. +; The output array may be thought of as the input function integrated over +; larger pixels. The value of each output pixel is the result of the integral +; computed over N*N points. If Nsteps is even, two adjacent output pixels +; will share one input point: this strategy is necessary when using Simpson +; rule (at least its simplest version), which requires an odd number of +; points per integration interval. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + + + +; SIMPSON_COEFF: auxiliary function, compute coefficients for Simpson rule. + +FUNCTION simpson_coeff, n, l + + on_error, 2 + w = fltarr(n) & s = lindgen((n - 1)/2) + w[2*s] = 2. & w[2*s+1] = 4. + w[0] = 1. & w[n-1] = 1. + w = w / 3. * float(l) / (n - 1) + return, w +end + + +FUNCTION simpson_pix_integral, f, nsteps, PIX_SIZE = pixsize + + on_error, 2 + s = size52(f, /DIM) & nstep = round(nsteps) + nx = s[0]/nstep & ny = s[1]/nstep & e = 1 - nstep mod 2 + if s[0] ne (nx*nstep + e) or s[1] ne (ny*nstep + e) then $ + message, 'wrong array size' + if n_elements(pixsize) eq 0 then pixsize = 1. + npt = nstep + e & half = npt/2 + w = simpson_coeff(npt, pixsize) & wt = transpose(w) + lx = half & ly = half & ux = s[0]-1-half & uy = s[1]-1-half + intf = fltarr(nx, ny) + i = -1L + for y = ly, uy, nstep do begin + i = i + 1 & j = -1L + for x = lx, ux, nstep do begin + j = j + 1 + intf[j,i] = wt # f[x-half:x+half,y-half:y+half] # w + endfor + endfor + return, intf +end + + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; Matrix implementation (for nstep even). More beautiful but slower! + +; FUNCTION simp_coeff_array, nstep, nint +; +; c = simpson_coeff(nstep+1, 1.) +; a = fltarr( nstep*nint+1, nint ) +; for n = 0, nint-1 do a[n*nstep,n] = c +; return, a +; end + +; FUNCTION simpson_pix_integral, f, nstep + +; s = size52(f, /DIM) & nstep = round(nstep) +; nx = s[0]/nstep & ny = s[1]/nstep +; if size52(f, /N_DIM) ne 2 or $ +; s[0] mod 2 eq 0 or s[1] mod 2 eq 0 or $ +; nstep mod 2 ne 0 or nx mod 2 ne 0 or ny mod 2 ne 0 $ +; then return, f +; cx = simp_coeff_array( nstep, nx ) +; if ny ne nx then $ +; cy = simp_coeff_array( nstep, ny ) else cy = cx +; return, transpose(cx) # f # cy +; end + + + diff --git a/size52.pro b/size52.pro new file mode 100644 index 0000000000000000000000000000000000000000..a3cc138de217347ff01d057b5611ed2f4c60e086 --- /dev/null +++ b/size52.pro @@ -0,0 +1,46 @@ +; $Id: size52.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; SIZE52 +; +; PURPOSE: +; Alias of IDL 5.2 intrinsic SIZE function for previous versions. +; +; CATEGORY: +; Array informational routines. +; +; CALLING SEQUENCE: +; Result = SIZE52(X) +; +; INPUTS: +; X: IDL variable +; +; KEYWORD PARAMETERS: +; N_DIMENSION: Set this keyword to a nonzero value to retrieve the +; number of dimensions of X +; +; DIMENSION: Set this keyword to a nonzero value to retrieve a +; long-integer vector containing the size of each dimension of X +; +; TYPE: Set this keyword to a nonzero value to retrieve the IDL +; type code of X +; +; OUTPUTS: +; Result: Same as output of SIZE if no keyword is set. Otherwise return +; the result specified by the KEYWORD. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +FUNCTION size52, x, N_DIMENSION = ndim, DIMENSION = dim, TYPE = type + + s = size(x) + if keyword_set(ndim) then s = s[0] else $ + if keyword_set(dim) then begin + if s[0] le 1 then s = s[0] else s = s[1:s[0]] + endif else $ + if keyword_set(type) then s = s[s[0] + 1] + return, s +end diff --git a/sort_list.pro b/sort_list.pro new file mode 100644 index 0000000000000000000000000000000000000000..7b4f0e0a27ae40ec8b408f2d1099617a831db4a2 --- /dev/null +++ b/sort_list.pro @@ -0,0 +1,37 @@ +; $Id: sort_list.pro, v 1.1 Mar 2012 e.d. $ +; +;+ +; NAME: +; SORT_LIST +; +; PURPOSE: +; Sort stars by decreasing flux. +; +; CATEGORY: +; STARFINDER auxiliary procedures. +; +; CALLING SEQUENCE: +; Result = SORT_LIST(List, SUBSCRIPTS = S) +; +; INPUTS: +; List: list of stars +; +; KEYWORD PARAMETERS: +; SUBSCRIPTS: subscripts of stars to be sorted. If undefined, sort +; all stars in the list +; +; OUTPUTS: +; Return sorted list of sublist, if SUBSCRIPTS is set. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, June 2001. +; 1) Created this file (E. D., March 2012). +;- + +FUNCTION sort_list, list, SUBSCRIPTS = s + + on_error, 2 + if n_tags(list) eq 0 then return, list + s = reverse(sort(list.f)) + return, list[s] +end diff --git a/spline_coeff.pro b/spline_coeff.pro new file mode 100644 index 0000000000000000000000000000000000000000..59d1f039eeb78404903ff4b4906be22d352650a1 --- /dev/null +++ b/spline_coeff.pro @@ -0,0 +1,218 @@ +; $Id: spline_coeff.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; SPLINE_COEFF +; +; PURPOSE: +; Given a set of observations on a rectangular grid of points, +; compute the coefficients of the 2D interpolating spline. +; +; CATEGORY:; +; Mathematics. Interpolation +; +; CALLING SEQUENCE: +; SPLINE_COEFF, Data, Coefficients, X_knots, Y_knots, $ +; X, Y, Lo_x, Up_x, Lo_y, Up_y +; +; INPUTS: +; Data: 2D array of data to be interpolated +; +; OPTIONAL INPUTS: +; X, Y: Vectors of abscissae and ordinates, in increasing order. +; If not provided, a default grid is defined by the function +; SAMPLING_GRID +; +; Lo_x, Up_x, Lo_y, Up_y: Lower and Upper, X- and Y- bounds of +; rectangular domain where the observations are assigned. +; The following conditions must be fulfilled: +; Lo_x <= X <= Up_x, Lo_y <= Y <= Up_y +; +; KEYWORD PARAMETERS: +; DEGREE: Integer odd degree of spline. The default is DEGREE = 3. +; +; OUTPUTS: +; Coefficients: 2D array of spline coefficients, with the same size +; size as the input Data. Return a scalar if an error occurs or +; if the input parameters are not acceptable. +; +; X_knots, Y_knots: Vectors of spline knots +; +; OPTIONAL OUTPUTS: +; X, Y: Vectors of abscissae and ordinates +; +; Lo_x, Up_x, Lo_y, Up_y: Lower and Upper, X- and Y- bounds of +; rectangular domain where the observations are assigned +; +; RESTRICTIONS: +; Apply only to 2D data. +; +; PROCEDURE: +; Apply the procedures described in +; Paul Dierckx, "Curve and surface fitting with splines", +; Clarendon Press, Oxford (1995) +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +; Adapted in IDL from the sofware FITPACK, written in FORTRAN by P.Dierckx. +; Complete references may be found in +; Paul Dierckx, "Curve and surface fitting with splines", +; Clarendon Press, Oxford (1995) +;- + + + +;;; Auxiliary procedures/functions. + +; SPL_ASCENDING: given the vector x of size n, check the condition +; x[i] < x[i+1], for i = 0, n - 2. + +FUNCTION spl_ascending, x + + on_error, 2 + if size52(x, /N_DIM) gt 1 then return, 0B + n = n_elements(x) + check = (x lt shift(x, -1) )[0:n-2] and 1B + return, min(check) +end + +; SPL_CHECK_DATA: check input coordinates. If not set, define a default grid. + +PRO spl_check_data, data, x, y, lo_x, up_x, lo_y, up_y, error + + on_error, 2 + error = size52(data, /N_DIM) ne 2 & if error then return + sx = n_elements(x) & sy = n_elements(y) + if sx eq 0 or sy eq 0 or $ + n_elements(lo_x) eq 0 or n_elements(up_x) eq 0 or $ + n_elements(lo_y) eq 0 or n_elements(up_y) eq 0 then begin + s = size52(data, /DIM) & sx = s[0] & sy = s[1] + x = sampling_grid(sx, 1, lo_x, up_x) + y = sampling_grid(sy, 1, lo_y, up_y) + endif else $ + error = min(x) lt lo_x or max(x) gt up_x or $ + min(y) lt lo_y or max(y) gt up_y or $ + not spl_ascending(x) or not spl_ascending(y) + return +end + +; SPL_COMPUTE_KNOTS: 1-D set of knots for spline interpolation of odd degree. + +FUNCTION spl_compute_knots, x, a, b, degree, error + + on_error, 2 + n = n_elements(x) + error = n le degree or degree mod 2 eq 0 & if error then return, 0 + k = fltarr(n + degree + 1) + k[0:degree] = a & k[n:n+degree] = b ; additional knots + if n gt degree + 1 then $ ; interior knots + k[degree + 1:n - 1] = x[degree/2 + 1:degree/2 + n - degree - 1] + return, k +end + +; SPL_GIVPAR: compute parameters for a Givens transformation. + +PRO spl_givpar, piv, r, c, s + + on_error, 2 + abs_piv = abs(piv) + if abs_piv ge r then $ + aux_r = abs_piv * sqrt(1 + (r/piv)^2) else $ + aux_r = r * sqrt(1 + (piv/r)^2) + c = r / aux_r & s = piv / aux_r & r = aux_r + return +end + +; SPL_G_ROTATE: rotate vectors v1 and v2 applying a Givens rotation (c,s). + +PRO spl_g_rotate, c, s, v1, v2 + + on_error, 2 + u1 = v1 & u2 = v2 + v1 = c * u1 - s * u2 + v2 = s * u1 + c * u2 + return +end + +; SPL_GIV_TRANSFORM: rotate observation array a and data array d +; applying Givens rotations. + +PRO spl_giv_transform, a, npt, d, degree + + on_error, 2 + n_points = n_elements(npt) + aux_a = a & a = a - a + aux_d = transpose(d) & d = aux_d - aux_d + for n = 0L, n_points - 1 do begin + this = npt[n] & va = aux_a[*,n] & vd = aux_d[*,n] + for i = 0, degree do begin + piv = va[i] + if piv ne 0 then begin + temp = a[0,this] & spl_givpar, piv, temp, c, s & a[0,this] = temp + temp = d[*,this] & spl_g_rotate, c, s, vd, temp & d[*,this] = temp + if i lt degree then begin + temp = va[i+1:degree] & temp1 = a[1:degree-i,this] + spl_g_rotate, c, s, temp, temp1 + va[i+1:degree] = temp & a[1:degree-i,this] = temp1 + endif + endif + this = this + 1 + endfor + endfor + return +end + +; SPL_BACK_SUB: solve the set of m linear algebraic systems a x = b, +; where a is an n*n upper triangular matrix of bandwidth w, +; x and b are m*n arrays. + +FUNCTION spl_back_sub, a, b, w + + on_error, 2 + s = size52(b, /DIM) & m = s[0] & n = s[1] + x = fltarr(m, n + w - 1) + for i = n - 1, 0, -1 do $ + x[*,i] = (b[*,i] - x[*,i+1:i+w-1] # a[1:w-1,i]) / a[0,i] + return, x[*,0:n-1] +end + +; SPL_SOLVE_SYS: compute the coefficients of the interpolating spline +; as the solution of the linear system (ay) c (ax)' = data + +FUNCTION spl_solve_sys, ax, nptx, ay, npty, data, degree + + on_error, 2 + d = data + ; Reduce (ax) to upper triangular form. Apply the same transformation + ; to the data. + spl_giv_transform, ax, nptx, d, degree + ; Reduce (ay) to upper triangular form. Apply the same transformation + ; to the data. + spl_giv_transform, ay, npty, d, degree + ; Solve the linear system (ay) c (ax)' = d + temp = spl_back_sub(ay, d, degree + 1) + c = spl_back_sub(ax, transpose(temp), degree + 1) + return, transpose(c) ; c is (nx) * (ny) +end + +;;; The main routine. + +PRO spline_coeff, data, DEGREE = degree, coefficients, $ + x_knots, y_knots, x, y, lo_x, up_x, lo_y, up_y + + on_error, 2 + coefficients = 0 + spl_check_data, data, x, y, lo_x, up_x, lo_y, up_y, error + if error then return + if n_elements(degree) eq 0 then degree = 3 + if degree mod 2 eq 0 then degree = degree + 1 + x_knots = spl_compute_knots(x, lo_x, up_x, degree, error) + if error then return + y_knots = spl_compute_knots(y, lo_y, up_y, degree, error) + if error then return + b_splines, x, x_knots, degree, ax, nptx + b_splines, y, y_knots, degree, ay, npty ; delta_t = 2.6 + coefficients = spl_solve_sys(ax, nptx, ay, npty, data, degree) + ; delta_t = 10.6 + return +end diff --git a/spline_interp.pro b/spline_interp.pro new file mode 100644 index 0000000000000000000000000000000000000000..2888e2eb6108ef63ef48dec396eadeb12d47646a --- /dev/null +++ b/spline_interp.pro @@ -0,0 +1,66 @@ +; $Id: spline_interp.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; SPLINE_INTERP +; +; PURPOSE: +; Given the coefficients of a 2D spline, as returned by SPLINE_COEFF, +; evaluate the spline on a grid of points. +; +; CATEGORY: +; Mathematics. Interpolation. +; +; CALLING SEQUENCE: +; Result = SPLINE_INTERP(C, X_knots, Y_knots, X, Y) +; +; INPUTS: +; C: 2D array of spline coefficients, returned by SPLINE_COEFF +; +; X_knots, Y_knots: Vector of spline knots, returned by SPLINE_COEFF +; +; X, Y: Coordinates of points where the spline function must be +; evaluated. The following conditions must be hold: +; MIN(X_knots) <= X <= MAX(X_knots) +; MIN(Y_knots) <= Y <= MAX(Y_knots) +; If some X or Y does not fulfills these conditions, it is replaced +; by the corresponding bound on X_knots and Y_knots +; +; KEYWORD PARAMETERS: +; DEGREE: Degree of spline. It must be the same as that used in the call +; to SPLINE_COEFF. The default value (strongly recommended) is +; DEGREE = 3. +; +; OUTPUTS: +; Result: 2D array of spline evaluations, of size Nx*Ny, where Nx and Ny +; are the number of elements in the input vectors X and Y +; +; RESTRICTIONS: +; Apply only to 2D data. +; +; PROCEDURE: +; Apply the procedures described in +; Paul Dierckx, "Curve and surface fitting with splines", +; Clarendon Press, Oxford (1995) +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +; Adapted in IDL from the sofware FITPACK, written in FORTRAN by P.Dierckx. +; Complete references may be found in +; Paul Dierckx, "Curve and surface fitting with splines", +; Clarendon Press, Oxford (1995) +;- + +FUNCTION spline_interp, c, x_knots, y_knots, x, y, DEGREE = degree + + on_error, 2 + if n_elements(degree) eq 0 then degree = 3 + x_points = (x > min( x_knots)) < max(x_knots) + y_points = (y > min( y_knots)) < max(y_knots) + b_splines, x_points, x_knots, degree, bx, kx, /BOUNDS, /FULL + b_splines, y_points, y_knots, degree, by, ky, /BOUNDS, /FULL + ; delta_t = 2.7 + return, transpose(bx) # c # by ; delta_t = 336 + ; NOTE: the array implementation is faster than the + ; component-wise by a factor of 2 for an array 128*128 +end diff --git a/stack_combine.pro b/stack_combine.pro new file mode 100644 index 0000000000000000000000000000000000000000..843302dbeab2fbfae20ef6cd28b06ba69f56bca6 --- /dev/null +++ b/stack_combine.pro @@ -0,0 +1,106 @@ +; $Id: stack_combine.pro, v 1.2 July 2006 e.d. $ +; +;+ +; NAME: +; STACK_COMBINE +; +; PURPOSE: +; Combine frames in a 3D stack by average, average with sigma-rejection +; or median. +; +; CATEGORY: +; Signal processing. +; +; CALLING SEQUENCE: +; Result = STACK_COMBINE(Stack) +; +; INPUTS: +; Stack: 3D array, representing the stack of frames to be combined +; +; KEYWORD PARAMETERS: +; AVGTYPE: Set this keyword to choose a combination algorithm among +; the following possibilities: +; AVGTYPE = 0 [default]: pixel-by-pixel average +; = 1: pixel-by-pixel minimum +; = 2: pixel-by-pixel median +; +; MASK: 3D binary array used to mask the frames in the Stack. +; The n-th frame of the 3D array Mask passed with this keyword may +; be defined as follows: +; Mask[j, i, n] = 1, if Stack[j, i, n] is a valid pixel +; = 0, if Stack[j, i, n] must be rejected +; The default is no masking. +; +; WEIGHTS: Set this keyword to a N-components vector, where N is the +; number of frames in the Stack. The n-th component is the weight to +; apply to the n-th input frame. The weights must be positive and +; non-zero. +; If the median is used, then the weights are divided by their minimum +; value and rounded to integers. Every plane in the Stack is then +; replicated a number of times equal to the corresponding weight. +; This keyword has no effect when AVGTYPE = 1. +; +; NSTDEV: when the average with sigma-clipping is used, the outliers to +; be excluded from the computation are defined as those values whose +; absolute distance from the median of the sample is larger than NSTDEV +; times the standard deviation of the sample. +; +; OUTPUTS: +; Result: 2D array, given by the combination of the planes in the Stack. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, September 2001. +; 1) Mean and median average only (E.D. May 2005) +; 2) Added option for combination with MIN operator (E.D. July 2006) +;- + +FUNCTION stack_combine, stack, MASK = mask, WEIGHTS = weights, $ + AVGTYPE = avgtype, _EXTRA = extra + + on_error, 2 + ; Default average type + if n_elements(avgtype) eq 0 then avgtype = 0 + ; Stack size + if size52(stack, /N_DIM) ne 3 then return, stack + s = size52(stack, /DIM) & s0 = s[0] & s1 = s[1] & n_frames = s[2] + ; Mask some frames? + masked = n_elements(mask) ne 0 + ; Weighted average? + weighted = n_elements(weights) ne 0 + if weighted then weighted = min(weights) gt 0 + if weighted then w = weights else w = replicate(1, n_frames) + w = w / total(w) + if weighted and avgtype eq 2 then w = round(w / min(w)) + ; Combine planes of the stack + if avgtype eq 0 then begin + wstack = replicate(w[0], s0, s1) + for n = 1, n_frames - 1 do $ + wstack = [[[wstack]], [[replicate(w[n], s0, s1)]]] + m = total(wstack * stack, 3) + endif else begin + m = make_array(s0, s1, TYPE = size52(stack, /TYPE)) + for i = 0L, s1 - 1 do for j = 0L, s0 - 1 do begin + slice = stack[j,i,*] & w_ji = w & n = n_frames + ; Mask undesired frames + if masked then begin + accept = where(mask[j,i,*] ne 0, n) + if n ne 0 then begin + slice = slice[accept] & w_ji = w_ji[accept] + endif + endif + ; If median is applied, replicate frames according to their weight + if avgtype eq 2 and weighted and n ne 0 then begin + temp = replicate(slice[0], w_ji[0]) + for k = 1L, n - 1 do $ + temp = [temp, replicate(slice[k], w_ji[k])] + slice = temp + endif + ; Compute average of pixel [j,i] + if n ne 0 then $ + if avgtype eq 1 then $ + m[j,i] = min(slice) else $ + m[j,i] = median(slice, /EVEN) + endfor + endelse + return, m +end diff --git a/star.pro b/star.pro new file mode 100644 index 0000000000000000000000000000000000000000..f6cb4b0944b0acde525cedb506d396028e119cf4 --- /dev/null +++ b/star.pro @@ -0,0 +1,34 @@ +; $Id: star.pro, v 1.1 Mar 2012 e.d. $ +; +;+ +; NAME: +; STAR +; +; PURPOSE: +; Create named structure, called "starlet", representing a star. +; This structure is the basic element of a list of stars, which +; might both accepted stars and still presumed ones. +; +; CATEGORY: +; STARFINDER auxiliary procedures. +; +; CALLING SEQUENCE: +; Result = STAR() +; +; OUTPUTS: +; Return "starlet" structure +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, June 2001. +; 1) Created this file (E. D., March 2012). +;- + +FUNCTION star + + return, {starlet, $ + x: 0., sigma_x: 0., $ ; x- coordinate and error + y: 0., sigma_y: 0., $ ; y- coordinate and error + f: 0., sigma_f: 0., $ ; flux and error + c: -1., $ ; correlation + is_a_star: 0B} ; flag +end diff --git a/star_param.pro b/star_param.pro new file mode 100644 index 0000000000000000000000000000000000000000..4a4ac9e2d5a76fb2f49310916f30c57dd4865c5c --- /dev/null +++ b/star_param.pro @@ -0,0 +1,57 @@ +; $Id: star_param.pro, v 1.1 Mar 2012 e.d. $ +; +;+ +; NAME: +; STAR_PARAM +; +; PURPOSE: +; Extract stars parameters from star list, possibly including +; presumed stars. +; +; CATEGORY: +; STARFINDER auxiliary procedures. +; +; CALLING SEQUENCE: +; STAR_PARAM, List, SUBSCRIPTS = S, $ +; N, X, Y, F, C, Sigma_X, Sigma_Y, Sigma_F +; +; INPUTS: +; List: list of stars +; +; KEYWORD PARAMETERS: +; SUBSCRIPTS: 1D vector of subscript of stars to be extracted. +; If undefined, extract parameters of all true stars in the list. +; +; OUTPUTS: +; N: number of extracted stars +; +; X, Y, F: position and flux of stars +; +; C: correlation coefficienf +; +; Sigma_X, Sigma_Y, Sigma_F: errors on position and flux +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, June 2001. +; 1) Created this file (E. D., March 2012). +;- + +PRO star_param, list, SUBSCRIPTS = s, $ + n, x, y, f, c, sigma_x, sigma_y, sigma_f + + on_error, 2 + if n_tags(list) eq 0 then begin + n = 0L & s = -1L & return + endif + n = n_elements(s) + if n eq 0 then begin + n = n_elements(list) & s = lindgen(n) ; extract all elements + endif + if s[0] lt 0 then n = 0 else begin $ + x = list[s].x & sigma_x = list[s].sigma_x + y = list[s].y & sigma_y = list[s].sigma_y + f = list[s].f & sigma_f = list[s].sigma_f + c = list[s].c + endelse + return +end diff --git a/starfinder.pro b/starfinder.pro new file mode 100644 index 0000000000000000000000000000000000000000..d6482d8290eded33ac37019cba9f454f0d86f78d --- /dev/null +++ b/starfinder.pro @@ -0,0 +1,1197 @@ +; $Id: starfinder.pro, v 1.8.2a May 2014 e.d. $ +; +;+ +; NAME: +; STARFINDER +; +; PURPOSE: +; Given a stellar field and an approximation of the Point Spread Function +; (PSF), detect the stellar sources and estimate their position and flux. +; The image may be contaminated by a smooth non-uniform background emission. +; If the imaged field is not isoplanatic, it is possible to partition it +; into approximately isoplanatic sub-regions, provided a local estimate of +; the PSF is available. In this case, the field is analyzed as a whole as +; in the constant PSF case, with the only difference that the local PSF is +; used to analyze each star. +; A spatially variable PSF may also be handled by providing a parametric +; PSF model variable across the imaged field. +; +; CATEGORY: +; Signal processing. Stellar fields astrometry and photometry. +; +; CALLING SEQUENCE: +; STARFINDER, Image, Psf, Threshold, Min_correlation, $ +; X, Y, Fluxes, Sigma_x, Sigma_y, Sigma_f, Correlation +; +; INPUTS: +; Image: 2D image of the stellar field +; +; Psf: 2D array, representing the Point Spread Function of the stellar +; field. If the PSF is space-variant, Psf is a 3D stack of local +; PSF measurements, relative to a partition of the imaged field into +; sub-regions arranged in a regular grid. In this case it is necessary +; to supply the bounds of the partition (see keyword SV_PAR). +; +; Threshold: Vector of lower detection levels (above the local background, +; which is temporarly removed before detection). These levels are +; assumed to be "absolute" levels by default. If the keyword +; REL_THRESHOLD (see below) is set and the noise standard deviation is +; defined (see the keyword NOISE_STD) below, then the threshold levels +; are assumed to be "relative", i.e. expressed in units of the noise +; standard deviation. +; Example 1: let Threshold be = [10., 3.] +; If the keyword REL_THRESHOLD is not defined or the keyword +; parameter NOISE_STD is undefined, the threshold levels 10 and 3 +; are absolute intensity levels, i.e. all the peaks brighter than 10 +; and 3 counts are considered as presumed stars. If REL_THRESHOLD is +; set and NOISE_STD is defined, the intensity levels for detection +; are respectively (10 * NOISE_STD) and (3 * NOISE_STD). +; The number of elements of the vector Threshold establishes the number +; of iterations of the "basic step" (see PROCEDURE below). +; The components of the vector Threshold may be all equal or decreasing. +; +; Min_correlation: Minimum value of correlation between an acceptable +; stellar image and the Psf. It must be > 0 and < 1. +; +; KEYWORD PARAMETERS: +; SV_PAR: Set this keyword to a structure defining the bounds of the +; imaged field partition when the PSF is not isoplanatic. +; Let the imaged field be partitioned into Nx*Ny sub-regions, such +; that the (j, i)-th region is bounded by +; Lx[j] < x < Ux[j], Ly[i] < y < Uy[i], where +; j = 0, ..., Nx - 1; i = 0, ..., Ny - 1. +; The structure must be defined as follows: +; SV_PAR = {Lx: Lx, Ux: Ux, Ly: Ly, Uy: Uy}. +; The (j, i)-th sub-region must correspond to the k-th psf in the 3D +; stack, where k = i*Nx + j. +; +; PSF_TYPE: Set this keyword to a string indicating the name of a PSF +; model. Available PSF models are defined in the documentation of the +; routine IMAGE_MODEL (see documentation of the input parameter Psf in +; the file IMAGE_MODEL.PRO). This keyword has to be used together with +; the keyword PSF_DATA. +; The keywords PSF_TYPE and PSF_DATA are useful to define a parametric +; PSF model, that can be made variable across the field of view, +; depending on how it is defined in IMAGE_MODEL.PRO. +; When PSF_TYPE and PSF_DATA are used, the input parameter Psf and the +; keyword parameter SV_PAR described above have to be supplied anyway, +; as they are used in the preliminary phases of the analysis. +; +; PSF_DATA: Use this keyword to provide a pointer to the auxiliary data +; required by the PSF model defined by the keyword PSF_TYPE, when it is +; used. Auxiliary data are defined according to the documentation of the +; input parameter Data in the file IMAGE_MODEL.PRO. +; +; REL_THRESHOLD: Set this keyword to specify that the detection levels +; contained in the input parameter Threshold have to be considered as +; "relative" levels, i.e. they must be multiplied by the noise standard +; deviation. This keyword is effective only if the NOISE_STD is defined +; (see below). +; +; X_BAD, Y_BAD: Coordinates of bad pixels to be excluded. +; It is important to mask bad pixels, especially in the following phases +; of the analysis: +; 1) search for presumed stars +; 2) correlation check +; 3) local fitting. +; +; BACKGROUND: 2D array, containing an initial guess of the image background +; emission. If the keyword is undefined, the background is estimated +; automatically, provided the box size BACK_BOX (see below) is defined. +; The background estimate is subtracted before searching for presumed +; stars in the image and before computing the correlation coefficient. +; +; BACK_BOX: Set this keyword to a scalar value specifying the box size +; to estimate the background emission. For more details see the routine +; ESTIMATE_BACKGROUND in the file "estimate_background.pro". +; The value of this keyword may be conveniently defined as a multiple +; of the PSF Full Width at Half Maximum (e.g. 5 * FWHM). +; If if is not supplied, the program uses the argument of the keyword +; BACKGROUND (if defined of course!) as a background estimate. However +; this will prevent any improvements in the background knowledge in the +; course of iterations. If the stellar field is supposed to have no +; background emission, the keywords BACKGROUND and BACK_BOX should be +; left undefined. +; +; For other keywords concerning background estimation see the routines +; IMAGE_BACKGROUND and ESTIMATE_BACKGROUND. +; +; FOUR: Set this keyword to identify relative maxima referring only to +; the 4-neighbors of each pixel. The default is to use 8-neighbors. +; +; PRE_SMOOTH: Set this keyword to smooth the image with a standard low-pass +; filter before searching for local maxima. This reduces the probability +; to pick up noise spikes, which would increase the computation time for +; further checks and the probability of false detections. +; For more details see the procedure SEARCH_OBJECTS, in the file +; "search_objects.pro". +; +; MINIF: If PRE_SMOOTH is set, the keyword MINIF may be used to enter the +; integer minification factor used to down-sample the image before +; searching for local maxima, as an alternative smoothing strategy to +; the standard low-pass filter used by default when PRE_SMOOTH is set. +; +; CORREL_MAG: Set this keyword to an integer scalar specifying the sub-pixel +; accuracy with which the template PSF should be superposed to a given +; object when computing the correlation coefficient. +; If CORREL_MAG = 1, the PSF is superposed to the +; object just taking the maximum intensity pixel as a reference. +; If CORREL_MAG is > 1, all the possible fractional shifts of the PSF, +; with step size of 1/CORREL_MAG, are considered. +; The default value is CORREL_MAG = 2 (half pixel positioning). +; +; DEBLEND: Set this keyword to a nonzero value to perform de-blending +; of the objects detected iterating the basic step (see PROCEDURE). +; This de-blending strategy allows the algorithm to detect secondary +; components of crowded groups, whose principal component has previously +; been detected as a single star. This strategy may be referred to as +; 'partial de-blending'. +; +; DEBLOST: Set this keyword to a nonzero value to recover all the +; significant intensity enhancement lost in the course of the analysis, +; in order to search for possible blends. This strategy allows the +; algorithm to re-analyze the blended objects whose principal component +; has not been previously found as a single star (see DEBLEND above). +; This strategy, applied after the 'partial de-blending' described with +; the keyword DEBLEND above, may be referred to as 'full de-blending'. +; More details can be found in the PROCEDURE description below. +; +; CUT_THRESHOLD: Set this keyword to a scalar value in the range ]0, 1[ +; to specify the relative threshold to use when "cutting" a given +; object to measure its area. The default is CUT_THRESHOLD = 0.5, +; i.e. the object is cut at 50% of the peak height. Notice that the +; background is subtracted beforehands. Notice also that if the +; absolute value of the threshold for binary detection is comparable +; to the gaussian noise level (see also keywords GAUSSIAN_NOISE and +; BIN_N_STDEV), the estimated area of the object is not reliable: thus +; deblending is not applied. This check on the threshold value is +; performed only if the gaussian noise standard deviation is defined +; (see the keyword GAUSSIAN_NOISE). +; For more details on the determination of the area, see the routine +; PEAK_AREA in the file "peak_area.pro". +; +; BIN_N_STDEV: Set this keyword to a positive scalar to specify the +; minimum cutting threshold for binary detection, in units of the +; gaussian noise standard deviation. The default value is +; BIN_N_STDEV = 3. +; +; BIN_THRESHOLD: When a crowded group (in the simplest case a close binary +; star) is analyzed, the deblending algorithm searches for residuals +; representing secondary components by subtracting the known sources: +; The brightest local maximum in the residual sub-image is taken as a +; candidate secondary star. In principle the detection threshold used +; for isolated objects, set by the parameter Threshold described above, +; should not be applied in this case, because the residual is affected +; by the poor knowledge about the subtracted components. +; BIN_THRESHOLD specifies the relative threshold, in units of the +; parameter Threshold, to use for the detection of candidate secondary +; sources in crowded groups. The default is BIN_THRESHOLD = 0, i.e. all +; the positive residuals (remember that the local background level has +; been removed!) are considered. Their intensity is compared to the +; detection threshold after fitting: if the fitted intensity happens to; +; be below the value set by Threshold, the new secondary component is +; rejected. +; +; BIN_TOLERANCE: An object is classified as "blend" if the relative error; +; between its area and the area of the PSF is greater than the threshold +; fixed by this keyword. The default is BIN_TOLERANCE = 0.2, i.e. 20%. +; +; NOISE_STD: Set this keyword to a 2D array, with the same size as Image, +; representing the noise standard deviation in each pixel of the input +; data. This array is used to compute the formal errors on astrometry +; and photometry. +; +; NO_SLANT: Set this keyword to avoid fitting the local background with +; a slanting plane. In this case the background to use for the local +; fit of the stars is derived from the 2D background array and it is +; kept fixed in the fitting. This option should be used only if the +; (input) estimate of the image background is very accurate. +; +; MIN_DISTANCE: Minimum distance, expressed in units of the PSF FWHM, to +; consider two sources as separated. This parameter is used to check the +; outcome of the fitting of a group of neighboring sources: if two sources +; are closer than this limit, the fit is considered unacceptable and the +; last detected source is rejected as a false detection. +; The default value is 1 (which means 1 PSF FWHM). +; +; N_ITER: At the end of the analysis, the detected stars are fitted again +; a number of times equal to the value specified with this keyword. +; The default is N_ITER = 1. To avoid re-fitting, set N_ITER = 0. +; Notice that the final number of re-fitting iterations includes also +; the extra re-fitting which is normally performed after each basic +; step (see also the keyword NO_INTERMEDIATE_ITER). +; +; NO_INTERMEDIATE_ITER: The currently detected stars are re-fitted after +; each basic step. Set NO_INTERMEDIATE_ITER to avoid this re-fitting. +; +; ASTROMETRIC_TOL, PHOTOMETRIC_TOL: When the last re-fitting is iterated +; a lot of times, it is possible to stop the iterations when convergence +; on stellar positions and fluxes is achieved. The default values for +; convergence are ASTROMETRIC_TOL = 0.01 pixels and +; PHOTOMETRIC_TOL = 0.01 (i.e. 1%). +; In practice setting N_ITER = 1 or 2 is generally enough to obtain +; reliable astrometry and photometry, without having to iterate a lot. +; +; X_INPUT, Y_INPUT, F_INPUT: vectors with positions and fluxes of the stars, +; when these are already known. When these parameters are defined, no +; search is performed: the known sources are just fitted a pre-fixed number +; of times (see N_ITER) or until convergence (see ASTROMETRIC_TOL and +; PHOTOMETRIC_TOL). The sources are sorted by decreasing flux. +; Usual checks applied in fitting procedure are applied (minimum flux +; above threshold, minimum distance between any two stars). +; When this option is used, single-component fitting is performed, i.e. +; stars closer than the fitting box width are fit one at a time. +; Input sources for which the fit fails are rejected. +; *** Recommendation. When this option is used, the following keyword +; should be set: +; MIN_DISTANCE = 0.0 +; In order to avoid optimizing the input positions set the keyword /FLUXOPT +; (see below). In this case, the positions are kept fixed to the input values, +; only fluxes are optimized. +; +; FLUXOPT: Set this binary keyword to keep the positions of the sources +; fixed in the optimization process when an input list of sources is given +; (see keywords X_INPUT, Y_INPUT, F_INPUT above). +; +; C_INPUT: when an input list of sources is given (see keywords X_INPUT, +; Y_INPUT, F_INPUT above), the keyword C_INPUT may be used to provide the +; correlation coefficients of the sources (detemined in a previous detection +; run). These correlation coefficients are just retained on output, associated +; to the sources for which the fit process is successful. +; +; SVDINV: The fitting process is performed by a Newton-Gauss method. +; This method requires a matrix inversion at every iteration. +; Set this keyword to perform this matrix inversion by SVD (Singular +; Value Decomposition). The default is matrix inversion by Moore-Penrose +; Generalized Inverse (see routine 'ginv.pro'). +; +; SILENT: Set this keyword to avoid printing messaged to the output window. +; If not set, STARFINDER prints messages about the current analysis step and +; about the candidate and detected stars. "Candidate stars" are the peaks +; found in the image; "Detected stars" are the peaks which are confirmed to be +; stars in the analysis. The number of "Candidate stars" does not account for +; the additional candidate peaks found with the deblending procedure. +; +; OUTPUTS: +; X, Y: Coordinates of detected stars in pixel units. +; The origin is at (0, 0). +; +; Fluxes: Vector of stellar fluxes, referred to the normalization of the Psf. +; +; Sigma_x, Sigma_y, Sigma_f: Vector of formal errors on positions and +; fluxes, estimated by the fitting procedure. +; These vectors have all components equal to zero if no information is +; supplied about noise: in this case it has no meaning to estimate the +; error propagation in the fit. +; +; Correlation: Vector of correlation coefficients for accepted stars. +; Stars belonging to crowded groups detected with the de-blending +; procedure have a correlation coefficient of -1. +; +; OPTIONAL OUTPUTS: +; BACKGROUND: Last estimate of the image background, computed by the +; program after the iteration corresponding to the lowest +; detection level. +; +; STARS: 2D array, containing one shifted scaled replica of the PSF for +; each detected star. +; +; SIDE EFFECTS: +; If the keyword NOISE_STD is set to a scalar value it is transformed into +; a 2D constant array with the same size as the input image. +; +; RESTRICTIONS: +; 1) The algorithm is based on the assumption that the input Image may +; be considered as a superpositions of stellar sources and a smooth +; background. +; 2) Saturated stars should have been approximately repaired beforehands. +; This is especially important for the reliable detection and analysis of +; fainter sources in their surrounding. +; 3) High accuracy astrometry and photometry are based on sub-pixel +; positioning of the input PSF. In this version of the algorithm this +; relies on interpolation techniques, which are not suited to undersampled +; data. Thus the algorithm, at least in its present form, should not be +; used with poorly sampled data. +; 4) The keyword /FLUXOPT should be used only when a list of input sources +; is given and the input position estimates are very accurate. +; +; PROCEDURE: +; The standard analysis is accomplished as a sequence of "basic steps". +; One of these steps consists of 3 phases: +; 1) detection of presumed stars above a given threshold +; 2) check and analysis of detected objects, sorted by decreasing +; intensity +; 3) re_fitting +; Pixel (x,y) is the approximate center of a presumed star if +; a) Image(x,y) is a local maximum +; b) Image(x,y) > Background(x,y) + Stars(x,y) + Threshold, +; where Image is the stellar field, Background is an approximation of +; the background emission, Stars is a sum of shifted scaled replicas +; of the PSF (one for each detected star) and Threshold is the +; minimum central intensity of a detectable star. In practice the user +; provides a set of Threshold levels (generally decreasing!): the +; number of these levels fixes the number of times the basic step is +; repeated. +; At the first iteration of the basic step, the "image model" called +; Stars is identically zero; in later iterations it contains the detected +; sources. Subtraction of these stars simplifies the detection of fainter +; objects and allows a better estimation of the image Background. +; It should be noticed that the model of detected stars (Stars) is +; subtracted only in order to detecte new presumed stars and refine the +; background, whereas the subsequent astrometric and photometric analysis +; is performed on the original Image. +; Let us describe in detail how the presumed stars are analyzed (Phase +; 2 of each basic step). +; First of all each object in the newly formed list is "re-identified", +; i.e. searched again after subtraction of other brighter sources: this +; allows to reject many spurious detections associated to PSF features +; of bright stars. The object is compared with the PSF by a correlation +; check and accepted only if a sufficient similarity is found. Then it is +; fitted to estimate its position and flux. The local fitting is performed +; by FITSTARS (see the file "fitstars.pro" for more details), which takes +; into consideration all the following contributions: +; a) other known stars in the fitting box, which are fitted along with +; the object under examination (multiple fitting) +; b) known stars lying outside the fitting box: this term is extracted +; from the image model Stars and considered as a fixed contribution +; c) the local background, which may be approximated either with a +; slanting plane (whose coefficients are optimized as well) or with a +; sub-image extracted from the image Background (kept fixed). +; If the fit is successful, the parameters of the stars are saved and +; the image model Stars is updated. +; The basic step (phases 1 + 2 + 3) may be iterated: a new list of +; objects is formed by searching in the image after subtraction of the +; previously detected stars. Then the analysis proceeds on the original +; frame. This iteration is very useful to detect hidden objects, e.g. +; close binaries down to separations comparable to 1 PSF FWHM. +; An optional deblending strategy is available, consisting of two +; separate phases. The first step (see keyword DEBLEND) consists of a search +; for secondary sources around the objects detected in the earlier phases of +; the analysis; in the second step (see keyword DEBLOST) all the significant +; intensity enhancement previously discarded are recovered and a check is +; performed in order to find among these some possible blends. Blends are +; recognized on the basis of their larger extension as compared to the PSF. +; The area of a given object is measured by a thresholding technique, applied +; at a specified threshold below the peak. When an object is recognized as a +; blend, deblending is attempted iteratively by searching for a new residual +; and subsequent fitting. The iteration is stopped when no more residual is +; found or the fitting of the last residual is not successful. The deblending +; procedure is applied at the end of the last iteration of the basic step +; described above, i.e. when all the "resolved" objects are supposed to have +; been detected. +; It should be stressed that step 3 (re-fitting) has the goal to improve +; the astrometric and photometric measurement of the detected stars. If the +; fit of a given source fails, however, the source (which has already been +; identified as a true one) is not rejected: its parameters are simply +; set to their previous value, without any update. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August-September 1999. +; Updates: +; 1) New partial deblending strategy (Emiliano Diolaiti, October 1999). +; 2) Full deblending strategy (Emiliano Diolaiti, February 2000). +; 3) Added PSF stack option; this version might not handle properly +; very close groups of objects lying on the boundary of two adjacent +; sub-regions (Emiliano Diolaiti, February 2000). +; 4) Modified keyword parameters in auxiliary routines to avoid trouble +; under IDL 5.0 (Emiliano Diolaiti, May 2000). +; 5) Modified STARFINDER_FIT module. Now faster. +; (Emiliano Diolaiti, December 2000). +; 6) Moved auxiliary routines to handle the list of stars in the +; program module STARLIST. (Emiliano Diolaiti, June 2001). +; 7) Modified STARFINDER_DEBLEND module. Now faster. +; (Emiliano Diolaiti, June 2001). +; 8) PSF stack option (see modification no. 3): fixed problem with stars +; lying close to boundary between adjacent sub-regions +; (E.D., December 2004). DEBLEND option not checked with PSF stack option. +; 9) Increased minimum distance between two stars to 1 FWHM. +; 10) Increased box width for distance check in STARFINDER_CHECK module +; (after suggestion by Jessica Lu, May 2005). +; 11) Temporarily removed modification no. 10 and restored previous version +; for trouble at image edge. +; 12) Added keywords X_INPUT, Y_INPUT, F_INPUT for fitting a set of known +; stars. Not yet tested with space-variant PSF (E. D., February 2006). +; 13) Added keyword MIN_DISTANCE, to set minimum acceptable distance between +; two stars (E. D., August 2006). +; 14) Fixed parameter value: fitting box size was not properly set with +; keywords X_INPUT, Y_INPUT, F_INPUT (E. D., March 2012). +; 15) When an input list of sources is given (keywords X_INPUT, Y_INPUT, +; F_INPUT), the sources for which the fit fails are rejected +; (E. D., March 2012). +; 16) Keyword "re_fitting" turned into a positional parameters in modules +; STARFINDER_FIT, STARFINDER_DEBLEND, STARFINDER_ANALYZE (E. D., March 2012). +; 17) Fixed bug about keyword MIN_DIST: 0 value was not accepted before +; (E. D., March 2012). +; 18) Added keyword SVDINV (E. D., March 2012). +; 19) Changed some output messages in VERBOSE mode (E.D., March 2012). +; 20) Created separate source files for all auxiliary routines described +; at point 6) (E. D., March 2012). +; 21) Changed default value of keyword CUT_THRESHOLD = 0.5 (E.D., March 2012). +; 22) Added keyword MAG=3 in call to PEAK_WIDTH in module +; STARFINDER_DEB_PAR (E.D., March 2012). +; 23) Added keyword FLUXOPT (E.D., April 2012). +; 24) Removed check MAX_DIST in module STARFINDER_DEBLEND (E.D., April 2012). +; 25) Set default value of keyword CORREL_MAG = 2 (E.D., April 2012). +; 26) Removed bug in STARFINDER_FIT module: sometimes very close sources +; were swapped by call to COMPARE_LISTS (E.D., April 2012). +; 27) Updated documentation (E. D., May 2014). +;- + + + +;;; Auxiliary procedures/functions to handle list of stars data structure. + + +;FUNCTION star +; +;PRO update_list, list, SUBSCRIPTS = s, x, y, f, c, $ +; sigma_x, sigma_y, sigma_f, IS_STAR = is_star +; +;FUNCTION create_element, x, y, f +; +;FUNCTION merge_list, l1, l2 +; +;FUNCTION add_subscript, subscripts, s +; +;FUNCTION delete_element, list +; +;PRO star_param, list, SUBSCRIPTS = s, $ +; n, x, y, f, c, sigma_x, sigma_y, sigma_f +; +;FUNCTION where_stars, list, LX = lx, UX = ux, LY = ly, UY = uy, n +; +;FUNCTION extract_stars, list, n +; +;FUNCTION sort_list, list, SUBSCRIPTS = s +; +;FUNCTION reverse_class, list, SUBSCRIPTS = s +; +;FUNCTION starlist, n, x, y, f, c +; + + + +;;; Auxiliary procedures/functions to define the program's parameters. + +; STARFINDER_FWHM: compute PSF FWHM. + +FUNCTION starfinder_fwhm, psf, n_psf + + on_error, 2 + fw = fltarr(n_psf) + for n = 0L, n_psf - 1 do fw[n] = fwhm(psf[*,*,n], /CUBIC, MAG = 3) + return, fw +end + +; DEFINE_CORR_BOX: define correlation box size. + +FUNCTION define_corr_box, psf_fwhm + + on_error, 2 + box = round(1.5 * psf_fwhm) + box = box + 1 - box mod 2 + return, box +end + +; DEFINE_FIT_BOX: define fitting box size. + +FUNCTION define_fit_box, psf_fwhm, WIDER = wider + + on_error, 2 + if keyword_set(wider) then b = 2. else b = 1. + box = round(define_corr_box(psf_fwhm) > 5 + b * psf_fwhm) + box = box + 1 - box mod 2 + return, box +end + +; STARFINDER_ID_PAR: define parameters to re-identify presumed stars. + +FUNCTION starfinder_id_par, psf_fwhm, FOUR = four + + on_error, 2 + box = define_fit_box(psf_fwhm) + neigh4 = keyword_set(four) and 1B + return, {box: box, neigh4: neigh4} +end + +; STARFINDER_CORR_PAR: define parameters for correlation. + +FUNCTION starfinder_corr_par, psf, psf_fwhm, n_psf, CORREL_MAG = correl_mag, $ + _EXTRA = extra + + on_error, 2 + correlation_box = define_corr_box(psf_fwhm) + search_box = correlation_box / 2 + search_box = search_box + 1 - search_box mod 2 + if n_elements(correl_mag) eq 0 then correl_mag = 2 + if correl_mag gt 1 then edge = 2 else edge = 0 + temp_siz = correlation_box + 2 * edge + template = ptrarr(n_psf, /ALLOCATE) + if correl_mag gt 1 then templates = ptrarr(n_psf, /ALLOCATE) $ + else begin + templates = 0 & dx = 0 & dy = 0 + endelse + for n = 0L, n_psf - 1 do begin + temp = sub_array(psf[*,*,n], temp_siz[n]) + if correl_mag gt 1 then begin + shifted_templates, temp, correl_mag, _EXTRA = extra, EDGE = edge, $ + templs, dx, dy + temp = sub_array(temp, correlation_box[n]) + *templates[n] = templs + endif + *template[n] = temp + endfor + return, {correl_mag: correl_mag, $ + correlation_box: correlation_box, $ + search_box: search_box, $ + template: template, $ + templates: templates, dx: dx, dy: dy} +end + +; STARFINDER_FIT_PAR: define parameters for fitting. + +FUNCTION starfinder_fit_par, psf, psf_fwhm, n_psf, MIN_DISTANCE = min_dist, _EXTRA = extra + + on_error, 2 + fitting_box = define_fit_box(psf_fwhm, _EXTRA = extra) + psf_size = 2 * max(fitting_box) < (max((size52(psf, /DIM))[0:1]) - 2) +; edge = round((fitting_box - psf_fwhm) / 2.0) > 1 + edge = 0.5 + if n_elements(min_dist) eq 0 then min_dist = 1.0 + min_distance = min_dist * psf_fwhm + fitting_psf = fltarr(psf_size, psf_size, n_psf) + psf_max = fltarr(n_psf) + for n = 0L, n_psf - 1 do begin + fitting_psf[*,*,n] = sub_array(psf[*,*,n], psf_size) + psf_max[n] = max(psf[*,*,n]) + endfor + return, {fitting_box: fitting_box, edge: edge, $ + min_distance: min_distance, $ + fitting_psf: fitting_psf, psf: psf, $ + psf_max: psf_max, psf_fwhm: psf_fwhm} +end + +; STARFINDER_DEB_PAR: define parameters for deblending. + +FUNCTION starfinder_deb_par, psf, psf_fwhm, n_psf, $ + BIN_THRESHOLD = bin_threshold, $ + CUT_THRESHOLD = cut, $ + BIN_N_STDEV = n_std, BIN_TOLERANCE = tol + + on_error, 2 + if n_elements(bin_threshold) eq 0 then bin_threshold = 0. + if n_elements(cut) eq 0 then cut = 0.5 & cut = cut > 0 < 1 ;cut = cut > 0.2 < 1 + if n_elements(n_std) eq 0 then n_std = 3 + if n_elements(tol) eq 0 then tol = 0.2 & tol = tol > 0 < 1 + box = lonarr(n_psf) & area = fltarr(n_psf) + for n = 0L, n_psf - 1 do begin + box[n] = round(3 * peak_width(psf[*,*,n], REL = cut, MAG = 3)) + area[n] = peak_area(psf[*,*,n], REL = cut, MAG = 3) + endfor + return, {bin_threshold: bin_threshold, n_std: n_std, $ + box: box, cut: cut, tol: tol, psf_area: area} +end + +; STARFINDER_DEALLOC: de-allocate heap variables. + +PRO starfinder_dealloc, corr_par, fit_data, model_data + + on_error, 2 + if n_elements(corr_par) eq 0 then return + if (ptr_valid(corr_par.template))[0] then ptr_free, corr_par.template + if (ptr_valid(corr_par.templates))[0] then ptr_free, corr_par.templates + if ptr_valid(fit_data) then ptr_free, fit_data + if ptr_valid(model_data) then ptr_free, model_data + return +end + + + +;;; Other auxiliary procedures/functions. + +; STARFINDER_BAD: find bad pixels in the sub-image [lx:ux,ly:uy]. + +PRO starfinder_bad, x_bad, y_bad, lx, ux, ly, uy, x_bad_here, y_bad_here + + on_error, 2 + w = where(x_bad ge lx and x_bad le ux and $ + y_bad ge ly and y_bad le uy, n) + if n ne 0 then begin + x_bad_here = x_bad[w] - lx & y_bad_here = y_bad[w] - ly + endif + return +end + +; STARFINDER_BOXES: extract boxes from image, background and image model. + +PRO starfinder_boxes, image, background, stars, x, y, boxsize, $ + lx, ux, ly, uy, i_box, b_box, s_box, $ + x_bad, y_bad, x_bad_here, y_bad_here + + on_error, 2 + i_box = sub_array(image, boxsize, REFERENCE = [x, y], $ + LX = lx, UX = ux, LY = ly, UY = uy) + s_box = stars[lx:ux,ly:uy] & b_box = background[lx:ux,ly:uy] + if n_elements(x_bad) ne 0 and n_elements(y_bad) ne 0 then $ + starfinder_bad, x_bad, y_bad, lx, ux, ly, uy, x_bad_here, y_bad_here + return +end + +; STARFINDER_ID: re-identification of a presumed star. + +PRO starfinder_id, image, background, stars, sv_par, id_par, $ + min_intensity, x, y, found + + on_error, 2 + if n_elements(sv_par) ne 0 then $ + r = pick_region(sv_par.lx, sv_par.ux, sv_par.ly, sv_par.uy, x, y) $ + else r = 0 + ; Extract boxes + starfinder_boxes, image, background, stars, x, y, id_par.box[r], $ + lx, ux, ly, uy, i_box, b_box, s_box + ; Search + max_search, i_box - b_box - s_box, min_intensity[lx:ux,ly:uy], $ + X0 = x - lx, Y0 = y - ly, n, x, y, $ + /NEAREST, /MAXIMUM, FOUR = id_par.neigh4 + found = n ne 0 + if found then begin + x = x[0] + lx & y = y[0] + ly + endif + return +end + +; STARFINDER_CORRELATE: correlation check. + +PRO starfinder_correlate, image, background, stars, sv_par, x_bad, y_bad, $ + corr_par, min_correlation, x, y, correl, accepted + + on_error, 2 + if n_elements(sv_par) ne 0 then $ + r = pick_region(sv_par.lx, sv_par.ux, sv_par.ly, sv_par.uy, x, y) $ + else r = 0 + ; Extract boxes + boxsize = corr_par.correlation_box[r] + corr_par.search_box[r] + 1 + starfinder_boxes, image, background, stars, x, y, boxsize, $ + lx, ux, ly, uy, i_box, b_box, s_box, x_bad, y_bad, xb, yb + ; Compute correlation + if corr_par.correl_mag gt 1 then begin + templates = *corr_par.templates[r] & dx = corr_par.dx & dy = corr_par.dy + endif + correlate_max, i_box - b_box - s_box, *corr_par.template[r], x - lx, y - ly, $ + corr_par.search_box[r], X_BAD = xb, Y_BAD = yb, $ + TEMPLATES = templates, DX = dx, DY = dy, correl, x, y + x = x + lx & y = y + ly + accepted = correl ge min_correlation and $ + x ge lx and x le ux and y ge ly and y le uy + return +end + +; STARFINDER_CHECK: check outcome of local fitting. + +FUNCTION starfinder_check, fit_error, x_fit, y_fit, f_fit, $ + lx, ux, ly, uy, list, min_intensity, fit_par, r + + on_error, 2 + ; Check convergence of fit, minimum acceptable value of fluxes, + ; range of positions +; padd = 2.0 ; added after suggestion by Jessica Lu + padd = 0.0 + good = fit_error ge 0 and $ + min(x_fit) ge lx-padd and max(x_fit) le ux+padd and $ + min(y_fit) ge ly-padd and max(y_fit) le uy+padd + if good then begin + threshold = min_intensity[round(x_fit),round(y_fit)] / fit_par.psf_max[r] + good = min(f_fit - threshold) ge 0 + good = min(f_fit - threshold) ge 0 + endif + if good then begin + ; Consider the subset of stars in a neighborhood of [lx:ux,ly:uy] + ; and check their reciprocal distances + s = where_stars(LX = lx - fit_par.min_distance[r], $ + UX = ux + fit_par.min_distance[r], $ + LY = ly - fit_par.min_distance[r], $ + UY = uy + fit_par.min_distance[r], list, n) + star_param, list, SUBSCRIPTS = s, n, x, y, f + if n gt 1 then $ + good = min(reciprocal_distance(x, y)) ge fit_par.min_distance[r] + endif + return, good +end + +; STARFINDER_FIT: local fitting. + +PRO starfinder_fit, list, this_max, image, siz, background, stars, noise_std, $ + sv_par, x_bad, y_bad, fit_par, fit_data, model_data, $ + min_intensity, NO_SLANT = no_slant, $ + re_fitting, _EXTRA = extra, star_here, $ + PSF_TYPE = psf_type, PSF_DATA = psf_data + + on_error, 2 + star_param, list, SUBSCRIPTS = this_max, n, x, y + if n_elements(sv_par) ne 0 then $ + r = pick_region(sv_par.lx, sv_par.ux, sv_par.ly, sv_par.uy, x, y) $ + else r = 0 + ; Extract boxes + starfinder_boxes, image, background, stars, x, y, fit_par.fitting_box[r], $ + lx, ux, ly, uy, i_box, b_box, s_box, x_bad, y_bad, xb, yb + if n_elements(xb) ne 0 and n_elements(yb) ne 0 then $ + w_bad = coord_to_subs(xb, yb, (size52(i_box, /DIM))[0]) + ; Select known stars into i_box and subtract them from the image model + s = where_stars(LX = lx + fit_par.edge, UX = ux - fit_par.edge, $ + LY = ly + fit_par.edge, UY = uy - fit_par.edge, list, n) + if re_fitting ne 0 then begin +; if n eq 0 then s = this_max ; edge star +; Modified March 2012 +; if n eq 0 then begin + if n eq 0 or re_fitting eq 2 then begin + s = this_max ; edge star or single-component fitting + n = 1 + endif + s_and_this = s + endif else s_and_this = add_subscript(s, this_max) + star_param, list, SUBSCRIPTS = s, n, x_add, y_add, f_add, c + if n ne 0 then $ + if keyword_set(psf_type) then $ + stars = image_model(x_add, y_add, -f_add, siz[0], siz[1], psf_type, $ + psf_data, _EXTRA = extra, MODEL = stars) else $ + if n_elements(sv_par) ne 0 then $ + stars = image_model(x_add, y_add, -f_add, siz[0], siz[1], fit_par.psf, $ + LX = sv_par.lx, UX = sv_par.ux, LY = sv_par.ly, UY = sv_par.uy, $ + model_data, _EXTRA = extra, MODEL = stars) else $ + stars = image_model(x_add, y_add, -f_add, siz[0], siz[1], fit_par.psf, $ + model_data, _EXTRA = extra, MODEL = stars) + ; Fixed contribution for local fitting + contrib = stars[lx:ux,ly:uy] ; stars outside fitting box + if keyword_set(no_slant) then begin + contrib = contrib + b_box & b_box = 0 + endif + ; Local fitting + if re_fitting ne 0 then begin + x = x_add & y = y_add & f0 = f_add + endif else $ +; Modified March 2012: n is the number of known sources in the box + star_param, list, SUBSCRIPTS = s_and_this, n_and_this, x, y, f, c +; star_param, list, SUBSCRIPTS = s_and_this, n, x, y, f, c + x0 = x - lx & y0 = y - ly + if n_elements(noise_std) ne 0 then noise = noise_std[lx:ux,ly:uy] + if n_elements(sv_par) ne 0 then begin + sv_lx = sv_par.lx - lx + sv_ux = sv_par.ux - lx + sv_ly = sv_par.ly - ly + sv_uy = sv_par.uy - ly + endif + if keyword_set(psf_type) then begin + xori = (*psf_data).xori + yori = (*psf_data).yori + (*psf_data).xori = lx + (*psf_data).yori = ly + fitstars, i_box, FIXED = contrib, psf_type, $ + PSF_DATA = psf_data, x0, y0, F0 = f0, BACKGROUND = b_box, $ + NO_SLANT = no_slant, NOISE_STD = noise, _EXTRA = extra, $ + BAD_DATA = w_bad, x, y, f, b, fit_error, sigma_x, sigma_y, sigma_f + (*psf_data).xori = xori + (*psf_data).yori = yori + endif else $ + fitstars, i_box, FIXED = contrib, fit_par.fitting_psf, $ + PSF_DATA = fit_data, x0, y0, F0 = f0, BACKGROUND = b_box, $ + NO_SLANT = no_slant, NOISE_STD = noise, _EXTRA = extra, $ + BAD_DATA = w_bad, LX = sv_lx, UX = sv_ux, LY = sv_ly, UY = sv_uy, $ + x, y, f, b, fit_error, sigma_x, sigma_y, sigma_f +; Modified April 2012 +; x_out = x & y_out = y +; compare_lists, x0, y0, x_out, y_out, x0_out, y0_out, x, y, SUBSCRIPTS_2 = w + compare_lists, x0, y0, x, y, SUBSCRIPTS_2 = w + x = x[w] + lx & y = y[w] + ly & f = f[w] + if n_elements(sigma_f) ne 0 then begin + sigma_x = sigma_x[w] + sigma_y = sigma_y[w] + sigma_f = sigma_f[w] + endif + ; Is the examined max a star? Assume it is, then perform checks + temp_list = list + update_list, temp_list, SUB = s_and_this, x, y, f, c, $ + sigma_x, sigma_y, sigma_f, /IS_STAR + star_here = starfinder_check(fit_error, x, y, f, lx, ux, ly, uy, $ + temp_list, min_intensity, fit_par, r) + ; Update list of stars and image model + if star_here then begin + list = temp_list + x_add = x & y_add = y & f_add = f + endif else if re_fitting eq 2 then begin + list = reverse_class(list, SUBSCRIPT = this_max) + n = n - 1 + if n gt 0 then begin + w = where(s_and_this ne this_max[0]) + star_param, list, SUBSCRIPTS = s_and_this[w], n, x_add, y_add, f_add, c + endif + endif + if n gt 0 or re_fitting eq 0 then $ ; should be equivalent to "n gt 0 or re_fitting lt 2" + if keyword_set(psf_type) then $ + stars = image_model(x_add, y_add, f_add, siz[0], siz[1], psf_type, $ + psf_data, _EXTRA = extra, MODEL = stars) else $ + if n_elements(sv_par) ne 0 then $ + stars = image_model(x_add, y_add, f_add, siz[0], siz[1], fit_par.psf, $ + LX = sv_par.lx, UX = sv_par.ux, LY = sv_par.ly, UY = sv_par.uy, $ + model_data, _EXTRA = extra, MODEL = stars) else $ + stars = image_model(x_add, y_add, f_add, siz[0], siz[1], fit_par.psf, $ + model_data, _EXTRA = extra, MODEL = stars) + return +end + +; STARFINDER_BLEND: check the area of a presumed star to identify blends. + +FUNCTION starfinder_blend, image, background, stars, noise_std, $ + deb_par, x, y, r + + on_error, 2 + starfinder_boxes, image, background, stars, x, y, deb_par.box[r], $ + lx, ux, ly, uy, i_box, b_box, s_box + ima = i_box - b_box - s_box ; after this, it should be positive! + x0 = x - lx & y0 = y - ly + threshold = ima[x0,y0] * deb_par.cut + check = 1B + if n_elements(noise_std) ne 0 then $ + check = threshold gt deb_par.n_std * noise_std[x,y] + if check then begin + area = peak_area(ima, X = x0, Y = y0, MAG = 3, ABS_THRESHOLD = threshold) + check = relative_error(deb_par.psf_area[r], area) gt deb_par.tol + endif + return, check +end + +; STARFINDER_DEBLEND: de-blend a crowded group of stars +; (partial or full de-blending). + +PRO starfinder_deblend, list, this_max, image, siz, background, stars, $ + noise_std, sv_par, x_bad, y_bad, id_par, fit_par, $ + deb_par, fit_data, model_data, min_intensity, $ + re_fitting, AROUND = around, _EXTRA = extra, $ + PSF_TYPE = psf_type, PSF_DATA = psf_data + + on_error, 2 + star_param, list, SUB = this_max, n, x, y, f + x0 = round(x) & y0 = round(y) + if n_elements(sv_par) ne 0 then $ + r = pick_region(sv_par.lx, sv_par.ux, sv_par.ly, sv_par.uy, x, y) $ + else r = 0 + ; Is the present object a blend? + if keyword_set(around) then $ + if keyword_set(psf_type) then $ + stars = image_model(x, y, -f, siz[0], siz[1], psf_type, $ + psf_data, _EXTRA = extra, MODEL = stars) else $ + if n_elements(sv_par) ne 0 then $ + stars = image_model(x, y, -f, siz[0], siz[1], fit_par.psf, $ + LX = sv_par.lx, UX = sv_par.ux, LY = sv_par.ly, UY = sv_par.uy, $ + model_data, _EXTRA = extra, MODEL = stars) else $ + stars = image_model(x, y, -f, siz[0], siz[1], fit_par.psf, $ + model_data, _EXTRA = extra, MODEL = stars) +; stars = image_model(x, y, -f, siz[0], siz[1], fit_par.psf[*,*,r], $ +; model_data, _EXTRA = extra, MODEL = stars) + blended = starfinder_blend(image, background, stars, noise_std, $ + deb_par, x0, y0, r) + if keyword_set(around) then $ + if keyword_set(psf_type) then $ + stars = image_model(x, y, f, siz[0], siz[1], psf_type, $ + psf_data, _EXTRA = extra, MODEL = stars) else $ + if n_elements(sv_par) ne 0 then $ + stars = image_model(x, y, f, siz[0], siz[1], fit_par.psf, $ + LX = sv_par.lx, UX = sv_par.ux, LY = sv_par.ly, UY = sv_par.uy, $ + model_data, _EXTRA = extra, MODEL = stars) else $ + stars = image_model(x, y, f, siz[0], siz[1], fit_par.psf, $ + model_data, _EXTRA = extra, MODEL = stars) +; stars = image_model(x, y, f, siz[0], siz[1], fit_par.psf[*,*,r], $ +; model_data, _EXTRA = extra, MODEL = stars) + if not blended then return ; not a blend + ncomp = 0L & found = ncomp eq 0 & star_here = found + if keyword_set(around) then ncomp = ncomp + 1 +; max_dist = 1.25 * fit_par.psf_fwhm[r] + while star_here do begin + x = x0 & y = y0 + if ncomp ne 0 then begin + ; Identify another component of the blend + starfinder_boxes, image, background, stars, x, y, deb_par.box[r], $ + lx, ux, ly, uy, i_box, b_box, s_box + max_search, i_box - b_box - s_box, $ + deb_par.bin_threshold * min_intensity[lx:ux,ly:uy], $ + X0 = x - lx, Y0 = y - ly, CHECK_DIST = max_dist, $ + /MAXIMUM, FOUR = id_par.neigh4, n, x, y + found = n ne 0 & star_here = found + if found then begin + x = x[0] + lx & y = y[0] + ly + endif + endif + if found then begin + ; Fit the new detected component + if ncomp ne 0 then begin + list = merge_list(list, create_element(x, y, 0)) + index = n_elements(list) - 1 + endif else index = this_max + starfinder_fit, list, index, image, siz, background, stars, $ + noise_std, sv_par, x_bad, y_bad, fit_par, fit_data, $ + model_data, min_intensity, re_fitting, _EXTRA = extra, $ + star_here, PSF_TYPE = psf_type, PSF_DATA = psf_data + endif + if star_here then ncomp = ncomp + 1 + endwhile + if ncomp eq 1 and not keyword_set(around) then begin + ; The object was mis-recognized as a blend: it is single + star_param, list, SUB = this_max, n, x, y, f + if keyword_set(psf_type) then $ + stars = image_model(x, y, -f, siz[0], siz[1], psf_type, $ + psf_data, _EXTRA = extra, MODEL = stars) else $ + if n_elements(sv_par) ne 0 then $ + stars = image_model(x, y, -f, siz[0], siz[1], fit_par.psf, $ + LX = sv_par.lx, UX = sv_par.ux, LY = sv_par.ly, UY = sv_par.uy, $ + model_data, _EXTRA = extra, MODEL = stars) else $ + stars = image_model(x, y, -f, siz[0], siz[1], fit_par.psf, $ + model_data, _EXTRA = extra, MODEL = stars) +; stars = image_model(x, y, -f, siz[0], siz[1], fit_par.psf[*,*,r], $ +; model_data, _EXTRA = extra) + list = reverse_class(list, SUBSCRIPT = this_max) + endif + return +end + +; STARFINDER_ANALYZE: analyze the object list[this_max]. + +PRO starfinder_analyze, list, this_max, image, siz, background, stars, $ + noise_std, sv_par, x_bad, y_bad, id_par, corr_par, $ + fit_par, fit_data, model_data, $ + min_intensity, min_correlation, re_fitting, _EXTRA = extra + + on_error, 2 + star_param, list, SUB = this_max, n, x, y + ; Is the maximum list[this_max] a feature of an already detected star? + starfinder_id, image, background, stars, sv_par, id_par, $ + min_intensity, x, y, check + if not check then return ; yes, it is + ; Correlation check + starfinder_correlate, image, background, stars, sv_par, x_bad, y_bad, $ + corr_par, min_correlation, x, y, c, check + if not check then return ; too low correlation + ; Fit + update_list, list, SUB = this_max, x, y, 0, c + starfinder_fit, list, this_max, image, siz, background, stars, noise_std, $ + sv_par, x_bad, y_bad, fit_par, fit_data, model_data, $ + min_intensity, re_fitting, _EXTRA = extra + return +end + +; STARFINDER_CONVERG: check convergence of positions and fluxes for Phase 3. + +FUNCTION starfinder_converg, list0, list, $ + ASTROMETRIC_TOL = a_tol, PHOTOMETRIC_TOL = ph_tol + + on_error, 2 + if n_elements(a_tol) eq 0 then a_tol = 1e-2;1e-4;0.01 + if n_elements(ph_tol) eq 0 then ph_tol = 1e-2;1e-4;0.01 + s = where_stars(list, n) + if n eq 0 then return, n eq 0 + star_param, list0, SUB = s, n, x0, y0, f0 + star_param, list, SUB = s, n, x, y, f + check = convergence(x0, x, a_tol, /ABSOLUTE) and $ + convergence(y0, y, a_tol, /ABSOLUTE) and $ + convergence(f0, f, ph_tol) + return, check +end + + + +;;; The main routine. + +PRO starfinder, $ + image, psf, SV_PAR = sv_par, $ + X_BAD = x_bad, Y_BAD = y_bad, $ + BACKGROUND = background, BACK_BOX = back_box, $ + threshold, REL_THRESHOLD = rel_threshold, NOISE_STD = noise_std, $ + min_correlation, DEBLEND = deblend, DEBLOST = deblost, _EXTRA = extra, $ + N_ITER = n_iter, NO_INTERMEDIATE_ITER = no_intermediate, SILENT = silent, $ + x, y, fluxes, sigma_x, sigma_y, sigma_f, correlation, STARS = stars, $ + X_INPUT = x_in, Y_INPUT = y_in, F_INPUT = f_in, C_INPUT = c_in, $ + PSF_TYPE = psf_type, PSF_DATA = psf_data + + catch, error + if error ne 0 then begin + starfinder_dealloc, corr_par, fit_data, model_data + return + endif + + ; Define background and image model + siz = size52(image, /DIM) + if n_elements(background) eq 0 then $ + if n_elements(back_box) eq 0 then $ + background = fltarr(siz[0], siz[1]) else $ + background = estimate_background(image, back_box, _EXTRA = extra) + if n_elements(noise_std) ne 0 then $ + if n_elements(noise_std) ne n_elements(image) then $ + noise_std = replicate(noise_std[0], siz[0], siz[1]) + stars = fltarr(siz[0], siz[1]) + + ; Define some program parameters and default values + re_fitting = 0 ; re_fitting = 0 --> normal mode + if n_elements(x_in) ne 0 and n_elements(y_in) ne 0 and n_elements(f_in) ne 0 then $ + re_fitting = 2 ; re_fitting = 2 --> fit of input list of sources + if n_elements(n_iter) eq 0 then n_iter = 1 + if size52(psf, /N_DIM) eq 3 then $ + n_psf = (size52(psf, /DIM))[2] else n_psf = 1 + psf_fwhm = starfinder_fwhm(psf, n_psf) + id_par = starfinder_id_par(psf_fwhm, _EXTRA = extra) + corr_par = starfinder_corr_par(psf, psf_fwhm, n_psf, _EXTRA = extra) + ; modified March 2012 + if re_fitting eq 2 then $ + fit_par = starfinder_fit_par(psf, psf_fwhm, n_psf, /WIDER, _EXTRA = extra) else $ + fit_par = starfinder_fit_par(psf, psf_fwhm, n_psf, _EXTRA = extra) +; fit_par = starfinder_fit_par(psf, psf_fwhm, n_psf, _EXTRA = extra) + if keyword_set(deblend) or keyword_set(deblost) then $ + deb_par = starfinder_deb_par(psf, psf_fwhm, n_psf, _EXTRA = extra) + if n_elements(sv_par) eq 0 then begin + fit_data = ptr_new(/ALLOCATE) & model_data = ptr_new(/ALLOCATE) + endif + + ; Iterative search for suspected stars and subsequent analysis + n_levels = n_elements(threshold) + n_stars = 0L & n_presumed = 0L & n_max = 0L + if re_fitting eq 2 then begin + n_levels = 1 + n_stars = n_elements(f_in) + list_of_stars = sort_list(reverse_class(starlist(n_stars, x_in, y_in, f_in))) + if keyword_set(psf_type) then $ + stars = image_model(x_in, y_in, f_in, siz[0], siz[1], psf_type, $ + psf_data, _EXTRA = extra) else $ + if n_elements(sv_par) ne 0 then $ + stars = image_model(x_in, y_in, f_in, siz[0], siz[1], psf, $ + LX = sv_par.lx, UX = sv_par.ux, LY = sv_par.ly, UY = sv_par.uy, $ + _EXTRA = extra) else $ + stars = image_model(x_in, y_in, f_in, siz[0], siz[1], psf, _EXTRA = extra) + endif + + for n_lev = 0L, n_levels - 1 do begin + + ; Update detection threshold + threshold_n = float(threshold[n_lev]) + if keyword_set(rel_threshold) and n_elements(noise_std) ne 0 then $ + threshold_n = threshold_n * noise_std + if n_elements(threshold_n) eq 1 then $ + threshold_n = replicate(threshold_n[0], siz[0], siz[1]) + + ; Phase 1: find local maxima having + ; central intensity > known stars + background + threshold + if re_fitting eq 0 then begin + if not keyword_set(silent) then $ + print, "STARFINDER: search for candidate stars" + search_objects, image - stars, LOW_SURFACE = background, threshold_n, $ + _EXTRA = extra, n_max, x0, y0, i0 + n_presumed = n_presumed + n_max + endif + + ; Phase 2: analyze presumed stars with correlation and fitting, + ; estimating positions and fluxes. + if n_max ne 0 and re_fitting eq 0 then begin + if not keyword_set(silent) then $ + print, "STARFINDER: analysis of candidate stars" + list_of_max = starlist(n_max, x0, y0, i0) + list_of_stars = merge_list(list_of_stars, sort_list(list_of_max)) + for n = n_stars, n_stars - 1 + n_max do $ + starfinder_analyze, list_of_stars, n, image, siz, background, stars, $ + noise_std, sv_par, x_bad, y_bad, id_par, $ + corr_par, fit_par, fit_data, model_data, $ + threshold_n, min_correlation, re_fitting, _EXTRA = extra, $ + PSF_TYPE = psf_type, PSF_DATA = psf_data + list_of_stars = sort_list(extract_stars(list_of_stars, n_stars)) + ; Update background estimate + if n_elements(back_box) ne 0 then $ + background = estimate_background(image - stars, back_box, _EXTRA = extra) + ; Modify fitting parameters for subsequent operations + fit_par = starfinder_fit_par(psf, psf_fwhm, n_psf, /WIDER, _EXTRA = extra) + if ptr_valid(fit_data) then ptr_free, fit_data + endif ; n_max ne 0 + + ; Phase 2b: de-blend detected objects. + if keyword_set(deblend) and n_lev eq n_levels - 1 and n_stars ne 0 and re_fitting eq 0 then begin + if not keyword_set(silent) then $ + print, "STARFINDER: deblending of detected stars" + ; Check each detected star: is it a blend? + for n = 0L, n_stars - 1 do $ + starfinder_deblend, list_of_stars, n, image, siz, background, stars, $ + noise_std, sv_par, x_bad, y_bad, id_par, $ + fit_par, deb_par, fit_data, model_data, $ + threshold_n, re_fitting, _EXTRA = extra, /AROUND, $ + PSF_TYPE = psf_type, PSF_DATA = psf_data + list_of_stars = sort_list(extract_stars(list_of_stars, n_stars)) + ; Update background estimate + if n_elements(back_box) ne 0 then $ + background = estimate_background(image - stars, back_box, _EXTRA = extra) + endif + + ; Phase 2c: recover lost objects to search for blends. +; if keyword_set(deblost) and n_lev eq n_levels - 1 and n_stars ne 0 and re_fitting eq 0 then begin + if keyword_set(deblost) and n_lev eq n_levels - 1 and re_fitting eq 0 then begin + if not keyword_set(silent) then $ + print, "STARFINDER: search for blends among lost objects" + search_objects, image - stars, LOW_SURFACE = background, threshold_n, $ + _EXTRA = extra, n_max, x0, y0, i0 + if n_max ne 0 then begin + list_of_max = starlist(n_max, x0, y0, i0) + list_of_stars = merge_list(list_of_stars, sort_list(list_of_max)) + for n = n_stars, n_stars - 1 + n_max do $ + starfinder_deblend, list_of_stars, n, image, siz, background, stars, $ + noise_std, sv_par, x_bad, y_bad, id_par, fit_par, $ + deb_par, fit_data, model_data, threshold_n, $ + re_fitting, _EXTRA = extra, $ + PSF_TYPE = psf_type, PSF_DATA = psf_data + list_of_stars = sort_list(extract_stars(list_of_stars, n_stars)) + ; Update background estimate + if n_elements(back_box) ne 0 then $ + background = estimate_background(image - stars, back_box, _EXTRA = extra) + endif + endif + + ; Phase 3: re-determination of positions and fluxes by iterative + ; fitting of detected stars. + if n_lev eq n_levels - 1 then maxit = n_iter else $ + if keyword_set(no_intermediate) then maxit = 0 else maxit = 1 + if n_stars ne 0 then list0 = list_of_stars + if re_fitting eq 0 then re_fitting = 1 + iter = 0L & converging = n_stars eq 0 + while iter lt maxit and not converging do begin + if not keyword_set(silent) then $ + if n_lev lt n_levels - 1 then $ + print, "STARFINDER: re-fitting" else $ + print, "STARFINDER: re-fitting: iteration", iter + 1 +; Modified March 2012 +; if n_lev lt n_levels - 1 then $ +; print, "STARFINDER: intermediate re-fitting" else $ +; print, "STARFINDER: final re-fitting: iteration", iter + 1 + for n = 0L, n_stars - 1 do $ + ; modified March 2012 + if list_of_stars[n].is_a_star or re_fitting eq 1 then $ ;;; if list_of_stars[n].is_a_star or re_fitting eq 1 or iter eq 0 then $ + starfinder_fit, list_of_stars, n, image, siz, background, stars, $ + noise_std, sv_par, x_bad, y_bad, fit_par, fit_data, $ + model_data, threshold_n, re_fitting, _EXTRA = extra, $ + PSF_TYPE = psf_type, PSF_DATA = psf_data + if maxit gt 1 then $ + converging = starfinder_converg(list0, list_of_stars, _EXTRA = extra) + iter = iter + 1 & list0 = list_of_stars + endwhile + if re_fitting eq 1 then re_fitting = 0 + fit_par = starfinder_fit_par(psf, psf_fwhm, n_psf, _EXTRA = extra) + if ptr_valid(fit_data) then ptr_free, fit_data + + endfor ; end of cycle on threshold levels + + ; De-allocate pointer heap variables + starfinder_dealloc, corr_par, fit_data, model_data + + ; Save results + if n_stars ne 0 then begin + list_of_stars = sort_list(extract_stars(list_of_stars, n_stars)) + star_param, list_of_stars, n_stars, $ + x, y, fluxes, correlation, sigma_x, sigma_y, sigma_f + endif + if not keyword_set(silent) then begin + if re_fitting ne 2 then $ + print, "STARFINDER", n_presumed, " candidate stars" + print, "STARFINDER", n_stars, " detected stars" + endif + return +end diff --git a/starlist.pro b/starlist.pro new file mode 100644 index 0000000000000000000000000000000000000000..11994b1f5a0b7d7cc56f5fe2617617dd8138964d --- /dev/null +++ b/starlist.pro @@ -0,0 +1,42 @@ +; $Id: starlist.pro, v 1.1 Mar 2012 e.d. $ +; +;+ +; NAME: +; STARLIST +; +; PURPOSE: +; Create a new list of elements representing either +; true or presumed stars. +; +; CATEGORY: +; STARFINDER auxiliary procedures. +; +; CALLING SEQUENCE: +; Result = STARLIST(N, X, Y, F, C) +; +; INPUTS: +; N: number of elements in the list +; +; OPTIONAL INPUTS: +; X, Y, F: 1D vectors with position and flux of new elements +; +; C: 1D vector with correlation coefficients of new elements (if available) +; +; OUTPUTS: +; Return possibly initialized list of N elements +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, June 2001. +; 1) Added optional input C in STARLIST (E.D., March 2012). +; 2) Created this file (E. D., March 2012). +;- + +FUNCTION starlist, n, x, y, f, c + + on_error, 2 + element = star() + list = replicate(element, n) + if n_elements(x) ne 0 and n_elements(y) ne 0 then $ + update_list, list, x, y, f, c + return, list +end diff --git a/stars.txt b/stars.txt new file mode 100644 index 0000000000000000000000000000000000000000..e6ca708d3fb4450e3895409c2666fbddcfbb8195 --- /dev/null +++ b/stars.txt @@ -0,0 +1,1000 @@ + 141.444 239.065 1.00000e+007 0.000000 0.000000 0.000000 0.000000 + 263.880 245.285 7.47086e+006 0.000000 0.000000 0.000000 0.000000 + 231.020 322.379 6.59129e+006 0.000000 0.000000 0.000000 0.000000 + 238.198 88.8366 3.32472e+006 0.000000 0.000000 0.000000 0.000000 + 278.154 274.961 1.83254e+006 0.000000 0.000000 0.000000 0.000000 + 29.2396 101.448 1.59408e+006 0.000000 0.000000 0.000000 0.000000 + 102.241 189.381 1.21068e+006 0.000000 0.000000 0.000000 0.000000 + 92.1810 178.633 773162. 0.000000 0.000000 0.000000 0.000000 + 302.978 15.4805 737351. 0.000000 0.000000 0.000000 0.000000 + 248.226 279.705 704095. 0.000000 0.000000 0.000000 0.000000 + 141.675 175.461 676584. 0.000000 0.000000 0.000000 0.000000 + 340.424 329.580 661287. 0.000000 0.000000 0.000000 0.000000 + 256.130 5.77905 638781. 0.000000 0.000000 0.000000 0.000000 + 99.0782 359.178 519881. 0.000000 0.000000 0.000000 0.000000 + 230.265 63.1091 506067. 0.000000 0.000000 0.000000 0.000000 + 160.545 357.701 500177. 0.000000 0.000000 0.000000 0.000000 + 87.1833 35.7546 479293. 0.000000 0.000000 0.000000 0.000000 + 181.326 21.0803 457698. 0.000000 0.000000 0.000000 0.000000 + 193.221 151.761 437480. 0.000000 0.000000 0.000000 0.000000 + 331.626 281.044 421626. 0.000000 0.000000 0.000000 0.000000 + 196.967 215.621 411944. 0.000000 0.000000 0.000000 0.000000 + 332.888 67.3763 396346. 0.000000 0.000000 0.000000 0.000000 + 301.842 183.755 377396. 0.000000 0.000000 0.000000 0.000000 + 14.9324 100.224 356474. 0.000000 0.000000 0.000000 0.000000 + 188.292 324.556 344011. 0.000000 0.000000 0.000000 0.000000 + 79.8033 20.1347 340647. 0.000000 0.000000 0.000000 0.000000 + 203.205 292.668 340602. 0.000000 0.000000 0.000000 0.000000 + 4.08286 251.532 336837. 0.000000 0.000000 0.000000 0.000000 + 89.6078 216.392 308973. 0.000000 0.000000 0.000000 0.000000 + 153.712 171.918 296011. 0.000000 0.000000 0.000000 0.000000 + 248.966 195.936 266923. 0.000000 0.000000 0.000000 0.000000 + 117.632 48.2569 260607. 0.000000 0.000000 0.000000 0.000000 + 308.253 29.6234 260248. 0.000000 0.000000 0.000000 0.000000 + 203.769 56.2594 250214. 0.000000 0.000000 0.000000 0.000000 + 238.393 363.804 237712. 0.000000 0.000000 0.000000 0.000000 + 316.437 357.973 236734. 0.000000 0.000000 0.000000 0.000000 + 306.785 152.560 235881. 0.000000 0.000000 0.000000 0.000000 + 342.694 277.907 233065. 0.000000 0.000000 0.000000 0.000000 + 54.1969 292.909 231872. 0.000000 0.000000 0.000000 0.000000 + 68.3034 347.885 222287. 0.000000 0.000000 0.000000 0.000000 + 94.0176 60.6761 221532. 0.000000 0.000000 0.000000 0.000000 + 102.126 156.966 221122. 0.000000 0.000000 0.000000 0.000000 + 328.504 50.0842 219851. 0.000000 0.000000 0.000000 0.000000 + 323.204 36.2747 212618. 0.000000 0.000000 0.000000 0.000000 + 61.5537 54.2696 205046. 0.000000 0.000000 0.000000 0.000000 + 167.366 142.279 197444. 0.000000 0.000000 0.000000 0.000000 + 129.178 170.515 193344. 0.000000 0.000000 0.000000 0.000000 + 80.7047 345.043 190810. 0.000000 0.000000 0.000000 0.000000 + 214.974 206.928 186916. 0.000000 0.000000 0.000000 0.000000 + 75.1124 331.121 186550. 0.000000 0.000000 0.000000 0.000000 + 219.742 57.2710 181822. 0.000000 0.000000 0.000000 0.000000 + 275.859 227.410 178175. 0.000000 0.000000 0.000000 0.000000 + 93.2953 151.123 176123. 0.000000 0.000000 0.000000 0.000000 + 231.419 322.719 173074. 0.000000 0.000000 0.000000 0.000000 + 238.244 107.443 172252. 0.000000 0.000000 0.000000 0.000000 + 152.464 347.544 170346. 0.000000 0.000000 0.000000 0.000000 + 143.542 295.029 168789. 0.000000 0.000000 0.000000 0.000000 + 169.870 248.333 168593. 0.000000 0.000000 0.000000 0.000000 + 292.060 261.347 164718. 0.000000 0.000000 0.000000 0.000000 + 35.8475 94.3537 162842. 0.000000 0.000000 0.000000 0.000000 + 48.4971 310.308 161867. 0.000000 0.000000 0.000000 0.000000 + 161.854 258.059 155129. 0.000000 0.000000 0.000000 0.000000 + 261.010 315.584 149634. 0.000000 0.000000 0.000000 0.000000 + 270.759 170.260 149466. 0.000000 0.000000 0.000000 0.000000 + 161.598 80.8070 146634. 0.000000 0.000000 0.000000 0.000000 + 118.249 347.903 144433. 0.000000 0.000000 0.000000 0.000000 + 188.792 324.297 142534. 0.000000 0.000000 0.000000 0.000000 + 150.565 319.440 142475. 0.000000 0.000000 0.000000 0.000000 + 294.201 322.857 139912. 0.000000 0.000000 0.000000 0.000000 + 40.3396 231.383 138934. 0.000000 0.000000 0.000000 0.000000 + 300.716 321.223 136407. 0.000000 0.000000 0.000000 0.000000 + 313.770 53.2494 131038. 0.000000 0.000000 0.000000 0.000000 + 165.575 116.979 125809. 0.000000 0.000000 0.000000 0.000000 + 37.1321 265.568 122559. 0.000000 0.000000 0.000000 0.000000 + 179.508 9.89236 114837. 0.000000 0.000000 0.000000 0.000000 + 225.502 251.722 113258. 0.000000 0.000000 0.000000 0.000000 + 363.681 80.0904 111222. 0.000000 0.000000 0.000000 0.000000 + 171.779 278.806 111141. 0.000000 0.000000 0.000000 0.000000 + 58.4512 258.920 110230. 0.000000 0.000000 0.000000 0.000000 + 316.804 304.948 107616. 0.000000 0.000000 0.000000 0.000000 + 302.164 91.1036 107397. 0.000000 0.000000 0.000000 0.000000 + 26.5350 219.965 106188. 0.000000 0.000000 0.000000 0.000000 + 283.449 155.508 105731. 0.000000 0.000000 0.000000 0.000000 + 211.195 185.650 105438. 0.000000 0.000000 0.000000 0.000000 + 188.711 82.2072 104895. 0.000000 0.000000 0.000000 0.000000 + 72.2484 105.537 101507. 0.000000 0.000000 0.000000 0.000000 + 289.912 3.21481 98803.0 0.000000 0.000000 0.000000 0.000000 + 254.344 7.08516 98763.3 0.000000 0.000000 0.000000 0.000000 + 302.036 52.2602 98698.9 0.000000 0.000000 0.000000 0.000000 + 274.811 359.645 95523.1 0.000000 0.000000 0.000000 0.000000 + 118.079 32.5073 93886.7 0.000000 0.000000 0.000000 0.000000 + 285.718 23.3751 93222.0 0.000000 0.000000 0.000000 0.000000 + 59.9410 178.202 92600.3 0.000000 0.000000 0.000000 0.000000 + 61.3693 269.778 91754.5 0.000000 0.000000 0.000000 0.000000 + 202.962 100.187 90361.8 0.000000 0.000000 0.000000 0.000000 + 256.779 96.4422 88950.8 0.000000 0.000000 0.000000 0.000000 + 95.8379 144.879 87336.5 0.000000 0.000000 0.000000 0.000000 + 231.668 118.223 86284.7 0.000000 0.000000 0.000000 0.000000 + 192.931 254.259 85762.4 0.000000 0.000000 0.000000 0.000000 + 232.133 31.2530 84899.0 0.000000 0.000000 0.000000 0.000000 + 94.6659 174.799 83423.6 0.000000 0.000000 0.000000 0.000000 + 136.477 188.531 83193.9 0.000000 0.000000 0.000000 0.000000 + 298.921 278.288 82613.9 0.000000 0.000000 0.000000 0.000000 + 39.6911 230.275 82228.0 0.000000 0.000000 0.000000 0.000000 + 115.177 244.697 82011.5 0.000000 0.000000 0.000000 0.000000 + 191.734 189.689 81725.9 0.000000 0.000000 0.000000 0.000000 + 255.640 247.122 79493.9 0.000000 0.000000 0.000000 0.000000 + 148.062 143.007 75975.5 0.000000 0.000000 0.000000 0.000000 + 122.010 119.083 75621.7 0.000000 0.000000 0.000000 0.000000 + 200.155 190.028 75504.5 0.000000 0.000000 0.000000 0.000000 + 169.312 285.975 74337.4 0.000000 0.000000 0.000000 0.000000 + 357.948 307.675 73702.0 0.000000 0.000000 0.000000 0.000000 + 359.127 274.367 71515.1 0.000000 0.000000 0.000000 0.000000 + 45.3947 57.2922 71148.6 0.000000 0.000000 0.000000 0.000000 + 204.018 10.9722 70522.5 0.000000 0.000000 0.000000 0.000000 + 244.906 334.343 65969.8 0.000000 0.000000 0.000000 0.000000 + 258.525 297.974 65360.8 0.000000 0.000000 0.000000 0.000000 + 258.209 214.108 64573.3 0.000000 0.000000 0.000000 0.000000 + 199.392 61.8723 64532.7 0.000000 0.000000 0.000000 0.000000 + 61.0304 229.297 63913.4 0.000000 0.000000 0.000000 0.000000 + 160.626 164.319 62196.8 0.000000 0.000000 0.000000 0.000000 + 329.092 248.683 61878.7 0.000000 0.000000 0.000000 0.000000 + 127.118 169.217 61871.3 0.000000 0.000000 0.000000 0.000000 + 191.067 240.918 61298.6 0.000000 0.000000 0.000000 0.000000 + 250.683 28.4992 61234.0 0.000000 0.000000 0.000000 0.000000 + 40.4708 4.53567 60880.9 0.000000 0.000000 0.000000 0.000000 + 259.064 80.9131 60770.4 0.000000 0.000000 0.000000 0.000000 + 143.693 92.0707 58822.8 0.000000 0.000000 0.000000 0.000000 + 27.1328 110.225 57835.5 0.000000 0.000000 0.000000 0.000000 + 142.263 303.959 57615.0 0.000000 0.000000 0.000000 0.000000 + 22.9556 296.589 57282.7 0.000000 0.000000 0.000000 0.000000 + 23.7486 312.086 56885.8 0.000000 0.000000 0.000000 0.000000 + 103.904 211.577 56217.2 0.000000 0.000000 0.000000 0.000000 + 122.554 167.526 56121.2 0.000000 0.000000 0.000000 0.000000 + 275.144 70.9676 56078.2 0.000000 0.000000 0.000000 0.000000 + 181.944 342.399 56035.8 0.000000 0.000000 0.000000 0.000000 + 248.900 264.279 55127.5 0.000000 0.000000 0.000000 0.000000 + 179.764 48.4617 54755.7 0.000000 0.000000 0.000000 0.000000 + 64.7956 190.716 54361.8 0.000000 0.000000 0.000000 0.000000 + 168.035 354.381 53846.0 0.000000 0.000000 0.000000 0.000000 + 305.555 13.5973 53464.5 0.000000 0.000000 0.000000 0.000000 + 254.125 164.183 52782.5 0.000000 0.000000 0.000000 0.000000 + 129.826 82.6673 52045.7 0.000000 0.000000 0.000000 0.000000 + 175.487 96.5041 51705.9 0.000000 0.000000 0.000000 0.000000 + 131.850 5.19465 49549.7 0.000000 0.000000 0.000000 0.000000 + 251.157 47.6331 48155.6 0.000000 0.000000 0.000000 0.000000 + 156.185 297.552 44830.9 0.000000 0.000000 0.000000 0.000000 + 32.7849 347.404 44548.8 0.000000 0.000000 0.000000 0.000000 + 139.296 359.265 44434.4 0.000000 0.000000 0.000000 0.000000 + 78.6520 224.699 43521.3 0.000000 0.000000 0.000000 0.000000 + 93.3061 340.185 42670.1 0.000000 0.000000 0.000000 0.000000 + 181.422 158.919 42212.9 0.000000 0.000000 0.000000 0.000000 + 196.592 280.650 41965.6 0.000000 0.000000 0.000000 0.000000 + 84.1532 66.5267 41632.9 0.000000 0.000000 0.000000 0.000000 + 280.767 87.3626 41607.4 0.000000 0.000000 0.000000 0.000000 + 271.622 166.118 41468.1 0.000000 0.000000 0.000000 0.000000 + 13.6983 304.871 41131.6 0.000000 0.000000 0.000000 0.000000 + 48.1051 227.197 40753.8 0.000000 0.000000 0.000000 0.000000 + 20.1975 354.705 40607.0 0.000000 0.000000 0.000000 0.000000 + 345.646 132.836 40481.6 0.000000 0.000000 0.000000 0.000000 + 183.484 238.597 40418.5 0.000000 0.000000 0.000000 0.000000 + 238.737 121.789 40295.7 0.000000 0.000000 0.000000 0.000000 + 263.596 281.672 40224.8 0.000000 0.000000 0.000000 0.000000 + 162.429 177.789 40063.9 0.000000 0.000000 0.000000 0.000000 + 95.0396 86.2365 40052.7 0.000000 0.000000 0.000000 0.000000 + 354.378 190.422 39998.6 0.000000 0.000000 0.000000 0.000000 + 114.971 15.8472 39566.9 0.000000 0.000000 0.000000 0.000000 + 26.5936 16.1287 39319.5 0.000000 0.000000 0.000000 0.000000 + 77.1282 249.081 39219.6 0.000000 0.000000 0.000000 0.000000 + 306.809 9.47797 38975.8 0.000000 0.000000 0.000000 0.000000 + 83.0219 60.7900 38736.6 0.000000 0.000000 0.000000 0.000000 + 137.099 162.847 38704.7 0.000000 0.000000 0.000000 0.000000 + 64.8679 167.281 38437.0 0.000000 0.000000 0.000000 0.000000 + 211.216 148.011 38358.8 0.000000 0.000000 0.000000 0.000000 + 40.9717 155.190 38339.0 0.000000 0.000000 0.000000 0.000000 + 182.266 28.3910 38258.6 0.000000 0.000000 0.000000 0.000000 + 244.444 255.625 38151.2 0.000000 0.000000 0.000000 0.000000 + 32.2430 98.1823 38130.0 0.000000 0.000000 0.000000 0.000000 + 143.451 292.529 38101.9 0.000000 0.000000 0.000000 0.000000 + 180.375 259.875 37821.1 0.000000 0.000000 0.000000 0.000000 + 107.606 156.024 37788.5 0.000000 0.000000 0.000000 0.000000 + 210.998 81.1154 37774.4 0.000000 0.000000 0.000000 0.000000 + 340.622 133.712 37698.4 0.000000 0.000000 0.000000 0.000000 + 197.761 163.981 37594.1 0.000000 0.000000 0.000000 0.000000 + 93.8015 156.494 37421.5 0.000000 0.000000 0.000000 0.000000 + 260.590 350.815 37192.3 0.000000 0.000000 0.000000 0.000000 + 317.750 64.7870 37074.9 0.000000 0.000000 0.000000 0.000000 + 70.4437 190.468 37004.9 0.000000 0.000000 0.000000 0.000000 + 221.623 90.9939 36930.1 0.000000 0.000000 0.000000 0.000000 + 128.444 186.987 36916.6 0.000000 0.000000 0.000000 0.000000 + 26.0331 290.122 36763.0 0.000000 0.000000 0.000000 0.000000 + 87.0650 258.033 36545.5 0.000000 0.000000 0.000000 0.000000 + 279.910 342.241 36502.7 0.000000 0.000000 0.000000 0.000000 + 121.926 276.727 36382.9 0.000000 0.000000 0.000000 0.000000 + 265.246 137.480 36338.4 0.000000 0.000000 0.000000 0.000000 + 292.552 86.9713 36232.1 0.000000 0.000000 0.000000 0.000000 + 338.423 108.581 35388.8 0.000000 0.000000 0.000000 0.000000 + 196.687 304.196 35296.3 0.000000 0.000000 0.000000 0.000000 + 148.693 99.0690 35194.9 0.000000 0.000000 0.000000 0.000000 + 158.453 231.067 35136.4 0.000000 0.000000 0.000000 0.000000 + 300.064 115.489 35068.0 0.000000 0.000000 0.000000 0.000000 + 15.3618 355.783 34731.0 0.000000 0.000000 0.000000 0.000000 + 259.877 313.889 34655.6 0.000000 0.000000 0.000000 0.000000 + 344.235 60.4770 34556.6 0.000000 0.000000 0.000000 0.000000 + 171.975 216.563 34190.5 0.000000 0.000000 0.000000 0.000000 + 255.588 17.8280 34086.0 0.000000 0.000000 0.000000 0.000000 + 141.199 363.133 34003.9 0.000000 0.000000 0.000000 0.000000 + 198.494 316.173 33991.7 0.000000 0.000000 0.000000 0.000000 + 14.4216 269.120 33928.2 0.000000 0.000000 0.000000 0.000000 + 245.228 160.431 33848.2 0.000000 0.000000 0.000000 0.000000 + 359.424 72.3061 33841.2 0.000000 0.000000 0.000000 0.000000 + 11.2306 35.1264 33807.8 0.000000 0.000000 0.000000 0.000000 + 278.206 274.896 33586.8 0.000000 0.000000 0.000000 0.000000 + 12.5086 30.9141 33398.5 0.000000 0.000000 0.000000 0.000000 + 312.332 26.9308 33267.9 0.000000 0.000000 0.000000 0.000000 + 185.655 59.1044 32918.2 0.000000 0.000000 0.000000 0.000000 + 61.2724 190.277 32798.8 0.000000 0.000000 0.000000 0.000000 + 220.191 189.604 32750.6 0.000000 0.000000 0.000000 0.000000 + 127.860 121.978 32729.5 0.000000 0.000000 0.000000 0.000000 + 355.378 252.497 32705.5 0.000000 0.000000 0.000000 0.000000 + 214.552 255.179 32384.5 0.000000 0.000000 0.000000 0.000000 + 57.5893 181.740 32368.8 0.000000 0.000000 0.000000 0.000000 + 14.4280 335.523 32204.1 0.000000 0.000000 0.000000 0.000000 + 21.6336 118.752 31321.7 0.000000 0.000000 0.000000 0.000000 + 162.314 205.696 30852.6 0.000000 0.000000 0.000000 0.000000 + 223.611 243.805 30825.2 0.000000 0.000000 0.000000 0.000000 + 340.626 15.6565 30613.5 0.000000 0.000000 0.000000 0.000000 + 255.144 184.321 30606.3 0.000000 0.000000 0.000000 0.000000 + 150.121 63.3999 30459.8 0.000000 0.000000 0.000000 0.000000 + 233.868 252.369 30407.8 0.000000 0.000000 0.000000 0.000000 + 91.4964 11.0882 30184.1 0.000000 0.000000 0.000000 0.000000 + 140.221 260.644 30161.7 0.000000 0.000000 0.000000 0.000000 + 285.664 310.042 30151.7 0.000000 0.000000 0.000000 0.000000 + 83.0180 341.710 30018.8 0.000000 0.000000 0.000000 0.000000 + 259.472 335.211 29964.9 0.000000 0.000000 0.000000 0.000000 + 104.117 221.599 29905.0 0.000000 0.000000 0.000000 0.000000 + 158.391 195.846 29880.7 0.000000 0.000000 0.000000 0.000000 + 194.507 176.202 29819.0 0.000000 0.000000 0.000000 0.000000 + 47.3849 327.727 29782.1 0.000000 0.000000 0.000000 0.000000 + 223.476 102.402 29754.3 0.000000 0.000000 0.000000 0.000000 + 238.197 4.73843 29652.0 0.000000 0.000000 0.000000 0.000000 + 86.1274 125.202 29535.9 0.000000 0.000000 0.000000 0.000000 + 218.689 39.0515 29162.9 0.000000 0.000000 0.000000 0.000000 + 103.693 162.297 29135.5 0.000000 0.000000 0.000000 0.000000 + 178.188 153.348 28962.6 0.000000 0.000000 0.000000 0.000000 + 76.0960 337.107 28841.5 0.000000 0.000000 0.000000 0.000000 + 279.589 313.014 28506.7 0.000000 0.000000 0.000000 0.000000 + 152.271 293.573 28390.0 0.000000 0.000000 0.000000 0.000000 + 188.247 256.592 28375.0 0.000000 0.000000 0.000000 0.000000 + 354.740 209.341 28323.1 0.000000 0.000000 0.000000 0.000000 + 193.396 114.306 28163.6 0.000000 0.000000 0.000000 0.000000 + 334.937 23.8380 28114.4 0.000000 0.000000 0.000000 0.000000 + 24.7114 292.980 27856.0 0.000000 0.000000 0.000000 0.000000 + 156.112 316.887 27722.5 0.000000 0.000000 0.000000 0.000000 + 209.919 209.652 27519.5 0.000000 0.000000 0.000000 0.000000 + 280.987 171.985 27475.0 0.000000 0.000000 0.000000 0.000000 + 143.491 296.094 27402.2 0.000000 0.000000 0.000000 0.000000 + 297.004 322.017 27159.2 0.000000 0.000000 0.000000 0.000000 + 61.5556 102.870 27043.6 0.000000 0.000000 0.000000 0.000000 + 328.340 320.377 27039.4 0.000000 0.000000 0.000000 0.000000 + 190.481 154.634 27030.4 0.000000 0.000000 0.000000 0.000000 + 342.294 54.8700 26940.8 0.000000 0.000000 0.000000 0.000000 + 37.8107 29.3057 26879.3 0.000000 0.000000 0.000000 0.000000 + 246.564 238.117 26846.3 0.000000 0.000000 0.000000 0.000000 + 64.6574 259.351 26589.7 0.000000 0.000000 0.000000 0.000000 + 280.739 212.330 26546.6 0.000000 0.000000 0.000000 0.000000 + 54.7320 320.027 26357.3 0.000000 0.000000 0.000000 0.000000 + 141.126 125.049 26310.2 0.000000 0.000000 0.000000 0.000000 + 314.045 320.963 26281.7 0.000000 0.000000 0.000000 0.000000 + 122.404 229.985 26176.5 0.000000 0.000000 0.000000 0.000000 + 232.309 157.823 26172.8 0.000000 0.000000 0.000000 0.000000 + 133.826 56.8797 26135.3 0.000000 0.000000 0.000000 0.000000 + 251.216 339.756 26089.7 0.000000 0.000000 0.000000 0.000000 + 248.913 178.616 26077.4 0.000000 0.000000 0.000000 0.000000 + 183.376 354.064 26073.6 0.000000 0.000000 0.000000 0.000000 + 165.715 273.441 26047.2 0.000000 0.000000 0.000000 0.000000 + 150.352 177.678 25951.7 0.000000 0.000000 0.000000 0.000000 + 313.176 331.037 25901.4 0.000000 0.000000 0.000000 0.000000 + 270.414 49.7428 25779.4 0.000000 0.000000 0.000000 0.000000 + 73.8670 60.6083 25582.7 0.000000 0.000000 0.000000 0.000000 + 355.293 350.701 25541.3 0.000000 0.000000 0.000000 0.000000 + 262.530 141.016 25468.6 0.000000 0.000000 0.000000 0.000000 + 255.002 111.344 25217.2 0.000000 0.000000 0.000000 0.000000 + 289.411 71.4270 25216.9 0.000000 0.000000 0.000000 0.000000 + 219.648 180.555 24981.3 0.000000 0.000000 0.000000 0.000000 + 175.428 132.120 24967.6 0.000000 0.000000 0.000000 0.000000 + 125.422 289.734 24945.9 0.000000 0.000000 0.000000 0.000000 + 183.006 123.685 24945.5 0.000000 0.000000 0.000000 0.000000 + 176.200 116.606 24915.3 0.000000 0.000000 0.000000 0.000000 + 291.452 243.735 24911.3 0.000000 0.000000 0.000000 0.000000 + 326.645 303.884 24786.9 0.000000 0.000000 0.000000 0.000000 + 214.429 143.610 24659.9 0.000000 0.000000 0.000000 0.000000 + 311.426 47.9050 24614.0 0.000000 0.000000 0.000000 0.000000 + 98.3522 320.341 24601.9 0.000000 0.000000 0.000000 0.000000 + 220.412 139.228 24593.1 0.000000 0.000000 0.000000 0.000000 + 42.7167 59.2701 24578.4 0.000000 0.000000 0.000000 0.000000 + 107.204 289.952 24508.6 0.000000 0.000000 0.000000 0.000000 + 46.9480 118.741 24437.0 0.000000 0.000000 0.000000 0.000000 + 159.269 55.3080 24427.0 0.000000 0.000000 0.000000 0.000000 + 170.868 74.5069 24309.4 0.000000 0.000000 0.000000 0.000000 + 168.355 155.541 24229.1 0.000000 0.000000 0.000000 0.000000 + 306.077 303.947 23994.5 0.000000 0.000000 0.000000 0.000000 + 232.098 108.789 23993.4 0.000000 0.000000 0.000000 0.000000 + 136.224 253.386 23847.3 0.000000 0.000000 0.000000 0.000000 + 276.501 30.7327 23746.8 0.000000 0.000000 0.000000 0.000000 + 273.140 298.849 23502.5 0.000000 0.000000 0.000000 0.000000 + 262.615 40.7531 23376.4 0.000000 0.000000 0.000000 0.000000 + 21.2239 153.017 23342.0 0.000000 0.000000 0.000000 0.000000 + 357.514 138.104 23264.1 0.000000 0.000000 0.000000 0.000000 + 340.214 29.6600 23083.4 0.000000 0.000000 0.000000 0.000000 + 139.031 303.849 23011.2 0.000000 0.000000 0.000000 0.000000 + 127.348 191.829 22954.2 0.000000 0.000000 0.000000 0.000000 + 241.219 266.692 22941.1 0.000000 0.000000 0.000000 0.000000 + 170.164 68.4117 22927.0 0.000000 0.000000 0.000000 0.000000 + 242.757 102.877 22885.0 0.000000 0.000000 0.000000 0.000000 + 346.583 148.389 22865.2 0.000000 0.000000 0.000000 0.000000 + 196.090 346.828 22838.0 0.000000 0.000000 0.000000 0.000000 + 123.187 21.0655 22802.1 0.000000 0.000000 0.000000 0.000000 + 218.130 205.490 22747.1 0.000000 0.000000 0.000000 0.000000 + 129.180 235.133 22601.7 0.000000 0.000000 0.000000 0.000000 + 323.108 7.16255 22454.3 0.000000 0.000000 0.000000 0.000000 + 161.937 162.696 22281.2 0.000000 0.000000 0.000000 0.000000 + 342.856 196.480 22150.8 0.000000 0.000000 0.000000 0.000000 + 245.465 153.179 22044.6 0.000000 0.000000 0.000000 0.000000 + 262.846 119.758 21830.1 0.000000 0.000000 0.000000 0.000000 + 247.060 44.7340 21737.1 0.000000 0.000000 0.000000 0.000000 + 14.6492 242.304 21586.9 0.000000 0.000000 0.000000 0.000000 + 305.187 298.337 21354.4 0.000000 0.000000 0.000000 0.000000 + 136.872 92.1575 21351.5 0.000000 0.000000 0.000000 0.000000 + 243.913 241.775 21328.0 0.000000 0.000000 0.000000 0.000000 + 61.8279 334.386 21259.3 0.000000 0.000000 0.000000 0.000000 + 316.777 211.091 21234.2 0.000000 0.000000 0.000000 0.000000 + 5.01734 304.795 21216.3 0.000000 0.000000 0.000000 0.000000 + 229.980 73.8202 21105.5 0.000000 0.000000 0.000000 0.000000 + 45.3229 129.853 21087.7 0.000000 0.000000 0.000000 0.000000 + 305.763 76.3401 20793.4 0.000000 0.000000 0.000000 0.000000 + 144.351 350.475 20430.6 0.000000 0.000000 0.000000 0.000000 + 312.433 208.532 20342.0 0.000000 0.000000 0.000000 0.000000 + 217.491 36.1401 20095.0 0.000000 0.000000 0.000000 0.000000 + 234.536 101.352 20022.5 0.000000 0.000000 0.000000 0.000000 + 66.6876 14.9143 20022.5 0.000000 0.000000 0.000000 0.000000 + 239.775 184.169 19979.0 0.000000 0.000000 0.000000 0.000000 + 207.392 300.690 19873.1 0.000000 0.000000 0.000000 0.000000 + 285.473 86.7142 19870.3 0.000000 0.000000 0.000000 0.000000 + 103.209 329.003 19788.0 0.000000 0.000000 0.000000 0.000000 + 144.329 5.52987 19689.5 0.000000 0.000000 0.000000 0.000000 + 163.591 48.8786 19684.6 0.000000 0.000000 0.000000 0.000000 + 41.1080 347.128 19634.7 0.000000 0.000000 0.000000 0.000000 + 265.919 176.901 19627.3 0.000000 0.000000 0.000000 0.000000 + 3.01452 70.8604 19596.9 0.000000 0.000000 0.000000 0.000000 + 45.7414 212.730 19592.9 0.000000 0.000000 0.000000 0.000000 + 245.358 361.263 19494.8 0.000000 0.000000 0.000000 0.000000 + 129.773 175.180 19375.8 0.000000 0.000000 0.000000 0.000000 + 193.050 236.955 19369.1 0.000000 0.000000 0.000000 0.000000 + 255.570 133.309 19365.4 0.000000 0.000000 0.000000 0.000000 + 349.580 288.906 19270.6 0.000000 0.000000 0.000000 0.000000 + 212.150 126.407 19262.1 0.000000 0.000000 0.000000 0.000000 + 284.457 50.2993 19110.8 0.000000 0.000000 0.000000 0.000000 + 108.445 343.947 19101.5 0.000000 0.000000 0.000000 0.000000 + 98.5429 75.6392 19093.8 0.000000 0.000000 0.000000 0.000000 + 243.557 29.4193 18852.6 0.000000 0.000000 0.000000 0.000000 + 41.2816 282.126 18554.4 0.000000 0.000000 0.000000 0.000000 + 252.426 363.544 18528.4 0.000000 0.000000 0.000000 0.000000 + 303.026 44.1925 18521.4 0.000000 0.000000 0.000000 0.000000 + 52.4907 278.827 18517.0 0.000000 0.000000 0.000000 0.000000 + 80.0980 104.970 18348.4 0.000000 0.000000 0.000000 0.000000 + 104.881 127.866 18244.3 0.000000 0.000000 0.000000 0.000000 + 44.3451 255.037 18238.5 0.000000 0.000000 0.000000 0.000000 + 131.637 122.596 18158.9 0.000000 0.000000 0.000000 0.000000 + 83.7545 59.7327 18156.9 0.000000 0.000000 0.000000 0.000000 + 80.2236 239.321 18130.3 0.000000 0.000000 0.000000 0.000000 + 317.533 47.1217 18119.6 0.000000 0.000000 0.000000 0.000000 + 145.036 140.407 17989.1 0.000000 0.000000 0.000000 0.000000 + 141.051 298.051 17982.8 0.000000 0.000000 0.000000 0.000000 + 156.464 319.885 17979.6 0.000000 0.000000 0.000000 0.000000 + 209.453 326.082 17946.1 0.000000 0.000000 0.000000 0.000000 + 307.757 229.041 17879.8 0.000000 0.000000 0.000000 0.000000 + 266.524 99.1295 17779.1 0.000000 0.000000 0.000000 0.000000 + 289.194 129.046 17714.1 0.000000 0.000000 0.000000 0.000000 + 93.9559 222.648 17696.6 0.000000 0.000000 0.000000 0.000000 + 160.844 104.638 17464.8 0.000000 0.000000 0.000000 0.000000 + 133.751 51.1189 17409.9 0.000000 0.000000 0.000000 0.000000 + 116.573 303.190 17376.9 0.000000 0.000000 0.000000 0.000000 + 87.7180 49.7890 17363.9 0.000000 0.000000 0.000000 0.000000 + 350.731 48.4579 17213.3 0.000000 0.000000 0.000000 0.000000 + 137.892 58.0751 16926.9 0.000000 0.000000 0.000000 0.000000 + 323.030 59.8720 16855.2 0.000000 0.000000 0.000000 0.000000 + 32.3589 338.198 16854.7 0.000000 0.000000 0.000000 0.000000 + 9.42043 273.939 16851.0 0.000000 0.000000 0.000000 0.000000 + 302.530 311.379 16790.2 0.000000 0.000000 0.000000 0.000000 + 48.4972 292.222 16781.3 0.000000 0.000000 0.000000 0.000000 + 283.278 14.4937 16676.5 0.000000 0.000000 0.000000 0.000000 + 46.3838 261.815 16664.5 0.000000 0.000000 0.000000 0.000000 + 88.9964 85.2222 16607.6 0.000000 0.000000 0.000000 0.000000 + 359.077 74.4421 16590.0 0.000000 0.000000 0.000000 0.000000 + 19.4328 83.3369 16540.7 0.000000 0.000000 0.000000 0.000000 + 152.302 28.6130 16520.8 0.000000 0.000000 0.000000 0.000000 + 223.018 76.2170 16514.7 0.000000 0.000000 0.000000 0.000000 + 316.030 77.9975 16510.2 0.000000 0.000000 0.000000 0.000000 + 138.917 160.170 16484.3 0.000000 0.000000 0.000000 0.000000 + 223.953 12.3861 16482.4 0.000000 0.000000 0.000000 0.000000 + 214.474 271.793 16481.6 0.000000 0.000000 0.000000 0.000000 + 78.1325 205.277 16481.3 0.000000 0.000000 0.000000 0.000000 + 235.625 52.7153 16471.8 0.000000 0.000000 0.000000 0.000000 + 354.783 139.073 16457.8 0.000000 0.000000 0.000000 0.000000 + 3.56808 198.879 16424.3 0.000000 0.000000 0.000000 0.000000 + 168.668 14.9409 16395.3 0.000000 0.000000 0.000000 0.000000 + 245.260 133.896 16380.6 0.000000 0.000000 0.000000 0.000000 + 301.882 353.278 16377.0 0.000000 0.000000 0.000000 0.000000 + 139.618 291.032 16321.1 0.000000 0.000000 0.000000 0.000000 + 165.128 297.489 16320.5 0.000000 0.000000 0.000000 0.000000 + 122.781 239.530 16265.7 0.000000 0.000000 0.000000 0.000000 + 188.373 64.3559 16263.2 0.000000 0.000000 0.000000 0.000000 + 177.580 235.027 16250.7 0.000000 0.000000 0.000000 0.000000 + 32.4557 306.842 16226.1 0.000000 0.000000 0.000000 0.000000 + 164.661 168.512 16198.5 0.000000 0.000000 0.000000 0.000000 + 262.112 211.318 16193.5 0.000000 0.000000 0.000000 0.000000 + 147.116 327.635 16180.3 0.000000 0.000000 0.000000 0.000000 + 102.228 72.9018 16147.2 0.000000 0.000000 0.000000 0.000000 + 306.166 263.748 16089.4 0.000000 0.000000 0.000000 0.000000 + 52.4548 171.662 16079.3 0.000000 0.000000 0.000000 0.000000 + 358.891 189.852 16043.8 0.000000 0.000000 0.000000 0.000000 + 253.507 229.634 16041.5 0.000000 0.000000 0.000000 0.000000 + 164.988 279.357 16041.2 0.000000 0.000000 0.000000 0.000000 + 128.062 338.965 15973.2 0.000000 0.000000 0.000000 0.000000 + 172.024 74.8392 15963.1 0.000000 0.000000 0.000000 0.000000 + 167.933 188.467 15961.1 0.000000 0.000000 0.000000 0.000000 + 350.253 289.588 15956.4 0.000000 0.000000 0.000000 0.000000 + 363.672 203.275 15932.4 0.000000 0.000000 0.000000 0.000000 + 129.272 93.8611 15904.2 0.000000 0.000000 0.000000 0.000000 + 234.879 123.706 15904.1 0.000000 0.000000 0.000000 0.000000 + 23.2646 210.800 15896.7 0.000000 0.000000 0.000000 0.000000 + 209.357 241.943 15882.7 0.000000 0.000000 0.000000 0.000000 + 180.687 117.996 15872.5 0.000000 0.000000 0.000000 0.000000 + 336.864 75.7837 15860.1 0.000000 0.000000 0.000000 0.000000 + 308.564 307.915 15824.9 0.000000 0.000000 0.000000 0.000000 + 224.627 296.561 15812.1 0.000000 0.000000 0.000000 0.000000 + 181.376 224.835 15749.0 0.000000 0.000000 0.000000 0.000000 + 138.004 248.014 15697.7 0.000000 0.000000 0.000000 0.000000 + 148.405 208.206 15648.6 0.000000 0.000000 0.000000 0.000000 + 43.0012 147.090 15624.2 0.000000 0.000000 0.000000 0.000000 + 55.4895 222.495 15589.3 0.000000 0.000000 0.000000 0.000000 + 138.836 206.598 15544.2 0.000000 0.000000 0.000000 0.000000 + 64.8731 72.5455 15533.4 0.000000 0.000000 0.000000 0.000000 + 271.611 227.078 15531.5 0.000000 0.000000 0.000000 0.000000 + 91.9145 103.725 15495.1 0.000000 0.000000 0.000000 0.000000 + 286.411 41.6308 15488.2 0.000000 0.000000 0.000000 0.000000 + 34.2647 312.923 15454.5 0.000000 0.000000 0.000000 0.000000 + 290.567 362.981 15416.5 0.000000 0.000000 0.000000 0.000000 + 360.258 354.323 15388.4 0.000000 0.000000 0.000000 0.000000 + 201.375 213.505 15383.9 0.000000 0.000000 0.000000 0.000000 + 201.298 354.240 15378.6 0.000000 0.000000 0.000000 0.000000 + 195.002 25.7019 15360.8 0.000000 0.000000 0.000000 0.000000 + 71.6831 255.232 15359.9 0.000000 0.000000 0.000000 0.000000 + 174.469 266.665 15340.3 0.000000 0.000000 0.000000 0.000000 + 158.528 317.745 15337.3 0.000000 0.000000 0.000000 0.000000 + 168.200 249.695 15314.3 0.000000 0.000000 0.000000 0.000000 + 310.647 263.492 15264.3 0.000000 0.000000 0.000000 0.000000 + 219.865 24.4243 15258.7 0.000000 0.000000 0.000000 0.000000 + 345.731 55.9484 15249.6 0.000000 0.000000 0.000000 0.000000 + 296.935 156.008 15244.2 0.000000 0.000000 0.000000 0.000000 + 63.8222 105.283 15238.0 0.000000 0.000000 0.000000 0.000000 + 291.241 239.914 15237.0 0.000000 0.000000 0.000000 0.000000 + 361.177 337.150 15235.1 0.000000 0.000000 0.000000 0.000000 + 161.663 116.349 15229.4 0.000000 0.000000 0.000000 0.000000 + 244.247 238.389 15214.8 0.000000 0.000000 0.000000 0.000000 + 194.193 163.689 15150.9 0.000000 0.000000 0.000000 0.000000 + 36.8054 104.885 15141.2 0.000000 0.000000 0.000000 0.000000 + 335.458 241.595 15082.1 0.000000 0.000000 0.000000 0.000000 + 251.383 315.676 15032.2 0.000000 0.000000 0.000000 0.000000 + 205.569 171.467 14991.4 0.000000 0.000000 0.000000 0.000000 + 334.390 332.674 14980.3 0.000000 0.000000 0.000000 0.000000 + 4.37845 242.829 14932.3 0.000000 0.000000 0.000000 0.000000 + 213.486 218.862 14926.5 0.000000 0.000000 0.000000 0.000000 + 11.6138 188.180 14915.5 0.000000 0.000000 0.000000 0.000000 + 62.7144 111.802 14906.5 0.000000 0.000000 0.000000 0.000000 + 341.116 122.965 14904.4 0.000000 0.000000 0.000000 0.000000 + 167.098 201.155 14879.8 0.000000 0.000000 0.000000 0.000000 + 119.749 258.761 14867.7 0.000000 0.000000 0.000000 0.000000 + 141.900 174.599 14859.6 0.000000 0.000000 0.000000 0.000000 + 76.6127 189.811 14852.7 0.000000 0.000000 0.000000 0.000000 + 306.080 36.0436 14843.5 0.000000 0.000000 0.000000 0.000000 + 149.521 114.137 14827.3 0.000000 0.000000 0.000000 0.000000 + 62.8410 93.3785 14818.4 0.000000 0.000000 0.000000 0.000000 + 65.2576 150.670 14817.8 0.000000 0.000000 0.000000 0.000000 + 192.994 235.970 14812.7 0.000000 0.000000 0.000000 0.000000 + 24.7471 67.3128 14800.1 0.000000 0.000000 0.000000 0.000000 + 352.101 307.515 14763.7 0.000000 0.000000 0.000000 0.000000 + 246.477 140.334 14759.9 0.000000 0.000000 0.000000 0.000000 + 192.727 123.419 14728.5 0.000000 0.000000 0.000000 0.000000 + 31.2353 69.5522 14725.5 0.000000 0.000000 0.000000 0.000000 + 106.922 61.6365 14697.9 0.000000 0.000000 0.000000 0.000000 + 123.972 120.534 14677.2 0.000000 0.000000 0.000000 0.000000 + 347.941 234.537 14637.1 0.000000 0.000000 0.000000 0.000000 + 3.60083 167.327 14623.2 0.000000 0.000000 0.000000 0.000000 + 16.6035 61.7953 14620.6 0.000000 0.000000 0.000000 0.000000 + 321.528 121.160 14609.6 0.000000 0.000000 0.000000 0.000000 + 130.912 105.016 14597.9 0.000000 0.000000 0.000000 0.000000 + 65.3000 39.6274 14567.7 0.000000 0.000000 0.000000 0.000000 + 297.005 220.434 14561.1 0.000000 0.000000 0.000000 0.000000 + 18.1205 82.5609 14554.5 0.000000 0.000000 0.000000 0.000000 + 69.7734 245.039 14532.6 0.000000 0.000000 0.000000 0.000000 + 204.297 199.394 14505.1 0.000000 0.000000 0.000000 0.000000 + 94.8972 14.2587 14481.6 0.000000 0.000000 0.000000 0.000000 + 63.3900 138.389 14448.9 0.000000 0.000000 0.000000 0.000000 + 234.641 358.762 14448.7 0.000000 0.000000 0.000000 0.000000 + 358.741 327.191 14407.8 0.000000 0.000000 0.000000 0.000000 + 349.905 62.7117 14396.1 0.000000 0.000000 0.000000 0.000000 + 22.7864 87.4560 14391.4 0.000000 0.000000 0.000000 0.000000 + 230.120 339.082 14390.1 0.000000 0.000000 0.000000 0.000000 + 108.111 321.511 14373.6 0.000000 0.000000 0.000000 0.000000 + 317.026 276.578 14356.8 0.000000 0.000000 0.000000 0.000000 + 272.134 337.203 14335.5 0.000000 0.000000 0.000000 0.000000 + 180.295 97.3230 14329.0 0.000000 0.000000 0.000000 0.000000 + 281.363 326.638 14265.0 0.000000 0.000000 0.000000 0.000000 + 227.063 311.070 14255.6 0.000000 0.000000 0.000000 0.000000 + 183.418 258.732 14254.3 0.000000 0.000000 0.000000 0.000000 + 56.3467 286.487 14240.1 0.000000 0.000000 0.000000 0.000000 + 283.491 28.8608 14220.0 0.000000 0.000000 0.000000 0.000000 + 199.402 279.639 14204.6 0.000000 0.000000 0.000000 0.000000 + 344.018 302.872 14203.2 0.000000 0.000000 0.000000 0.000000 + 362.073 85.5142 14187.5 0.000000 0.000000 0.000000 0.000000 + 63.8054 71.4535 14186.8 0.000000 0.000000 0.000000 0.000000 + 21.7488 355.081 14132.3 0.000000 0.000000 0.000000 0.000000 + 191.489 321.967 14125.4 0.000000 0.000000 0.000000 0.000000 + 201.271 147.150 14124.7 0.000000 0.000000 0.000000 0.000000 + 251.122 111.121 14123.2 0.000000 0.000000 0.000000 0.000000 + 222.080 273.185 14113.5 0.000000 0.000000 0.000000 0.000000 + 331.818 105.101 14108.4 0.000000 0.000000 0.000000 0.000000 + 88.1360 294.219 14081.4 0.000000 0.000000 0.000000 0.000000 + 200.960 198.588 14062.9 0.000000 0.000000 0.000000 0.000000 + 251.984 283.924 14059.0 0.000000 0.000000 0.000000 0.000000 + 138.533 353.062 14052.4 0.000000 0.000000 0.000000 0.000000 + 256.849 328.732 14044.8 0.000000 0.000000 0.000000 0.000000 + 111.828 241.685 14041.4 0.000000 0.000000 0.000000 0.000000 + 126.877 90.7269 14037.3 0.000000 0.000000 0.000000 0.000000 + 243.825 42.6837 14036.3 0.000000 0.000000 0.000000 0.000000 + 347.499 78.8332 14031.4 0.000000 0.000000 0.000000 0.000000 + 29.3669 256.523 14025.3 0.000000 0.000000 0.000000 0.000000 + 199.751 150.531 13989.7 0.000000 0.000000 0.000000 0.000000 + 150.995 272.232 13966.1 0.000000 0.000000 0.000000 0.000000 + 203.903 142.845 13905.4 0.000000 0.000000 0.000000 0.000000 + 166.491 62.0418 13870.6 0.000000 0.000000 0.000000 0.000000 + 176.796 328.800 13868.2 0.000000 0.000000 0.000000 0.000000 + 170.432 98.9300 13842.3 0.000000 0.000000 0.000000 0.000000 + 130.904 139.211 13839.8 0.000000 0.000000 0.000000 0.000000 + 144.127 50.9461 13831.6 0.000000 0.000000 0.000000 0.000000 + 72.1932 342.729 13821.6 0.000000 0.000000 0.000000 0.000000 + 61.3450 361.764 13801.0 0.000000 0.000000 0.000000 0.000000 + 140.029 161.223 13781.2 0.000000 0.000000 0.000000 0.000000 + 236.984 14.6743 13755.7 0.000000 0.000000 0.000000 0.000000 + 105.077 335.650 13703.9 0.000000 0.000000 0.000000 0.000000 + 325.066 194.209 13689.6 0.000000 0.000000 0.000000 0.000000 + 29.3006 215.859 13669.1 0.000000 0.000000 0.000000 0.000000 + 32.8177 62.4512 13665.8 0.000000 0.000000 0.000000 0.000000 + 266.401 99.9594 13658.5 0.000000 0.000000 0.000000 0.000000 + 211.166 312.198 13620.0 0.000000 0.000000 0.000000 0.000000 + 93.5192 105.553 13600.6 0.000000 0.000000 0.000000 0.000000 + 45.2959 81.0646 13587.3 0.000000 0.000000 0.000000 0.000000 + 288.607 351.075 13583.5 0.000000 0.000000 0.000000 0.000000 + 181.065 54.7112 13570.8 0.000000 0.000000 0.000000 0.000000 + 147.589 201.483 13567.4 0.000000 0.000000 0.000000 0.000000 + 202.244 199.902 13561.9 0.000000 0.000000 0.000000 0.000000 + 262.522 178.621 13515.2 0.000000 0.000000 0.000000 0.000000 + 248.962 152.616 13502.7 0.000000 0.000000 0.000000 0.000000 + 352.888 357.563 13500.1 0.000000 0.000000 0.000000 0.000000 + 213.718 302.877 13470.8 0.000000 0.000000 0.000000 0.000000 + 149.837 113.488 13460.0 0.000000 0.000000 0.000000 0.000000 + 189.020 138.238 13445.2 0.000000 0.000000 0.000000 0.000000 + 249.078 108.727 13444.3 0.000000 0.000000 0.000000 0.000000 + 294.594 208.276 13404.0 0.000000 0.000000 0.000000 0.000000 + 75.3453 333.563 13380.6 0.000000 0.000000 0.000000 0.000000 + 92.1763 151.808 13355.2 0.000000 0.000000 0.000000 0.000000 + 130.025 62.1811 13343.6 0.000000 0.000000 0.000000 0.000000 + 363.235 123.166 13310.2 0.000000 0.000000 0.000000 0.000000 + 7.02267 98.4354 13292.4 0.000000 0.000000 0.000000 0.000000 + 300.030 32.7445 13276.7 0.000000 0.000000 0.000000 0.000000 + 142.064 18.6700 13266.9 0.000000 0.000000 0.000000 0.000000 + 206.554 35.7591 13243.2 0.000000 0.000000 0.000000 0.000000 + 154.639 316.345 13235.1 0.000000 0.000000 0.000000 0.000000 + 189.662 60.7627 13227.4 0.000000 0.000000 0.000000 0.000000 + 10.8179 250.801 13220.2 0.000000 0.000000 0.000000 0.000000 + 174.230 175.471 13207.5 0.000000 0.000000 0.000000 0.000000 + 296.718 280.390 13149.6 0.000000 0.000000 0.000000 0.000000 + 306.943 244.012 13145.3 0.000000 0.000000 0.000000 0.000000 + 170.516 49.9658 13140.1 0.000000 0.000000 0.000000 0.000000 + 287.901 24.3702 13130.4 0.000000 0.000000 0.000000 0.000000 + 337.564 277.880 13118.5 0.000000 0.000000 0.000000 0.000000 + 77.8025 240.561 13113.7 0.000000 0.000000 0.000000 0.000000 + 238.540 185.863 13109.5 0.000000 0.000000 0.000000 0.000000 + 214.278 252.369 13075.2 0.000000 0.000000 0.000000 0.000000 + 106.459 289.293 13067.1 0.000000 0.000000 0.000000 0.000000 + 78.3597 109.804 13064.1 0.000000 0.000000 0.000000 0.000000 + 205.993 356.052 13062.6 0.000000 0.000000 0.000000 0.000000 + 359.209 334.555 13027.5 0.000000 0.000000 0.000000 0.000000 + 331.500 146.132 13022.4 0.000000 0.000000 0.000000 0.000000 + 354.290 97.9034 13014.3 0.000000 0.000000 0.000000 0.000000 + 171.987 4.87055 13012.6 0.000000 0.000000 0.000000 0.000000 + 185.807 324.935 12929.9 0.000000 0.000000 0.000000 0.000000 + 232.712 336.225 12910.3 0.000000 0.000000 0.000000 0.000000 + 236.602 287.362 12874.8 0.000000 0.000000 0.000000 0.000000 + 46.3189 10.3655 12847.5 0.000000 0.000000 0.000000 0.000000 + 357.498 154.141 12795.4 0.000000 0.000000 0.000000 0.000000 + 304.459 344.493 12771.7 0.000000 0.000000 0.000000 0.000000 + 34.3393 248.134 12766.2 0.000000 0.000000 0.000000 0.000000 + 349.093 18.0188 12753.9 0.000000 0.000000 0.000000 0.000000 + 327.095 68.1273 12752.3 0.000000 0.000000 0.000000 0.000000 + 139.162 283.033 12739.8 0.000000 0.000000 0.000000 0.000000 + 36.9341 344.077 12716.8 0.000000 0.000000 0.000000 0.000000 + 133.703 218.410 12697.8 0.000000 0.000000 0.000000 0.000000 + 214.902 274.618 12679.1 0.000000 0.000000 0.000000 0.000000 + 316.714 300.312 12660.2 0.000000 0.000000 0.000000 0.000000 + 298.096 264.712 12645.9 0.000000 0.000000 0.000000 0.000000 + 152.851 363.386 12645.7 0.000000 0.000000 0.000000 0.000000 + 306.643 168.527 12587.3 0.000000 0.000000 0.000000 0.000000 + 216.967 327.614 12585.2 0.000000 0.000000 0.000000 0.000000 + 284.660 351.667 12571.5 0.000000 0.000000 0.000000 0.000000 + 42.3146 165.977 12543.2 0.000000 0.000000 0.000000 0.000000 + 158.357 220.813 12527.9 0.000000 0.000000 0.000000 0.000000 + 153.702 257.090 12492.0 0.000000 0.000000 0.000000 0.000000 + 313.976 153.479 12473.8 0.000000 0.000000 0.000000 0.000000 + 148.738 252.439 12422.9 0.000000 0.000000 0.000000 0.000000 + 326.491 270.312 12413.9 0.000000 0.000000 0.000000 0.000000 + 156.401 267.468 12409.7 0.000000 0.000000 0.000000 0.000000 + 297.961 269.814 12406.8 0.000000 0.000000 0.000000 0.000000 + 121.527 229.707 12373.6 0.000000 0.000000 0.000000 0.000000 + 55.7035 85.5249 12286.2 0.000000 0.000000 0.000000 0.000000 + 348.916 88.4967 12284.8 0.000000 0.000000 0.000000 0.000000 + 96.4374 231.709 12284.0 0.000000 0.000000 0.000000 0.000000 + 300.548 154.692 12256.9 0.000000 0.000000 0.000000 0.000000 + 160.314 13.0360 12218.8 0.000000 0.000000 0.000000 0.000000 + 219.159 138.652 12211.3 0.000000 0.000000 0.000000 0.000000 + 248.748 76.2700 12198.8 0.000000 0.000000 0.000000 0.000000 + 20.4236 234.168 12198.2 0.000000 0.000000 0.000000 0.000000 + 31.4977 286.610 12175.5 0.000000 0.000000 0.000000 0.000000 + 243.974 278.342 12151.7 0.000000 0.000000 0.000000 0.000000 + 103.561 197.281 12130.3 0.000000 0.000000 0.000000 0.000000 + 152.034 122.094 12102.1 0.000000 0.000000 0.000000 0.000000 + 90.5346 9.32374 12085.4 0.000000 0.000000 0.000000 0.000000 + 305.691 194.666 12066.6 0.000000 0.000000 0.000000 0.000000 + 256.903 261.281 12047.2 0.000000 0.000000 0.000000 0.000000 + 311.221 81.2428 12035.9 0.000000 0.000000 0.000000 0.000000 + 273.788 297.322 12023.1 0.000000 0.000000 0.000000 0.000000 + 129.700 76.6173 12020.7 0.000000 0.000000 0.000000 0.000000 + 199.615 317.206 12019.1 0.000000 0.000000 0.000000 0.000000 + 115.028 92.0918 12006.0 0.000000 0.000000 0.000000 0.000000 + 287.003 147.767 11966.7 0.000000 0.000000 0.000000 0.000000 + 275.602 343.999 11957.1 0.000000 0.000000 0.000000 0.000000 + 100.723 327.139 11941.4 0.000000 0.000000 0.000000 0.000000 + 266.488 79.2946 11939.1 0.000000 0.000000 0.000000 0.000000 + 353.888 330.321 11889.3 0.000000 0.000000 0.000000 0.000000 + 210.325 49.9941 11853.8 0.000000 0.000000 0.000000 0.000000 + 114.017 57.2840 11840.9 0.000000 0.000000 0.000000 0.000000 + 302.389 179.064 11832.5 0.000000 0.000000 0.000000 0.000000 + 172.789 130.951 11802.4 0.000000 0.000000 0.000000 0.000000 + 254.915 299.850 11800.5 0.000000 0.000000 0.000000 0.000000 + 102.401 13.9669 11772.8 0.000000 0.000000 0.000000 0.000000 + 58.9761 281.184 11749.5 0.000000 0.000000 0.000000 0.000000 + 128.119 61.4088 11736.1 0.000000 0.000000 0.000000 0.000000 + 352.178 208.087 11697.3 0.000000 0.000000 0.000000 0.000000 + 162.070 152.026 11697.0 0.000000 0.000000 0.000000 0.000000 + 7.49861 251.724 11671.1 0.000000 0.000000 0.000000 0.000000 + 168.642 207.709 11644.3 0.000000 0.000000 0.000000 0.000000 + 136.068 121.350 11621.5 0.000000 0.000000 0.000000 0.000000 + 320.275 213.144 11586.3 0.000000 0.000000 0.000000 0.000000 + 227.644 62.5805 11585.6 0.000000 0.000000 0.000000 0.000000 + 78.8221 27.6432 11573.0 0.000000 0.000000 0.000000 0.000000 + 214.251 127.613 11568.7 0.000000 0.000000 0.000000 0.000000 + 163.459 15.5157 11561.8 0.000000 0.000000 0.000000 0.000000 + 61.6744 331.132 11556.6 0.000000 0.000000 0.000000 0.000000 + 264.949 294.568 11520.6 0.000000 0.000000 0.000000 0.000000 + 265.972 107.224 11509.7 0.000000 0.000000 0.000000 0.000000 + 93.5120 254.407 11477.7 0.000000 0.000000 0.000000 0.000000 + 125.181 56.6061 11467.5 0.000000 0.000000 0.000000 0.000000 + 53.8462 190.002 11451.9 0.000000 0.000000 0.000000 0.000000 + 186.376 251.675 11431.4 0.000000 0.000000 0.000000 0.000000 + 64.9942 344.878 11417.0 0.000000 0.000000 0.000000 0.000000 + 123.082 235.587 11390.1 0.000000 0.000000 0.000000 0.000000 + 150.701 210.810 11357.8 0.000000 0.000000 0.000000 0.000000 + 253.085 184.330 11351.2 0.000000 0.000000 0.000000 0.000000 + 253.276 258.162 11328.3 0.000000 0.000000 0.000000 0.000000 + 276.483 268.743 11313.8 0.000000 0.000000 0.000000 0.000000 + 189.279 187.865 11305.3 0.000000 0.000000 0.000000 0.000000 + 215.193 201.435 11265.0 0.000000 0.000000 0.000000 0.000000 + 69.5529 153.028 11248.0 0.000000 0.000000 0.000000 0.000000 + 44.8571 141.318 11246.3 0.000000 0.000000 0.000000 0.000000 + 255.785 257.613 11224.9 0.000000 0.000000 0.000000 0.000000 + 247.608 88.8264 11202.4 0.000000 0.000000 0.000000 0.000000 + 156.177 67.7048 11186.5 0.000000 0.000000 0.000000 0.000000 + 13.5149 239.654 11168.9 0.000000 0.000000 0.000000 0.000000 + 187.392 112.795 11138.0 0.000000 0.000000 0.000000 0.000000 + 14.8975 47.6641 11105.0 0.000000 0.000000 0.000000 0.000000 + 70.7366 188.308 11103.1 0.000000 0.000000 0.000000 0.000000 + 271.378 230.490 11090.4 0.000000 0.000000 0.000000 0.000000 + 168.597 53.0663 11074.7 0.000000 0.000000 0.000000 0.000000 + 285.365 155.739 11057.2 0.000000 0.000000 0.000000 0.000000 + 263.262 200.536 11031.6 0.000000 0.000000 0.000000 0.000000 + 290.855 229.283 11019.5 0.000000 0.000000 0.000000 0.000000 + 21.7265 11.0327 11001.5 0.000000 0.000000 0.000000 0.000000 + 211.312 193.496 10977.8 0.000000 0.000000 0.000000 0.000000 + 354.942 344.677 10969.7 0.000000 0.000000 0.000000 0.000000 + 179.984 250.949 10963.5 0.000000 0.000000 0.000000 0.000000 + 89.9879 102.486 10933.5 0.000000 0.000000 0.000000 0.000000 + 13.4885 49.8407 10925.8 0.000000 0.000000 0.000000 0.000000 + 293.170 296.958 10915.3 0.000000 0.000000 0.000000 0.000000 + 268.678 275.276 10914.3 0.000000 0.000000 0.000000 0.000000 + 11.4741 236.882 10905.5 0.000000 0.000000 0.000000 0.000000 + 216.674 117.140 10900.9 0.000000 0.000000 0.000000 0.000000 + 118.744 317.341 10900.8 0.000000 0.000000 0.000000 0.000000 + 93.5018 139.083 10898.7 0.000000 0.000000 0.000000 0.000000 + 17.3496 250.851 10896.2 0.000000 0.000000 0.000000 0.000000 + 192.690 4.16211 10893.5 0.000000 0.000000 0.000000 0.000000 + 28.9853 267.572 10889.8 0.000000 0.000000 0.000000 0.000000 + 225.422 299.069 10885.0 0.000000 0.000000 0.000000 0.000000 + 11.2046 101.703 10845.9 0.000000 0.000000 0.000000 0.000000 + 299.357 82.8579 10814.0 0.000000 0.000000 0.000000 0.000000 + 338.334 302.286 10798.6 0.000000 0.000000 0.000000 0.000000 + 288.247 291.015 10777.2 0.000000 0.000000 0.000000 0.000000 + 25.2908 249.434 10775.6 0.000000 0.000000 0.000000 0.000000 + 357.118 208.553 10770.1 0.000000 0.000000 0.000000 0.000000 + 107.145 312.038 10762.6 0.000000 0.000000 0.000000 0.000000 + 88.8668 141.845 10754.3 0.000000 0.000000 0.000000 0.000000 + 340.480 270.378 10745.8 0.000000 0.000000 0.000000 0.000000 + 322.660 238.161 10706.5 0.000000 0.000000 0.000000 0.000000 + 261.030 125.355 10705.9 0.000000 0.000000 0.000000 0.000000 + 5.36683 177.624 10684.8 0.000000 0.000000 0.000000 0.000000 + 84.5028 253.255 10636.6 0.000000 0.000000 0.000000 0.000000 + 69.3425 316.810 10602.1 0.000000 0.000000 0.000000 0.000000 + 214.932 127.791 10571.5 0.000000 0.000000 0.000000 0.000000 + 53.2043 143.702 10536.8 0.000000 0.000000 0.000000 0.000000 + 127.327 129.746 10529.5 0.000000 0.000000 0.000000 0.000000 + 199.281 325.606 10518.3 0.000000 0.000000 0.000000 0.000000 + 91.0923 32.7281 10478.6 0.000000 0.000000 0.000000 0.000000 + 290.000 104.865 10455.9 0.000000 0.000000 0.000000 0.000000 + 110.150 39.1507 10432.3 0.000000 0.000000 0.000000 0.000000 + 73.3965 209.015 10425.8 0.000000 0.000000 0.000000 0.000000 + 334.666 82.5704 10415.4 0.000000 0.000000 0.000000 0.000000 + 119.569 103.891 10405.2 0.000000 0.000000 0.000000 0.000000 + 5.51266 129.989 10404.5 0.000000 0.000000 0.000000 0.000000 + 352.328 322.056 10373.6 0.000000 0.000000 0.000000 0.000000 + 118.789 357.245 10365.4 0.000000 0.000000 0.000000 0.000000 + 189.231 338.293 10314.9 0.000000 0.000000 0.000000 0.000000 + 146.456 199.104 10311.6 0.000000 0.000000 0.000000 0.000000 + 275.953 350.132 10297.5 0.000000 0.000000 0.000000 0.000000 + 211.655 119.536 10295.0 0.000000 0.000000 0.000000 0.000000 + 107.430 252.205 10264.6 0.000000 0.000000 0.000000 0.000000 + 298.279 95.8443 10245.1 0.000000 0.000000 0.000000 0.000000 + 73.7256 317.953 10223.7 0.000000 0.000000 0.000000 0.000000 + 241.150 105.953 10203.3 0.000000 0.000000 0.000000 0.000000 + 289.573 235.203 10180.7 0.000000 0.000000 0.000000 0.000000 + 177.092 133.147 10170.5 0.000000 0.000000 0.000000 0.000000 + 66.4591 33.4103 10161.2 0.000000 0.000000 0.000000 0.000000 + 167.121 221.529 10147.7 0.000000 0.000000 0.000000 0.000000 + 150.614 217.315 10116.1 0.000000 0.000000 0.000000 0.000000 + 349.168 220.736 10114.9 0.000000 0.000000 0.000000 0.000000 + 189.201 327.360 10105.4 0.000000 0.000000 0.000000 0.000000 + 356.941 292.278 10071.5 0.000000 0.000000 0.000000 0.000000 + 159.444 350.615 10066.7 0.000000 0.000000 0.000000 0.000000 + 153.628 37.9686 10030.9 0.000000 0.000000 0.000000 0.000000 + 224.064 40.6913 10016.4 0.000000 0.000000 0.000000 0.000000 + 119.818 42.4017 10012.7 0.000000 0.000000 0.000000 0.000000 + 121.470 215.858 10010.6 0.000000 0.000000 0.000000 0.000000 + 184.875 289.720 9994.10 0.000000 0.000000 0.000000 0.000000 + 46.6822 362.316 9969.35 0.000000 0.000000 0.000000 0.000000 + 286.563 89.0843 9927.89 0.000000 0.000000 0.000000 0.000000 + 147.549 316.635 9833.91 0.000000 0.000000 0.000000 0.000000 + 140.216 239.690 9810.08 0.000000 0.000000 0.000000 0.000000 + 218.380 212.227 9791.60 0.000000 0.000000 0.000000 0.000000 + 185.957 95.6876 9773.53 0.000000 0.000000 0.000000 0.000000 + 250.300 140.294 9763.96 0.000000 0.000000 0.000000 0.000000 + 211.091 310.549 9754.13 0.000000 0.000000 0.000000 0.000000 + 300.558 138.689 9746.11 0.000000 0.000000 0.000000 0.000000 + 257.092 215.141 9744.90 0.000000 0.000000 0.000000 0.000000 + 246.171 47.4282 9728.41 0.000000 0.000000 0.000000 0.000000 + 102.151 37.6587 9726.53 0.000000 0.000000 0.000000 0.000000 + 234.644 320.442 9706.42 0.000000 0.000000 0.000000 0.000000 + 209.105 208.744 9681.31 0.000000 0.000000 0.000000 0.000000 + 19.1231 99.7509 9678.07 0.000000 0.000000 0.000000 0.000000 + 38.0761 219.821 9671.93 0.000000 0.000000 0.000000 0.000000 + 30.9910 211.942 9605.70 0.000000 0.000000 0.000000 0.000000 + 46.4823 149.956 9603.90 0.000000 0.000000 0.000000 0.000000 + 98.9956 300.200 9558.20 0.000000 0.000000 0.000000 0.000000 + 189.435 348.322 9497.54 0.000000 0.000000 0.000000 0.000000 + 229.480 91.6102 9487.79 0.000000 0.000000 0.000000 0.000000 + 218.761 291.812 9487.13 0.000000 0.000000 0.000000 0.000000 + 141.428 284.746 9474.41 0.000000 0.000000 0.000000 0.000000 + 26.3322 214.819 9446.11 0.000000 0.000000 0.000000 0.000000 + 20.0580 339.197 9418.89 0.000000 0.000000 0.000000 0.000000 + 98.2980 240.850 9417.25 0.000000 0.000000 0.000000 0.000000 + 54.2404 295.567 9409.39 0.000000 0.000000 0.000000 0.000000 + 306.913 202.034 9403.31 0.000000 0.000000 0.000000 0.000000 + 83.5711 209.601 9402.86 0.000000 0.000000 0.000000 0.000000 + 49.9428 186.438 9373.70 0.000000 0.000000 0.000000 0.000000 + 141.370 45.4081 9334.23 0.000000 0.000000 0.000000 0.000000 + 187.359 251.220 9330.72 0.000000 0.000000 0.000000 0.000000 + 256.338 255.339 9330.37 0.000000 0.000000 0.000000 0.000000 + 47.4843 242.818 9320.21 0.000000 0.000000 0.000000 0.000000 + 299.883 105.716 9308.30 0.000000 0.000000 0.000000 0.000000 + 347.525 73.1966 9299.82 0.000000 0.000000 0.000000 0.000000 + 127.778 51.4150 9285.16 0.000000 0.000000 0.000000 0.000000 + 243.152 87.5172 9252.12 0.000000 0.000000 0.000000 0.000000 + 18.7177 289.625 9248.86 0.000000 0.000000 0.000000 0.000000 + 111.025 107.977 9247.44 0.000000 0.000000 0.000000 0.000000 + 48.9049 321.713 9229.70 0.000000 0.000000 0.000000 0.000000 + 49.1277 22.6012 9189.48 0.000000 0.000000 0.000000 0.000000 + 7.74438 341.231 9186.96 0.000000 0.000000 0.000000 0.000000 + 114.374 319.487 9181.88 0.000000 0.000000 0.000000 0.000000 + 356.844 58.7305 9181.26 0.000000 0.000000 0.000000 0.000000 + 197.970 42.8118 9167.81 0.000000 0.000000 0.000000 0.000000 + 308.839 51.4067 9159.38 0.000000 0.000000 0.000000 0.000000 + 29.2213 234.074 9158.70 0.000000 0.000000 0.000000 0.000000 + 241.482 85.2010 9157.08 0.000000 0.000000 0.000000 0.000000 + 251.966 16.5346 9070.71 0.000000 0.000000 0.000000 0.000000 + 61.7394 162.858 9055.08 0.000000 0.000000 0.000000 0.000000 + 135.239 315.640 9036.42 0.000000 0.000000 0.000000 0.000000 + 66.3842 282.122 9028.25 0.000000 0.000000 0.000000 0.000000 + 84.6350 140.990 9007.93 0.000000 0.000000 0.000000 0.000000 + 222.329 342.022 9006.42 0.000000 0.000000 0.000000 0.000000 + 42.9745 341.749 8977.87 0.000000 0.000000 0.000000 0.000000 + 197.206 182.702 8977.56 0.000000 0.000000 0.000000 0.000000 + 69.5008 239.540 8968.93 0.000000 0.000000 0.000000 0.000000 + 72.0435 75.5923 8967.49 0.000000 0.000000 0.000000 0.000000 + 136.074 185.496 8946.40 0.000000 0.000000 0.000000 0.000000 + 6.45282 119.214 8940.80 0.000000 0.000000 0.000000 0.000000 + 264.851 274.553 8934.76 0.000000 0.000000 0.000000 0.000000 + 126.041 181.148 8927.69 0.000000 0.000000 0.000000 0.000000 + 235.483 54.1305 8853.71 0.000000 0.000000 0.000000 0.000000 + 113.735 351.481 8850.05 0.000000 0.000000 0.000000 0.000000 + 182.225 58.8718 8844.37 0.000000 0.000000 0.000000 0.000000 + 281.007 213.040 8785.75 0.000000 0.000000 0.000000 0.000000 + 280.136 298.103 8785.29 0.000000 0.000000 0.000000 0.000000 + 154.780 212.224 8738.26 0.000000 0.000000 0.000000 0.000000 + 362.577 358.870 8687.21 0.000000 0.000000 0.000000 0.000000 + 79.9677 223.692 8684.84 0.000000 0.000000 0.000000 0.000000 + 281.464 343.100 8642.57 0.000000 0.000000 0.000000 0.000000 + 227.974 93.5333 8642.12 0.000000 0.000000 0.000000 0.000000 + 225.162 179.988 8603.52 0.000000 0.000000 0.000000 0.000000 + 363.955 187.390 8573.78 0.000000 0.000000 0.000000 0.000000 + 304.473 145.395 8560.22 0.000000 0.000000 0.000000 0.000000 + 329.204 340.733 8548.99 0.000000 0.000000 0.000000 0.000000 + 3.46966 252.491 8542.90 0.000000 0.000000 0.000000 0.000000 + 350.219 184.647 8540.27 0.000000 0.000000 0.000000 0.000000 + 331.809 108.234 8527.97 0.000000 0.000000 0.000000 0.000000 + 3.84361 36.1200 8526.37 0.000000 0.000000 0.000000 0.000000 + 113.516 189.622 8513.15 0.000000 0.000000 0.000000 0.000000 + 135.314 328.291 8510.13 0.000000 0.000000 0.000000 0.000000 + 207.372 110.136 8501.20 0.000000 0.000000 0.000000 0.000000 + 120.626 11.9419 8493.59 0.000000 0.000000 0.000000 0.000000 + 39.2559 283.713 8484.60 0.000000 0.000000 0.000000 0.000000 + 20.0342 213.709 8476.63 0.000000 0.000000 0.000000 0.000000 + 281.907 63.6222 8470.19 0.000000 0.000000 0.000000 0.000000 + 271.525 171.649 8465.58 0.000000 0.000000 0.000000 0.000000 + 260.969 349.726 8421.75 0.000000 0.000000 0.000000 0.000000 + 244.879 352.469 8410.74 0.000000 0.000000 0.000000 0.000000 + 231.957 163.764 8396.27 0.000000 0.000000 0.000000 0.000000 + 57.4121 69.9200 8393.76 0.000000 0.000000 0.000000 0.000000 + 339.282 345.389 8380.26 0.000000 0.000000 0.000000 0.000000 + 58.7499 176.551 8376.42 0.000000 0.000000 0.000000 0.000000 + 296.630 179.301 8372.55 0.000000 0.000000 0.000000 0.000000 + 20.4299 219.358 8363.85 0.000000 0.000000 0.000000 0.000000 + 24.8150 354.064 8342.90 0.000000 0.000000 0.000000 0.000000 + 98.5372 185.151 8342.33 0.000000 0.000000 0.000000 0.000000 + 333.657 45.6321 8330.16 0.000000 0.000000 0.000000 0.000000 + 224.090 156.503 8324.05 0.000000 0.000000 0.000000 0.000000 + 99.2796 293.111 8322.83 0.000000 0.000000 0.000000 0.000000 + 85.1664 140.326 8302.26 0.000000 0.000000 0.000000 0.000000 + 212.479 238.727 8295.13 0.000000 0.000000 0.000000 0.000000 + 349.170 28.4832 8288.76 0.000000 0.000000 0.000000 0.000000 + 212.064 100.364 8282.41 0.000000 0.000000 0.000000 0.000000 + 116.647 297.652 8244.60 0.000000 0.000000 0.000000 0.000000 + 140.094 252.549 8239.89 0.000000 0.000000 0.000000 0.000000 + 3.73455 218.219 8234.69 0.000000 0.000000 0.000000 0.000000 + 41.0175 236.134 8216.58 0.000000 0.000000 0.000000 0.000000 + 130.661 14.6804 8215.85 0.000000 0.000000 0.000000 0.000000 + 157.922 249.381 8207.19 0.000000 0.000000 0.000000 0.000000 + 361.029 103.031 8201.34 0.000000 0.000000 0.000000 0.000000 + 25.1080 304.053 8196.51 0.000000 0.000000 0.000000 0.000000 + 251.132 226.503 8184.33 0.000000 0.000000 0.000000 0.000000 + 262.140 262.829 8179.95 0.000000 0.000000 0.000000 0.000000 + 112.822 234.655 8176.15 0.000000 0.000000 0.000000 0.000000 + 362.972 130.027 8170.37 0.000000 0.000000 0.000000 0.000000 + 286.230 214.833 8117.50 0.000000 0.000000 0.000000 0.000000 + 347.297 45.7254 8096.95 0.000000 0.000000 0.000000 0.000000 + 89.8166 132.465 8078.18 0.000000 0.000000 0.000000 0.000000 + 188.795 172.561 8077.90 0.000000 0.000000 0.000000 0.000000 + 96.6791 135.688 8063.67 0.000000 0.000000 0.000000 0.000000 + 317.930 159.719 8055.87 0.000000 0.000000 0.000000 0.000000 + 333.854 148.828 8055.59 0.000000 0.000000 0.000000 0.000000 + 189.200 301.279 8055.36 0.000000 0.000000 0.000000 0.000000 + 151.986 311.486 8039.44 0.000000 0.000000 0.000000 0.000000 + 317.173 97.2851 8026.74 0.000000 0.000000 0.000000 0.000000 + 223.548 211.624 8024.90 0.000000 0.000000 0.000000 0.000000 + 9.04450 340.864 8020.38 0.000000 0.000000 0.000000 0.000000 + 349.144 213.470 8005.95 0.000000 0.000000 0.000000 0.000000 + 109.896 313.486 8000.23 0.000000 0.000000 0.000000 0.000000 + 350.389 211.508 7988.00 0.000000 0.000000 0.000000 0.000000 + 206.645 123.713 7979.93 0.000000 0.000000 0.000000 0.000000 + 266.411 347.247 7978.76 0.000000 0.000000 0.000000 0.000000 + 80.1159 330.520 7976.54 0.000000 0.000000 0.000000 0.000000 + 214.298 176.375 7969.72 0.000000 0.000000 0.000000 0.000000 + 13.5541 204.100 7969.38 0.000000 0.000000 0.000000 0.000000 + 328.562 10.8260 7964.38 0.000000 0.000000 0.000000 0.000000 + 84.1033 121.227 7954.34 0.000000 0.000000 0.000000 0.000000 + 44.5995 201.531 7926.02 0.000000 0.000000 0.000000 0.000000 + 39.7059 73.2695 7919.54 0.000000 0.000000 0.000000 0.000000 + 353.915 95.5096 7878.12 0.000000 0.000000 0.000000 0.000000 + 257.584 7.33578 7860.87 0.000000 0.000000 0.000000 0.000000 + 69.1357 135.115 7856.97 0.000000 0.000000 0.000000 0.000000 + 189.035 357.202 7854.36 0.000000 0.000000 0.000000 0.000000 + 176.125 133.910 7851.27 0.000000 0.000000 0.000000 0.000000 + 86.8012 128.032 7816.09 0.000000 0.000000 0.000000 0.000000 + 281.446 330.991 7811.29 0.000000 0.000000 0.000000 0.000000 + 178.094 182.133 7805.46 0.000000 0.000000 0.000000 0.000000 + 303.274 229.602 7781.67 0.000000 0.000000 0.000000 0.000000 + 127.928 163.923 7739.83 0.000000 0.000000 0.000000 0.000000 + 193.399 224.266 7730.48 0.000000 0.000000 0.000000 0.000000 + 345.658 223.740 7708.36 0.000000 0.000000 0.000000 0.000000 + 184.026 65.1665 7692.67 0.000000 0.000000 0.000000 0.000000 + 105.698 64.8980 7649.95 0.000000 0.000000 0.000000 0.000000 + 130.572 14.4292 7648.34 0.000000 0.000000 0.000000 0.000000 + 315.749 17.0102 7646.99 0.000000 0.000000 0.000000 0.000000 + 224.948 199.281 7644.45 0.000000 0.000000 0.000000 0.000000 + 3.71182 140.943 7637.17 0.000000 0.000000 0.000000 0.000000 + 297.735 341.078 7624.77 0.000000 0.000000 0.000000 0.000000 + 184.381 266.513 7612.73 0.000000 0.000000 0.000000 0.000000 + 74.0777 76.6774 7610.14 0.000000 0.000000 0.000000 0.000000 + 229.994 327.230 7607.33 0.000000 0.000000 0.000000 0.000000 + 300.324 41.7404 7592.34 0.000000 0.000000 0.000000 0.000000 + 86.5110 250.161 7587.75 0.000000 0.000000 0.000000 0.000000 + 219.749 166.493 7586.91 0.000000 0.000000 0.000000 0.000000 + 140.995 54.3545 7578.47 0.000000 0.000000 0.000000 0.000000 + 222.102 27.6615 7564.80 0.000000 0.000000 0.000000 0.000000 + 166.091 60.8323 7560.70 0.000000 0.000000 0.000000 0.000000 + 54.0243 249.656 7560.08 0.000000 0.000000 0.000000 0.000000 + 86.2203 294.304 7551.22 0.000000 0.000000 0.000000 0.000000 + 230.635 13.7741 7551.03 0.000000 0.000000 0.000000 0.000000 + 42.3630 219.880 7520.25 0.000000 0.000000 0.000000 0.000000 + 223.474 5.85404 7506.76 0.000000 0.000000 0.000000 0.000000 + 339.768 78.3322 7496.85 0.000000 0.000000 0.000000 0.000000 + 181.819 318.912 7456.63 0.000000 0.000000 0.000000 0.000000 + 344.929 107.100 7451.92 0.000000 0.000000 0.000000 0.000000 + 194.012 110.977 7450.68 0.000000 0.000000 0.000000 0.000000 + 96.0159 87.0652 7448.98 0.000000 0.000000 0.000000 0.000000 + 309.907 201.304 7422.60 0.000000 0.000000 0.000000 0.000000 + 352.607 207.636 7407.49 0.000000 0.000000 0.000000 0.000000 + 10.0850 74.6048 7402.71 0.000000 0.000000 0.000000 0.000000 + 314.108 4.39326 7366.84 0.000000 0.000000 0.000000 0.000000 + 92.7798 301.844 7344.09 0.000000 0.000000 0.000000 0.000000 + 178.008 104.544 7342.37 0.000000 0.000000 0.000000 0.000000 + 190.225 328.578 7330.94 0.000000 0.000000 0.000000 0.000000 + 135.999 57.5390 7314.04 0.000000 0.000000 0.000000 0.000000 + 97.0393 5.48788 7313.27 0.000000 0.000000 0.000000 0.000000 + 165.200 159.932 7299.78 0.000000 0.000000 0.000000 0.000000 + 251.944 324.406 7278.26 0.000000 0.000000 0.000000 0.000000 + 68.5419 82.1139 7270.03 0.000000 0.000000 0.000000 0.000000 + 63.0529 52.6903 7243.15 0.000000 0.000000 0.000000 0.000000 + 330.566 190.512 7232.62 0.000000 0.000000 0.000000 0.000000 + 88.1024 155.162 7225.92 0.000000 0.000000 0.000000 0.000000 + 252.150 154.943 7196.68 0.000000 0.000000 0.000000 0.000000 + 296.441 217.759 7175.80 0.000000 0.000000 0.000000 0.000000 + 202.838 311.159 7156.54 0.000000 0.000000 0.000000 0.000000 + 231.353 84.4772 7150.01 0.000000 0.000000 0.000000 0.000000 + 28.1829 44.0445 7137.31 0.000000 0.000000 0.000000 0.000000 + 155.559 158.616 7136.71 0.000000 0.000000 0.000000 0.000000 + 158.502 360.723 7135.93 0.000000 0.000000 0.000000 0.000000 + 297.945 207.397 7135.41 0.000000 0.000000 0.000000 0.000000 + 174.532 275.227 7131.25 0.000000 0.000000 0.000000 0.000000 + 276.590 73.3019 7096.64 0.000000 0.000000 0.000000 0.000000 + 4.98168 226.570 7078.55 0.000000 0.000000 0.000000 0.000000 + 353.974 244.802 7064.15 0.000000 0.000000 0.000000 0.000000 + 238.717 285.406 7024.70 0.000000 0.000000 0.000000 0.000000 + 123.122 252.534 7004.02 0.000000 0.000000 0.000000 0.000000 + 341.752 234.903 6998.35 0.000000 0.000000 0.000000 0.000000 + 163.600 12.4996 6996.43 0.000000 0.000000 0.000000 0.000000 + 39.9085 188.847 6970.39 0.000000 0.000000 0.000000 0.000000 + 58.6450 126.128 6965.15 0.000000 0.000000 0.000000 0.000000 + 54.7612 220.406 6937.47 0.000000 0.000000 0.000000 0.000000 + 283.937 55.5230 6900.33 0.000000 0.000000 0.000000 0.000000 + 57.7399 307.712 6881.11 0.000000 0.000000 0.000000 0.000000 + 179.116 140.159 6871.33 0.000000 0.000000 0.000000 0.000000 + 163.289 332.439 6869.44 0.000000 0.000000 0.000000 0.000000 + 193.545 304.415 6863.99 0.000000 0.000000 0.000000 0.000000 + 112.087 266.857 6863.63 0.000000 0.000000 0.000000 0.000000 + 93.9835 13.9360 6862.43 0.000000 0.000000 0.000000 0.000000 + 336.524 204.765 6843.78 0.000000 0.000000 0.000000 0.000000 + 296.890 17.8750 6842.42 0.000000 0.000000 0.000000 0.000000 + 66.1222 217.429 6834.09 0.000000 0.000000 0.000000 0.000000 + 359.721 279.081 6816.87 0.000000 0.000000 0.000000 0.000000 + 92.6921 141.503 6810.39 0.000000 0.000000 0.000000 0.000000 + 9.90257 16.4829 6810.06 0.000000 0.000000 0.000000 0.000000 + 150.675 196.338 6804.06 0.000000 0.000000 0.000000 0.000000 + 326.662 361.814 6796.60 0.000000 0.000000 0.000000 0.000000 + 154.145 194.495 6790.79 0.000000 0.000000 0.000000 0.000000 + 24.1808 199.348 6788.36 0.000000 0.000000 0.000000 0.000000 + 262.393 133.538 6786.13 0.000000 0.000000 0.000000 0.000000 + 6.97511 116.572 6760.88 0.000000 0.000000 0.000000 0.000000 + 69.1432 42.7144 6760.76 0.000000 0.000000 0.000000 0.000000 + 195.255 84.6974 6742.48 0.000000 0.000000 0.000000 0.000000 + 333.503 187.364 6720.80 0.000000 0.000000 0.000000 0.000000 + 222.436 355.739 6710.22 0.000000 0.000000 0.000000 0.000000 diff --git a/sub_array.pro b/sub_array.pro new file mode 100644 index 0000000000000000000000000000000000000000..2b8c80a5d243ce1e960c147f26453253ca3edf8d --- /dev/null +++ b/sub_array.pro @@ -0,0 +1,54 @@ +; $Id: sub_array.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; SUB_ARRAY +; +; PURPOSE: +; Given a 2D array, extract a sub-array centered at a pre-fixed position +; and of given size. +; +; CATEGORY: +; Array manipulation. +; +; CALLING SEQUENCE: +; Result = SUB_ARRAY(Array, Bx, By) +; +; INPUTS: +; Array: 2D array +; +; Bx: X- size of sub-array to extract +; +; OPTIONAL INPUTS: +; By: Y- size of sub-array. The default is By = Bx +; +; KEYWORD PARAMETERS: +; REFERENCE: 2-components vector, coordinates of pixel where the +; sub-array must be centered. The default is the Array maximum. +; +; OUTPUTS: +; Result: Sub-array of size Bx*By and having the reference pixel specified +; by the keyword R at the relative position (Bx/2, By/2). +; If it is not possible to extract such a sub-array, the extracted +; sub-array may not have the reference pixel at (Bx/2, By/2) or its +; size may be different from Bx*By. +; +; OPTIONAL OUTPUTS: +; LX, UX, LY, UY: Output keywords containing the Lower and Upper, +; X- and Y- bounds of the sub-array in Array +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +FUNCTION sub_array, array, bx, by, REFERENCE = r, $ + LX = lx, UX = ux, LY = ly, UY = uy + + on_error, 2 + if n_params() eq 2 then by = bx + if n_elements(r) ne 2 then r = get_max(array) + bsize = round([bx, by]) + array_overlap, size52(array, /DIM), bsize, $ + round(r), bsize/2, lx, ux, ly, uy + return, array[lx:ux,ly:uy] +end diff --git a/sub_arrays.pro b/sub_arrays.pro new file mode 100644 index 0000000000000000000000000000000000000000..94af203f225ec7c2e71c058e375fb84d310782c6 --- /dev/null +++ b/sub_arrays.pro @@ -0,0 +1,58 @@ +; $Id: sub_arrays.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; SUB_ARRAYS +; +; PURPOSE: +; Given a 2D array, extract a stack of sub-arrays centered at specified +; positions and having a fixed size. +; Sub-arrays corresponding to positions next to the array border are +; suitably padded with 0s. +; +; CATEGORY: +; Array manipulation. +; +; CALLING SEQUENCE: +; SUB_ARRAYS, Array, X, Y, Boxsize, Stack, Masks +; +; INPUTS: +; Array: 2D array +; +; X, Y: Coordinates of reference positions +; +; Boxsize: Scalar, representing the size of each square sub-array +; +; OUTPUTS: +; Stack: 3D array of size Boxsize*Boxsize*N, where N is the number +; of reference points. Each reference point is ensured to be at +; the position (Boxsize/2, Boxsize/2) in the corresponding plane; +; of the output Stack +; +; Masks: Cube of byte type, having the same size as the previous Stack. +; The n-th plane in this cube is defined as follows: +; Masks[j, i, n] = 1, if Stack[j, i, n] is an Array pixel +; = 0, if Stack[j, i, n] is a padding pixel +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +PRO sub_arrays, array, x, y, boxsize, stack, masks + + on_error, 2 + nframes = n_elements(x) + stack = fltarr(boxsize, boxsize, nframes) + masks = bytarr(boxsize, boxsize, nframes) + for n = 0L, nframes - 1 do begin + box = sub_array(array, boxsize, REF = [x[n], y[n]], $ + LX = lx, UX = ux, LY = ly, UY = uy) + offset = [boxsize/2, boxsize/2] - [x[n] - lx, y[n] - ly] + box = extend_array(box, boxsize, /NO_OFF) + box = shift(box, offset[0], offset[1]) + stack[*,*,n] = box + lo = offset & up = lo + [ux - lx, uy - ly] + masks[lo[0]:up[0],lo[1]:up[1],n] = 1B + endfor + return +end diff --git a/subs_to_coord.pro b/subs_to_coord.pro new file mode 100644 index 0000000000000000000000000000000000000000..cbcb99137d0dbfd45679479dbb0331488e8a1750 --- /dev/null +++ b/subs_to_coord.pro @@ -0,0 +1,37 @@ +; $Id: subs_to_coord.pro, v 1.0 Aug 1999 e.d. $ +; +;+ +; NAME: +; SUBS_TO_COORD +; +; PURPOSE: +; Convert array subscripts to pixel coordinates. +; +; CATEGORY: +; Array manipulation. +; +; CALLING SEQUENCE: +; SUBS_TO_COORD, Subs, N_columns, X, Y +; +; INPUTS: +; Subs: Long integer vector of array subscripts +; +; N_columns: Number of columns in the 2D array +; +; OUTPUTS: +; X, Y: Column and row coordinates of pixels subscripted by Subs +; +; RESTRICTIONS: +; Apply only to 2D arrays. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +;- + +PRO subs_to_coord, subs, n_columns, x, y + + on_error, 2 + s = round(subs) & n = round(n_columns) + x = s mod n & y = s / n + return +end diff --git a/superpose_stars.pro b/superpose_stars.pro new file mode 100644 index 0000000000000000000000000000000000000000..0317485b19b596f7eb2d47516bf83c074e82f282 --- /dev/null +++ b/superpose_stars.pro @@ -0,0 +1,261 @@ +; $Id: superpose_stars.pro, v 1.4 Mar 2020 l.s. $ +; +;+ +; NAME: +; SUPERPOSE_STARS +; +; PURPOSE: +; Given the coordinates of a set of stars in an image, extract sub-images +; centered on the stars and combine them by a simple pixel-by-pixel average +; or average with sigma-clipping or median operation. This routine may be +; used to estimate the PSF in a stellar field image. +; If the stars are known, e.g. after analyzing the field with a preliminary +; PSF estimate, then it is possible to clean each selected star from all +; the other point sources around before deriving a new PSF estimate. +; +; CATEGORY: +; Signal processing. +; +; CALLING SEQUENCE: +; Result = SUPERPOSE_STARS(Image, X, Y, Siz, Stack, Masks) +; +; INPUTS: +; Image: 2D image of the stellar field +; +; X, Y: Coordinates of the stars to extract +; +; Siz: Scalar, representing the size of the output combined image +; +; KEYWORD PARAMETERS: +; NO_SUB_PIX: Set this keyword to avoid centering each star with sub-pixel +; accuracy. For details on centering, see the function CENTROIDER in +; the file 'centroider.pro'. +; +; INTERP_TYPE: Set this keyword to a string identifying the interpolation +; technique to use for sub-pixel centering. For details, see the function +; IMAGE_SHIFT in the file 'image_shift.pro' +; +; NOISE_LEVEL: Scalar, representing the background noise above the local +; sky level. It may be expressed as N * Sigma, where N is any number > 0 +; and Sigma is the overall standard deviation of the Image gaussian noise. +; +; SATURATION: Use this keyword to provide the saturation threshold which +; is used to mask the core of bright saturated stars when computing the +; median superposition. Though saturated, these stars should not be +; rejected, because they provide useful information on the PSF halo. +; The value of the keyword SATURATION may be: +; a) a scalar, representing the unique threshold to be used all over +; the frame; +; b) a 2D array, having the same size as the input Image, when the +; saturation level is not spatially uniform. This happens for +; example when the input Image has been background subtracted: +; if the removed contribution is not negligible with respect to +; the stellar peaks, the saturation threshold will be affected and +; should be defined as the original threshold minus the removed +; background contribution. +; +; MAX_NORM: Set this keyword to normalize the stars to unitary maximum +; before combining them in a single frame. The default is to normalize +; each sub-image to total unit flux. +; +; RAD_NORM: Set this keyword to normalize the stars using a circular +; region centered on the stellar peak. The value of this keyword is the +; radius of the normalization region. The default is to normalize +; each sub-image to total unit flux. +; +; AVGTYPE: Set this keyword to choose a combination algorithm among +; the following possibilities: +; AVGTYPE = 0 [default]: pixel-by-pixel average +; = 1: average with sigma-rejection of outliers +; = 2: median +; +; WEIGHTED: Set this keyword to weigh the stellar images, before averaging, +; according to their signal-to-noise ratio in the photon noise case. +; +; STARS: Stellar field model, including a replica of the PSF for each known +; star. This keyword, along with X, Y, FLUX, PSF, must be supplied when +; the selected stars, identified by the positions X and Y (see INPUTS) +; have to be cleaned from the contamination of secondary sources. This +; will produce a better PSF estimate. +; +; X_STARS, Y_STARS, FLUXES: Positions and fluxes of the known stars +; appearing in the image model supplied by the keyword STARS. +; +; PSF: Point Spread Function used to create the image model STARS. +; It may be either a 2D array or a 3D stack of local PSF images, when +; PSF is space-variant. In the latter case, it is necessary to supply +; the bounds of the image domain partition (see LX, UX, LY, UY). +; +; LX, UX, LY, UY: Vectors specifying the bounds of the image domain +; partition when the "space-variant PSF" option is used. In this case +; the sub-domain [LX[j]: Ux[j], LY[i]: UY[i]] must correspond to the +; (i * X_size + j)-th PSF in the input stack. For more details, see +; the documentation on the routine ARRAY_PARTITION. +; +; LOCAL_BKG: Set this keyword to calculate and remove the local background, +; modelled as a tilted plane, from each sub-image. +; The parameters of the local tilted plane are computed using only the pixels +; at the corners of the sub-image square domain of size Siz*Siz (the corners are +; defined as the pixels outside the circle inscribed in the square domain itself). +; +; OUTPUTS: +; Result: 2D array of size Siz*Siz containing the median combination of +; the extracted stars, after suitable processing +; +; OPTIONAL OUTPUTS: +; Stack: Cube of stellar images, optionally centered and normalized, +; before masking +; +; Masks: Cube of byte type, having the same size as the previous Stack. +; The n-th plane in this cube is defined as follows: +; Masks[j, i, n] = 1, if the pixel Stack[j, i, n] has been used to +; form the output image +; = 0, if the same pixel has been masked +; The Masks cube is used in median superposition +; +; RESTRICTIONS: +; 1) The input Image should be background subtracted. +; 2) Sub-pixel centering is based on interpolation and should be avoided +; with undersampled data. +; 3) The extracted stellar images are normalized before superposition. +; Saturated stars should have been previously repaired to ensure proper +; normalization. However previous repair of saturated stars should not +; prevent the use of the keyword SATURATION. +; +; PROCEDURE: +; Extract a box centered on each star, suitably padded with 0 if the +; source is near the Image edge. Additional 0-padded pixels will be +; masked. +; Optionally compute weights for median superposition, as the square root +; of the peak of each star. Center each sub-image with sub-pixel accuracy, +; then optionally mask pixels below the noise threshold and above the +; saturation level. Notice that if a noise level is defined, the pixels +; below this level are masked (set to 0!) and the central connected +; component of the resulting image (see IMAGE_CORE for details) is +; extracted. +; Combine the frames in the stack by average or average with sigma-rejection +; or median or weighted median. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999. +; Updates: +; 1) Fixed bug on normalization (Emiliano Diolaiti, January 2000). +; 2) Secondary stars subtraction (Emiliano Diolaiti, January 2000). +; 3) NOISE_LEVEL keyword (Emiliano Diolaiti, February 2000). +; 4) Renamed keyword NORM_MAX to MAX_NORM (Emiliano Diolaiti, September 2001). +; 5) Added keyword RAD_NORM (Emiliano Diolaiti, September 2001). +; 6) Replaced keyword UNWEIGHTED with WEIGHTED +; (Emiliano Diolaiti, September 2001). +; 7) Added keyword LOCAL_BKG (Laura Schreiber, March 2020). +;- + + + +; CLEAN_EXTRACT: extract stellar images after removing secondary sources. + +PRO clean_extract, image, x_psf, y_psf, siz, _EXTRA = extra, $ + stars, x_stars, y_stars, f_stars, psf, stack, masks + + on_error, 2 + imasiz = size52(image, /DIM) & nframes = n_elements(x_psf) + stack = fltarr(siz, siz, nframes) & masks = bytarr(siz, siz, nframes) + for n = 0L, nframes - 1 do begin + compare_lists, x_psf[n], y_psf[n], x_stars, y_stars, $ + aux1, aux2, x, y, SUBSCRIPTS_2 = w + f = f_stars[w] + sub_arrays, image - stars + image_model(x, y, f, $ + imasiz[0], imasiz[1], psf, _EXTRA = extra), $ + x_psf[n], y_psf[n], siz, frame_n, mask_n + stack[*,*,n] = frame_n & masks[*,*,n] = mask_n + endfor + return +end + +;;; The main routine. + +FUNCTION superpose_stars, image, x, y, siz, stack, masks, _EXTRA = extra, $ + NO_SUB_PIX = no_sub_pix, NOISE_LEVEL = noise_lev, $ + SATURATION = satur_lev, MAX_NORM = max_norm, $ + RAD_NORM = rad_norm, WEIGHTED = weighted, $ + STARS = stars, PSF = psf, $ + X_STARS = x_stars, Y_STARS = y_stars, FLUXES = f_stars, $ + LOCAL_BKG = local_bkg + + on_error, 2 + + ; Extract sub-images + if n_elements(stars) ne 0 and n_elements(psf) ne 0 and $ + n_elements(x_stars) ne 0 and n_elements(y_stars) ne 0 and $ + n_elements(f_stars) ne 0 then $ + clean_extract, image, x, y, siz, _EXTRA = extra, $ + stars, x_stars, y_stars, f_stars, psf, stack, masks else $ + sub_arrays, image, x, y, siz, stack, masks + var_satur = n_elements(satur_lev) ne 0 + if var_satur then $ + var_satur = max(abs(size52(satur_lev, /DIM) - size52(image, /DIM))) eq 0 + if var_satur then $ ; define a stack of non-uniform saturation levels + sub_arrays, satur_lev, x, y, siz, satur_stack + if size52(stack, /N_DIM) eq 2 then $ + nframes = 1 else nframes = (size52(stack, /DIM))[2] + + ; Calculation and removal of local background from sub-images + if keyword_set(local_bkg) then begin + wbkg = where(radial_dist(siz, siz, siz/2, siz/2) gt siz/2) + one = plane(1, 0, 0, siz, siz) + xp = plane(0, 1, 0, siz, siz) + yp = plane(0, 0, 1, siz, siz) + ainv = ginv([transpose(one[wbkg]), transpose(xp[wbkg]), transpose(yp[wbkg])]) + for n = 0L, nframes - 1 do begin + c = ainv ## transpose((stack[*,*,n])[wbkg]) + p = c[0] + c[1]*xp + c[2]*yp + stack[*,*,n] = stack[*,*,n] - p + endfor + endif + + ; Sub-pixel centering + if not keyword_set(no_sub_pix) then for n = 0L, nframes - 1 do $ + stack[*,*,n] = centroider(stack[*,*,n], XC = siz/2, YC = siz/2, _EXTRA = extra) + + ; Define weights + if keyword_set(weighted) then begin + w = fltarr(nframes) + for n = 0L, nframes - 1 do w[n] = sqrt(stack[siz/2,siz/2,n] > 0) + endif + + ; For each star, mask pixels above saturation threshold + if n_elements(satur_lev) ne 0 then begin + satur = satur_lev + for n = 0L, nframes - 1 do begin + if var_satur then satur = satur_stack[*,*,n] + exclude = where(stack[*,*,n] gt satur, n_ex) + mask_n = make_array(siz, siz, /BYTE, VALUE = 1B) + if n_ex ne 0 then mask_n[exclude] = 0B + masks[*,*,n] = masks[*,*,n] and mask_n + endfor + endif + + ; For each star, extract central connected component above noise level + if n_elements(noise_lev) ne 0 then begin + eps = 1e-12 + for n = 0L, nframes - 1 do $ + stack[*,*,n] = image_core(binary_array(stack[*,*,n], noise_lev) * $ + stack[*,*,n], eps, X = siz/2, Y = siz/2) + endif + + ; Normalize sub-images + if keyword_set(rad_norm) then $ + peak = where(radial_dist(siz, siz, siz/2, siz/2) le rad_norm) + for n = 0L, nframes - 1 do $ + if keyword_set(max_norm) then $ + stack[*,*,n] = stack[*,*,n] / stack[siz/2,siz/2,n] else $ + if keyword_set(rad_norm) then $ + stack[*,*,n] = stack[*,*,n] / total((stack[*,*,n])[peak]) else $ + stack[*,*,n] = stack[*,*,n] / total(stack[*,*,n]) + + ; Compute stack median + if nframes eq 1 then $ + avg = stack[*,*,0] * masks[*,*,0] else $ + avg = stack_combine(stack, WEIGHTS = w, MASK = masks, _EXTRA = extra) + + return, avg +end diff --git a/svpsf_extract.pro b/svpsf_extract.pro new file mode 100644 index 0000000000000000000000000000000000000000..b7c92b131f0b0aed35e5945766e1addae4b61649 --- /dev/null +++ b/svpsf_extract.pro @@ -0,0 +1,194 @@ +; $Id: svpsf_extract.pro, v 1.0 Dec 2004 e.d. $ +; +;+ +; NAME: +; SVPSF_EXTRACT +; +; PURPOSE: +; Partition an image into a regular grid and derive a PSF estimate +; for each sub-region by combining a set of point sources. +; The PSF stars are found automatically as significant peaks above +; a detection threshold or may be selected among a set supplied by +; the user. The extracted set of PSFs may be used for the identification +; and the analysis of the stars in the field by the procedure STARFINDER. +; After this analysis, SVPSF_EXTRACT may be iterated using the information +; retrieved by STARFINDER in order to improve the PSF estimate. +; +; CATEGORY: +; Stellar field photometry. +; +; CALLING SEQUENCE: +; SVPSF_EXTRACT, Image, Background, Threshold, Nx, Ny, Psfsize, Stack, Sv_par +; +; +; INPUTS: +; Image: 2D image array +; +; Background: 2D array, with the same size of Image, representing the +; background contribution +; +; Threshold: Scalar, representing the detection threshold for the +; presumed stellar peaks. +; +; Nx, Ny: Number of sub-regions along the X- and Y- axis respectively. +; The higher these numbers, the finer the partitioning of the image. +; +; Psfsize: Scalar, giving the size of each local PSF. +; +; KEYWORD PARAMETERS: +; SMOOTHBOX: Size of box for median smoothing before search for local peaks. +; +; SEARCHBOX: Define the size of the box where each significant stellar +; peak is the relative image maximum. The default is 9. +; +; NSTAR: Number of stars to combine for each sub-region. By definition, +; the NSTAR brightest stars in a given sub-region are selected. +; +; SATUR_LEV: Upper linearity threshold of the detector. The stars whose +; peak is higher than this level are not included in the PSF estimation. +; +; NORMRAD: The stars in a given sub-region, before being combined to +; estimate the local PSF, are normalized to the same flux integrated +; over a circular region of radius given by the value of this keyword. +; The default is 3. +; +; NOISE_LEVEL: Each stellar image, selected for PSF estimation, may be +; "cleaned" by masking the pixels below some threshold related to the +; noise level. A useful choice of this parameter is a few times the +; background noise standard deviation. +; +; X_STARS, Y_STARS: Input coordinates of stars to use for PSF extraction. +; If set, no search for local peaks is performed. The stars to use for +; the estimation of the PSF are selected among these positions, according +; to the constraints set by the keywords NSTAR and SATUR_LEV. +; +; FLUXES, STARS, PSF, SV_PAR: When this procedure is used after a run of +; STARFINDER, the available information on the image may be used +; to improve the PSF estimate. The new stack of PSF estimates is obtained +; by removing from each used star the contamination due to the surrounding +; sources previously identified by STARFINDER. In order to apply this +; option, the coordinates of the known stars must be supplied with the +; keywords X_STARS, Y_STARS described above and, in addition, it is +; necessary to supply also the fluxes, the image model, the previously +; used PSF and corresponding SV_PAR. The input PSF may also be a 2D array, +; if the previous analysis has been performed assuming a space-invariant +; PSF; in this case, the input SV_PAR must not be supplied. +; +; OUTPUTS: +; Stack: 3D array, of size Psfsize * Psfsize * (Nx * Ny), with the +; local PSF estimates. +; +; Sv_par: Structure to use as input parameter of the STARFINDER procedure +; to make photometry with spatially variable PSF. +; This structure has four fields: Lx, Ux, Ly, Uy. The value of each +; field is a vector containing the lower (Lx and Ly) and upper (Ux and +; Uy) bounds of the image sub-regions, along the X- and Y- directions. +; The k-th PSF in the Stack corresponds to the sub-region bounded by +; j*Nx + i, where i = k MOD Nx and j = k / Nx. +; +; RESTRICTIONS: +; +; PROCEDURE: +; Find all the significant peaks in the image, defined as the relative +; maxima above the detection threshold and not saturated. Each peak is the +; relative maximum within a certain sub-region of size given by the keyword +; parameter SEARCHBOX. The larger this figure, the lower the probability of +; selecting two close stars, whose images might be spatially overlapped. +; The image is then partitioned into a grid of sub-region and for each +; region the local PSF is estimated considering the brightest stars among +; the previously identified peaks. +; If the keywords X_STARS, Y_STARS are supplied, then the peaks are chosen +; among those defined by these keywords. +; If ALL the keywords X_STARS, Y_STARS, FLUXES, STARS, PSF, SV_PAR are supplied, +; then the peaks are chosen among those defined by X_STARS, Y_STARS and each +; stellar image selected for PSF extraction is cleaned from the contamination +; of the already known surrounding stars. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, December 2004. +; Updates: +;- + + +PRO svpsf_extract, image, background, threshold, nx, ny, psfsize, $ + stack, sv_par, $ + SMOOTHBOX = smoothbox, SEARCHBOX = searchbox, NSTAR = nstar, $ + SATUR_LEV = satur, NORMRAD = norm_rad, $ + NOISE_LEVEL = noise, CCTHRESHOLD = ccthreshold, _EXTRA = extra, $ + X_STARS = x_in, Y_STARS = y_in, FLUXES = f_in, $ + STARS = stars_in, PSF = psf_in, SV_PAR = sv_par_in, $ + SILENT = silent + + ; Default values of keywords + if n_elements(smoothbox) eq 0 then smoothbox = 3 +; if n_elements(searchbox) eq 0 then searchbox = 9 + if n_elements(nstar) eq 0 then nstar = 20 + if n_elements(norm_rad) eq 0 then norm_rad = 3 + + ; Find peaks + s = size(image, /DIM) & sx = s[0] & sy = s[1] + if n_elements(x_in) ne 0 and n_elements(y_in) ne 0 then begin + x = round(x_in) > 0 < (sx - 1) + y = round(y_in) > 0 < (sy - 1) + endif else $ + all_max, median_filter(image - background, smoothbox), x, y, BOX = searchbox + f = image[x,y] - background[x,y] + ; Reject peaks below detection threshold + w = where(f gt threshold) + x = x[w] & y = y[w] & f = f[w] + ; Reject peaks above saturation threshold + if n_elements(satur) ne 0 then begin + w = where(image[x,y] lt satur) + x = x[w] & y = y[w] & f = f[w] + endif + + ; Define sub-regions + array_partition, sx, nx, lx, ux + array_partition, sy, ny, ly, uy + sv_par = {lx: lx, ux: ux, ly: ly, uy: uy} + if n_elements(sv_par_in) ne 0 then begin + sv_lx = sv_par_in.lx + sv_ux = sv_par_in.ux + sv_ly = sv_par_in.ly + sv_uy = sv_par_in.uy + endif + + ; For each sub-region, estimate PSF + stack = fltarr(psfsize, psfsize, nx*ny) + for i = 0L, ny - 1 do for j = 0L, nx - 1 do begin + if not keyword_set(silent) then begin + print, "" + print, "Estimating PSF in sub-region centered at ", $ + (lx[j] + ux[j]) / 2, (ly[i] + uy[i]) / 2 + endif + ; Find stars in sub-region + w = where(x gt lx[j] and x lt ux[j] and y gt ly[i] and y lt uy[i], n_ji) + if n_ji eq 0 then $ + message, "No star in sub-region " + $ + strcompress(string(j), /REMOVE_ALL) + ", " + strcompress(string(i), /REMOVE_ALL) + x_ji = x[w] ;- lx[j] & + y_ji = y[w] ;- ly[i] & + f_ji = f[w] + ; Select brightest stars + n_ji = min([n_ji, nstar]) + s = (reverse(sort(f_ji)))[0:n_ji-1] + x_ji = x_ji[s] & y_ji = y_ji[s] & f_ji = f_ji[s] + ; Estimate local PSF + psf_extract, x_ji, y_ji, dummy1, dummy2, $ + image, psfsize, psf, fw, background, $ + RAD_NORM = norm_rad, AVGTYPE = 2, $ + NOISE_LEVEL = noise, /NONORM, _EXTRA = extra, $ + X_STARS = x_in, Y_STARS = y_in, FLUXES = f_in, $ + STARS = stars_in, PSF = psf_in, LX = sv_lx, UX = sv_ux, LY = sv_ly, UY = sv_uy + if not keyword_set(silent) then begin + print, "Used ", n_ji, " stars" +; print, "Stars magnitudes between ", -2.5*alog10(max(f_ji)), " and ", -2.5*alog10(min(f_ji)) + print, "PSF FWHM = ", fw + endif + if n_elements(ccthreshold) ne 0 then psf = image_core(psf, X = psfsize/2, Y = psfsize/2, ccthreshold) + psf = psf / total(psf) + stack[*,*,i*nx+j] = psf + endfor + + return +end diff --git a/synfield.fits b/synfield.fits new file mode 100644 index 0000000000000000000000000000000000000000..7eaf55eadbe18909832d08b8b8917a8237b946a2 Binary files /dev/null and b/synfield.fits differ diff --git a/update_list.pro b/update_list.pro new file mode 100644 index 0000000000000000000000000000000000000000..aa694b46f17d2ab71a6c31249148ffc71ee46e11 --- /dev/null +++ b/update_list.pro @@ -0,0 +1,58 @@ +; $Id: update_list.pro, v 1.1 Mar 2012 e.d. $ +; +;+ +; NAME: +; UPDATE_LIST +; +; PURPOSE: +; Update list of stars. +; +; CATEGORY: +; STARFINDER auxiliary procedures. +; +; CALLING SEQUENCE: +; UPDATE_LIST, List, SUBSCRIPTS = S, X, Y, F, C, $ +; Sigma_X, Sigma_Y, Sigma_F, IS_STAR = Is_Star +; +; INPUTS: +; List: list of stars. +; +; X, Y: 1D vectors with x- and y- position of stars to update. +; +; OPTIONAL INPUTS: +; F, C: 1D vectors with flux and correlation coefficient of stars to update. +; +; Sigma_X, Sigma_Y, Sigma_F: errors on position and flux. +; +; KEYWORD PARAMETERS: +; SUBSCRIPTS: 1D vector of subscripts of stars to update. If not defined, +; update all the stars in the list.; +; +; IS_STAR: set this keyword to say that the stars to update have already +; been accepted as true stars. +; +; OUTPUTS: +; List: updated list. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, June 2001. +; 1) Created this file (E. D., March 2012). +;- + +PRO update_list, list, SUBSCRIPTS = s, x, y, f, c, $ + sigma_x, sigma_y, sigma_f, IS_STAR = is_star + + on_error, 2 + n = n_elements(s) + if n eq 0 then s = lindgen(n_elements(x)) + if s[0] ge 0 then begin + list[s].x = x & list[s].y = y + if n_elements(f) ne 0 then list[s].f = f + if n_elements(c) ne 0 then list[s].c = c + if n_elements(sigma_x) ne 0 then list[s].sigma_x = sigma_x + if n_elements(sigma_y) ne 0 then list[s].sigma_y = sigma_y + if n_elements(sigma_f) ne 0 then list[s].sigma_f = sigma_f + list[s].is_a_star = keyword_set(is_star) and 1B + endif + return +end diff --git a/where_stars.pro b/where_stars.pro new file mode 100644 index 0000000000000000000000000000000000000000..82c50b6e924f6158f8ecb34531ef21876c8a56cb --- /dev/null +++ b/where_stars.pro @@ -0,0 +1,71 @@ +; $Id: where_stars.pro, v 1.1 Mar 2012 e.d. $ +; +;+ +; NAME: +; WHERE_STARS +; +; PURPOSE: +; Find subscripts of stars in a given star list, which might also +; include presumed stars. +; +; CATEGORY: +; STARFINDER auxiliary procedures. +; +; CALLING SEQUENCE: +; Result = WHERE_STARS(List, LX = Lx, UX = Ux, LY = Ly, UY = Uy, N) +; +; INPUTS: +; List: star list +; +; KEYWORD PARAMETERS: +; LX, UX, LY, UY: fix lower and upper x- and y- bounds of image +; region where the stars in the list have to searched +; +; OUTPUTS: +; Return subscripts of stars, possibly falling within specified +; region +; +; OPTIONAL OUTPUTS: +; N: number of found stars +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, June 2001. +; 1) Created this file (E. D., March 2012). +;- + +FUNCTION where_stars, list, LX = lx, UX = ux, LY = ly, UY = uy, n + + on_error, 2 + if n_tags(list) eq 0 then begin + n = 0L & return, -1L + endif + flag = list.is_a_star + if n_elements(lx) ne 0 then $ + flag = flag and list.x ge lx and list.x le ux $ + and list.y ge ly and list.y le uy + return, where(flag and 1B, n) +end + +;FUNCTION where_stars, list, LX = lx, UX = ux, LY = ly, UY = uy, n +; +; on_error, 2 +; if n_tags(list) eq 0 then begin +; n = 0L & return, -1L +; endif +; flag = list.is_a_star +; if n_elements(lx) ne 0 then begin +; wf = where(flag, n) +; if n eq 0 then return, -1L +; wlx = where((list.x)[wf] ge lx, n) +; if n eq 0 then return, -1L +; wux = where((list.x)[wf[wlx]] le ux, n) +; if n eq 0 then return, -1L +; wly = where((list.y)[wf[wlx[wux]]] ge ly, n) +; if n eq 0 then return, -1L +; wuy = where((list.y)[wf[wlx[wux[wly]]]] le uy, n) +; if n eq 0 then return, -1L +; return, [wf[wlx[wux[wly[wuy]]]]] +; endif else begin +; return, where(flag and 1B, n) +; endelse +;end diff --git a/xcompare_lists.pro b/xcompare_lists.pro new file mode 100644 index 0000000000000000000000000000000000000000..9c90b5e453c4eb1c193069e02409636fcc364531 --- /dev/null +++ b/xcompare_lists.pro @@ -0,0 +1,368 @@ +; $Id: xcompare_lists, v 1.4 Apr 2012 e.d. $ +; +;+ +; NAME: +; XCOMPARE_LISTS +; +; PURPOSE: +; Widget interface to read and compare two lists of objects. +; Each list is supposed to include floating point data ordered in rows +; of 7 elements. Such a list is produced by XStarFinder, as a result +; of the analysis of a stellar field. XCOMPARE_LISTS may be used to +; compare the results obtained for two images of the same field, observed +; at different wavelenghts. This task requires matching the coordinates, +; which are assumed to be reciprocally translated and rotated, and finding +; the common objects in the two lists. +; The output lists may be saved to a file. It is possible to plot the +; magnitudes of the common objects, producing a Color Magnitude Diagram. +; +; CATEGORY: +; Widgets. Stellar astrometry and photometry. +; +; CALLING SEQUENCE: +; XCOMPARE_LISTS, Wnum +; +; OPTIONAL INPUTS: +; Wnum: Number of an existing graphic window where the widget must +; produce a Color Magnitude Diagram. +; If undefined, the plot is displayed on a new graphic window. +; +; KEYWORD PARAMETERS: +; PATH: Initial path for file browsing when reading lists or saving +; results. If the argument of the keyword is a named variable, +; its value is overwritten. +; +; GROUP: XCompare_Lists group leader. +; +; UVALUE: XCompare_Lists user value. +; +; SIDE EFFECTS: +; 1) Initiates the XMANAGER if it is not already running. +; 2) If the window number Wnum is undefined and the user wants to plot the +; Color Magnitude Diagram, a new window is created. +; +; RESTRICTIONS: +; The Help menu opens the file +; '/starfinder/xcompare_lists_help.txt'. +; +; PROCEDURE: +; Create and register the widget as a modal widget. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, September 1999 +; Updates: +; 1) Fixed bug on output roto-translated coordinates +; (Emiliano Diolaiti, March 2000) +; 2) Enhanced error handling in event-handler +; (Emiliano Diolaiti, April 2000). +; 3) Reviewed handling of lists (Emiliano Diolaiti, June 2000). +; 4) Output file on 7 columns (E. D., April 2012). +;- + +; XCOMPARE_LISTS_EVENT: XCompare_Lists event handler. + +PRO xcompare_lists_event, event + + catch, error + if error ne 0 then begin + msg = dialog_message(/ERROR, !err_string) + child = widget_info(event.top, /CHILD) + widget_control, child, SET_UVALUE = data, /NO_COPY + close, /ALL + return + endif + ; Get user value + child = widget_info(event.top, /CHILD) + widget_control, child, GET_UVALUE = data, /NO_COPY + ; Event case + event_id = event.id & id = (*data).id + case event_id of + id.fil: $ + case event.value of + 'File.Load.List 1': begin + file = dialog_pickfile(/READ, FILTER = '*.txt', $ + PATH = *(*data).path, GET_PATH = path) + if file ne '' then begin + widget_control, /HOURGLASS + *(*data).path = path + in = transpose(read_float_data(file, 7)) + *(*data).x1 = in[*,0] & *(*data).sx1 = in[*,3] + *(*data).y1 = in[*,1] & *(*data).sy1 = in[*,4] + *(*data).f1 = in[*,2] & *(*data).sf1 = in[*,5] + *(*data).c1 = in[*,6] + ptr_free, (*data).x1c, (*data).sx1c, (*data).y1c, (*data).sy1c, $ + (*data).f1c, (*data).sf1c, (*data).c1c, $ + (*data).x_ref, (*data).y_ref + (*data).x1c = ptr_new(/ALLOCATE) + (*data).sx1c = ptr_new(/ALLOCATE) + (*data).y1c = ptr_new(/ALLOCATE) + (*data).sy1c = ptr_new(/ALLOCATE) + (*data).f1c = ptr_new(/ALLOCATE) + (*data).sf1c = ptr_new(/ALLOCATE) + (*data).c1c = ptr_new(/ALLOCATE) + (*data).x_ref = ptr_new(/ALLOCATE) + (*data).y_ref = ptr_new(/ALLOCATE) + endif + end + 'File.Load.List 2': begin + file = dialog_pickfile(/READ, FILTER = '*.txt', $ + PATH = *(*data).path, GET_PATH = path) + if file ne '' then begin + widget_control, /HOURGLASS + *(*data).path = path + in = transpose(read_float_data(file, 7)) + *(*data).x2 = in[*,0] & *(*data).sx2 = in[*,3] + *(*data).y2 = in[*,1] & *(*data).sy2 = in[*,4] + *(*data).f2 = in[*,2] & *(*data).sf2 = in[*,5] + *(*data).c2 = in[*,6] + ptr_free, (*data).x2c, (*data).sx2c, (*data).y2c, (*data).sy2c, $ + (*data).f2c, (*data).sf2c, (*data).c2c, $ + (*data).x2rt, (*data).y2rt + (*data).x2c = ptr_new(/ALLOCATE) + (*data).sx2c = ptr_new(/ALLOCATE) + (*data).y2c = ptr_new(/ALLOCATE) + (*data).sy2c = ptr_new(/ALLOCATE) + (*data).f2c = ptr_new(/ALLOCATE) + (*data).sf2c = ptr_new(/ALLOCATE) + (*data).c2c = ptr_new(/ALLOCATE) + (*data).x2rt = ptr_new(/ALLOCATE) + (*data).y2rt = ptr_new(/ALLOCATE) + endif + end + 'File.Load.Reference list': begin + file = dialog_pickfile(/READ, FILTER = '*.txt', $ + PATH = *(*data).path, GET_PATH = path) + if file ne '' then begin + widget_control, /HOURGLASS + *(*data).path = path + in = transpose(read_float_data(file, 2)) + *(*data).x_ref = in[*,0] & *(*data).y_ref = in[*,1] + endif + end + 'File.Save.List 1': begin + if n_elements(*(*data).f1c) ne 0 then begin + file = dialog_pickfile(/WRITE, FILTER = '*.txt', $ + PATH = *(*data).path, GET_PATH = path) + if file ne '' then begin + widget_control, /HOURGLASS + if strpos(file, '.txt') lt 0 then file = file + '.txt' + *(*data).path = path + out = [transpose(*(*data).x1c), $ + transpose(*(*data).y1c), $ + transpose(*(*data).f1c), $ + transpose(*(*data).sx1c), $ + transpose(*(*data).sy1c), $ + transpose(*(*data).sf1c), $ + transpose(*(*data).c1c)] + openw, lun, file, /GET_LUN, WIDTH = 200 + printf, lun, out & free_lun, lun + endif + endif + end + 'File.Save.List 2': begin + if n_elements(*(*data).f2c) ne 0 or $ + n_elements(*(*data).x2rt) ne 0 then begin + file = dialog_pickfile(/WRITE, FILTER = '*.txt', $ + PATH = *(*data).path, GET_PATH = path) + if file ne '' then begin + widget_control, /HOURGLASS + if strpos(file, '.txt') lt 0 then file = file + '.txt' + *(*data).path = path + if n_elements(*(*data).f2c) ne 0 then begin + x = *(*data).x2c & sx = *(*data).sx2c + y = *(*data).y2c & sy = *(*data).sy2c + f = *(*data).f2c & sf = *(*data).sf2c + c = *(*data).c2c + endif else $ + if n_elements(*(*data).x2rt) ne 0 then begin + x = *(*data).x2rt & sx = *(*data).sx2 + y = *(*data).y2rt & sy = *(*data).sy2 + f = *(*data).f2 & sf = *(*data).sf2 + c = *(*data).c2 + endif + out = [transpose(x), transpose(y), transpose(f), $ + transpose(sx), transpose(sy), transpose(sf), $ + transpose(c)] + openw, lun, file, /GET_LUN, WIDTH = 200 + printf, lun, out & free_lun, lun + endif + endif + end + end + id.match: begin + if n_elements(*(*data).x1) eq 0 or n_elements(*(*data).y1) eq 0 then $ + msg = dialog_message(/ERROR, 'Load list 1.') else $ + if n_elements(*(*data).x2) eq 0 or n_elements(*(*data).y2) eq 0 then $ + msg = dialog_message(/ERROR, 'Load list 2.') else $ + if n_elements(*(*data).x_ref) eq 0 or n_elements(*(*data).y_ref) eq 0 then $ + msg = dialog_message(/ERROR, 'Load reference points for list 1.') $ + else begin + xmatch_coord, *(*data).x1, *(*data).y1, *(*data).x2, *(*data).y2, $ + *(*data).x_ref, *(*data).y_ref, x2_out, y2_out, $ + GROUP = event.top + if n_elements(x2_out) ne 0 and n_elements(y2_out) ne 0 then begin + *(*data).x2rt = x2_out & *(*data).y2rt = y2_out + endif + endelse + end + id.find: begin + if n_elements(*(*data).x1) eq 0 or n_elements(*(*data).y1) eq 0 then $ + msg = dialog_message(/ERROR, 'Load list 1.') else $ + if n_elements(*(*data).x2) eq 0 or n_elements(*(*data).y2) eq 0 then $ + msg = dialog_message(/ERROR, 'Load list 2.') $ + else begin + if n_elements(*(*data).x2rt) le 1 then begin + *(*data).x2rt = *(*data).x2 & *(*data).y2rt = *(*data).y2 + endif + form = cw_form(['0,FLOAT,1,LABEL_LEFT=Distance,TAG=dist,WIDTH=12', $ + '2, BUTTON,OK,QUIT,NO_RELEASE'], TITLE = 'Matching distance') + widget_control, /HOURGLASS + compare_lists, *(*data).x1, *(*data).y1, *(*data).x2rt, *(*data).y2rt, $ + MAX_DISTANCE = form.dist, x1c, y1c, x2c, y2c, $ + SUBSCRIPTS_1 = sub1, SUBSCRIPTS_2 = sub2 + if n_elements(sub1) ne 0 then begin + *(*data).x1c = x1c & *(*data).y1c = y1c + *(*data).x2c = x2c & *(*data).y2c = y2c + *(*data).f1c = (*(*data).f1)[sub1] + *(*data).sx1c = (*(*data).sx1)[sub1] + *(*data).sy1c = (*(*data).sy1)[sub1] + *(*data).sf1c = (*(*data).sf1)[sub1] + *(*data).c1c = (*(*data).c1)[sub1] + *(*data).f2c = (*(*data).f2)[sub2] + *(*data).sx2c = (*(*data).sx2)[sub2] + *(*data).sy2c = (*(*data).sy2)[sub2] + *(*data).sf2c = (*(*data).sf2)[sub2] + *(*data).c2c = (*(*data).c2)[sub2] + endif + msg = dialog_message(/INFO, 'Found ' + $ + strcompress(string(n_elements(sub1)), /REMOVE_ALL) + ' common stars.') + endelse + end + id.cmd: begin + if n_elements(*(*data).f1c) ne 0 and n_elements(*(*data).f2c) ne 0 then begin + mag1 = *(*data).f1c & mag2 = *(*data).f2c + endif else $ + if n_elements(*(*data).f1) ne 0 and n_elements(*(*data).f2) ne 0 then begin + mag1 = *(*data).f1 & mag2 = *(*data).f2 + endif else $ + msg = dialog_message(/ERROR, 'No data to plot.') + if n_elements(mag1) ne 0 and n_elements(mag2) ne 0 then begin + w = where(mag1 gt 0 and mag2 gt 0, count) + if count ne 0 then begin + mag1 = -2.5*alog10(mag1[w]) & mag2 = -2.5*alog10(mag2[w]) + form = cw_form(TITLE = 'Magnitude zero point: set 1', $ + ['0,FLOAT,0,LABEL_LEFT=Magnitude zero point,TAG=z,WIDTH=12', $ + '2, BUTTON,OK,QUIT,NO_RELEASE']) + mag1 = mag1 + form.z + form = cw_form(TITLE = 'Magnitude zero point: set 2', $ + ['0,FLOAT,0,LABEL_LEFT=Magnitude zero point,TAG=z,WIDTH=12', $ + '2, BUTTON,OK,QUIT,NO_RELEASE']) + mag2 = mag2 + form.z + xplot, mag1, mag1 - mag2, WNUM = (*data).wnum, $ + PATH = *(*data).path, DEFAULT_PAR = *(*data).plot_par + endif + endif + end + id.hlp: $ + xdispfile, file_name('starfinder', 'xcompare_lists_help.txt'), $ + TITLE = 'XCompare_Lists help', /MODAL + id.done: begin + widget_control, child, SET_UVALUE = data, /NO_COPY + widget_control, event.top, /DESTROY + end + endcase + if event_id ne id.done then $ + widget_control, child, SET_UVALUE = data, /NO_COPY + return +end + +; XCOMPARE_LISTS_DEF: define data structure. + +FUNCTION xcompare_lists_def, wnum, path, id + + on_error, 2 + if n_elements(wnum) eq 0 then w = -1 else w = wnum + data = {x1: ptr_new(/ALLOCATE), sx1: ptr_new(/ALLOCATE), $ + y1: ptr_new(/ALLOCATE), sy1: ptr_new(/ALLOCATE), $ + f1: ptr_new(/ALLOCATE), sf1: ptr_new(/ALLOCATE), $ + c1: ptr_new(/ALLOCATE), $ ; input parameters of set 1 + x2: ptr_new(/ALLOCATE), sx2: ptr_new(/ALLOCATE), $ + y2: ptr_new(/ALLOCATE), sy2: ptr_new(/ALLOCATE), $ + f2: ptr_new(/ALLOCATE), sf2: ptr_new(/ALLOCATE), $ + c2: ptr_new(/ALLOCATE), $ ; input parameters of set 2 + x_ref: ptr_new(/ALLOCATE), y_ref: ptr_new(/ALLOCATE), $ + x2rt: ptr_new(/ALLOCATE), y2rt: ptr_new(/ALLOCATE), $ + id: id, wnum: w, path: ptr_new(path), $ ; other parameters + x1c: ptr_new(/ALLOCATE), sx1c: ptr_new(/ALLOCATE), $ + y1c: ptr_new(/ALLOCATE), sy1c: ptr_new(/ALLOCATE), $ + f1c: ptr_new(/ALLOCATE), sf1c: ptr_new(/ALLOCATE), $ + c1c: ptr_new(/ALLOCATE), $ ; output parameters of set 1 + x2c: ptr_new(/ALLOCATE), sx2c: ptr_new(/ALLOCATE), $ + y2c: ptr_new(/ALLOCATE), sy2c: ptr_new(/ALLOCATE), $ + f2c: ptr_new(/ALLOCATE), sf2c: ptr_new(/ALLOCATE), $ + c2c: ptr_new(/ALLOCATE), $ ; output parameters of set 2 + plot_par: ptr_new(/ALLOCATE)} + return, data +end + +; XCOMPARE_LISTS_DEL: de-reference and de-allocate heap variables. + +PRO xcompare_lists_del, data, path + + if n_elements(*(*data).path) ne 0 then path = *(*data).path + ptr_free, (*data).x1, (*data).sx1, (*data).y1, (*data).sy1, $ + (*data).f1, (*data).sf1, (*data).c1, $ + (*data).x2, (*data).sx2, (*data).y2, (*data).sy2, $ + (*data).f2, (*data).sf2, (*data).c2, $ + (*data).x_ref, (*data).y_ref, (*data).x2rt, (*data).y2rt, $ + (*data).x1c, (*data).sx1c, (*data).y1c, (*data).sy1c, $ + (*data).f1c, (*data).sf1c, (*data).c1c, $ + (*data).x2c, (*data).sx2c, (*data).y2c, (*data).sy2c, $ + (*data).f2c, (*data).sf2c, (*data).c2c, $ + (*data).path, (*data).plot_par + ptr_free, data + return +end + +; XCOMPARE_LISTS: XCompare_Lists widget definition module. + +PRO xcompare_lists, wnum, PATH = path, GROUP = group, UVALUE = uvalue + + on_error, 2 + ; Create group leader if necessary + if n_elements(group) eq 0 then $ + group_id = widget_base() else group_id = group + ; Create modal base + if n_elements(uvalue) eq 0 then uvalue = 0B + base = widget_base(TITLE = 'XCompare_Lists', /MODAL, UVALUE = uvalue, $ + GROUP_LEADER = group_id, /COLUMN) + ; Define child of top level base to store data + child = widget_base(base) + ; Define File menu + desc = ['1\File', '1\Load', '0\List 1', '0\List 2', $ + '2\Reference list', '3\Save', '0\List 1', '2\List 2'] + fil = cw_pdmenu(base, desc, /RETURN_FULL_NAME) + ; Define 'processing' buttons + pbase = widget_base(base, /COLUMN) + match = widget_button(pbase, VALUE = 'Match coordinates', /NO_RELEASE) + find = widget_button(pbase, VALUE = 'Find coincident', /NO_RELEASE) + cmd = widget_button(pbase, VALUE = 'Color Magnitude Diagram', /NO_RELEASE) + ; Define other buttons + ubase = widget_base(base, /ROW) + hlp = widget_button(ubase, VALUE = 'Help', /NO_RELEASE) + done = widget_button(ubase, VALUE = 'Exit', /NO_RELEASE) + ; Define pointer to auxiliary/output data + id = {fil: fil, match: match, find: find, cmd: cmd, hlp: hlp, done: done} + data = xcompare_lists_def(wnum, path, id) + data = ptr_new(data, /NO_COPY) + widget_control, child, SET_UVALUE = data + ; Realize, register, etc. + widget_control, base, /REALIZE + xmanager, 'xcompare_lists', base, EVENT_HANDLER = 'xcompare_lists_event' + ; De-reference output data and de-allocate heap variables + xcompare_lists_del, data, path + ; Destroy group leader if necessary + if n_elements(group) eq 0 then $ + widget_control, group_id, /DESTROY + return +end diff --git a/xcompare_lists_help.txt b/xcompare_lists_help.txt new file mode 100644 index 0000000000000000000000000000000000000000..73f95ad81e09b6714f9242163532ef81a6a672e8 --- /dev/null +++ b/xcompare_lists_help.txt @@ -0,0 +1,69 @@ + XCompare_Lists help page + + + + GENERAL DESCRIPTION + + The XCompare_Lists widget compares two lists of stars, + resulting for instance from the analysis of two images of + the same field observed at different wavelenghts. + In general the two frames are translated and rotated one + another: the proper transformation must be determined, in + order to map one set of coordinates onto the other. + After matching the reference frames, the common points + can be found. + The input lists of stars must have the same format as the + outputs produced by XStarFinder_Run; the data must be + stored on rows of 7 columns each, in the following order: + + x y flux sigma_x sigma_y sigma_flux correlation + + The reference list used to match the reference frames (see + the 'File' menu below) must contain only the coordinates + of the reference stars, in the following order: + + x y + + + + CONTROLS/BUTTONS + + 'File': + The 'File' pull-down menu includes two sub-menus for + lists input/output. + The 'Load' sub-menu allows the user to read + - the lists to compare, named List 1 and List 2; + - a reference list (coordinates only!), containing a subset + of the objects in List 1, which is used to find the + coordinate transformation between the two reference frames. + The 'Save' sub-menu allows the user to save the processed + Lists 1 and 2, either after matching the reference frames + or after finding the common objects. + + 'Match coordinates': + Find the optimal transformation to map coordinates of + List 2 onto the reference frame of List 1. This is done + by the widget application XMatch_Coord. + + 'Find coincident': + Find coincident points in the two lists. The user is + requested to supply a 'matching distance', which + represents a tolerance to identify coincident points. + + 'Color Magnitude Diagram': + Create a simple color-magnitude diagram, plotting Mag1 + on the vertical axis vs. (Mag1 - Mag2) on the horizontal + axis, where Mag1 and Mag2 are the magnitudes of the + stars in Lists 1 and 2 respectively. Magnitudes are + defined as + Mag = -2.5 * Log10(Flux); + the user is requested to enter a zero-point for the + magnitude scale. + The plot is created by means of the XPlot widget + application and can be saved as a PostScript file. + + 'Help': + Display this help page. + + 'Exit': + Quit XCompare_Lists. \ No newline at end of file diff --git a/xdispfile.pro b/xdispfile.pro new file mode 100644 index 0000000000000000000000000000000000000000..4fcb5f8aadb814b35fb41b3d902151f12273e4f6 --- /dev/null +++ b/xdispfile.pro @@ -0,0 +1,110 @@ +; $Id: xdispfile.pro, v 1.1 Apr 2000 e.d. $ +; +;+ +; NAME: +; XDISPFILE +; +; PURPOSE: +; Display an ASCII text file. +; This is a simplified version of the IDL library routine +; XDisplayFile, with the only additional feature that it +; can be called by other modal widgets. +; +; CATEGORY: +; Widgets. +; +; CALLING SEQUENCE: +; XDISPFILE, Filename +; +; INPUTS: +; Filename: String, containing the name of the file. +; +; KEYWORD PARAMETERS: +; BLOCK: Set this keyword to have XMANAGER block the IDL +; command line when this application is registered. +; The default is 'not blocked'. +; +; GROUP: The widget identifier of the group leader of the widget. +; If this keyword is specified and the keyword MODAL (see +; below) is not set, the death of the group leader results +; in the death of XDispFile. +; +; MODAL: Set this keyword to any nonzero value to create the widget +; as a modal widget, blocking the IDL command line and every +; other previously created widget applications. +; +; TITLE: Widget title. The default is Filename. +; +; WIDTH: Width of the text widget in characters. The default is 80. +; +; HEIGHT: Number of lines to be displayed in the text widget. +; Scroll bars are available to see the hidden text. +; The default height is 24 lines. +; +; OUTPUTS: +; None. Text is displayed on the widget. +; +; SIDE EFFECTS: +; Initiates the XMANAGER if it is not already running. +; +; PROCEDURE: +; Read the specified ASCII file and display its content +; on a text widget. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, November 1999. +; Updates: +; 1) Enhanced error handling (Emiliano Diolaiti, April 2000). +;- + +; XDISPFILE_EVENT: XDispFile event handler. + +PRO xdispfile_event, event + + widget_control, event.id, GET_UVALUE = ev_type + if strlowcase(ev_type) eq 'quit' then $ + widget_control, event.top, /DESTROY + return +end + +; XDISPFILE: XDispFile widget definition module. + +PRO xdispfile, filename, GROUP = group, MODAL = modal, BLOCK = block, $ + TITLE = title, WIDTH = width, HEIGHT = height + + catch, error + if error ne 0 then begin + msg = dialog_message(/ERROR, !err_string) + close, /ALL + return + endif + ; Open file and read its content + openr, unit, filename, /GET_LUN + text = '' & line = text + while not eof(unit) do begin + readf, unit, line & text = [text, line] + endwhile + free_lun, unit + ; Define default values + if n_elements(title) eq 0 then title = filename + if n_elements(group) ne 0 then group_leader = group + if keyword_set(modal) then $ + if n_elements(group) eq 0 then group_leader = widget_base() + if n_elements(width) eq 0 then width = 80 + if n_elements(height) eq 0 then height = 24 + ; Define widget + base = widget_base(TITLE = title, /COLUMN, $ + MODAL = modal, GROUP_LEADER = group_leader) + text_id = widget_text(base, /SCROLL, VALUE = text, $ + XSIZE = width, YSIZE = height) + lo_base = widget_base(base) + quit_id = widget_button(lo_base, VALUE = 'Close', UVALUE = 'quit') + ; Realize widget, register it, etc. + widget_control, base, /REALIZE + xmanager, 'xdispfile', base, $ + EVENT_HANDLER = 'xdispfile_event', $ + NO_BLOCK = (not keyword_set(block)) and 1B + if keyword_set(modal) then if n_elements(group) eq 0 then $ + widget_control, group_leader, /DESTROY + return +end diff --git a/xdisplayopt.pro b/xdisplayopt.pro new file mode 100644 index 0000000000000000000000000000000000000000..4b7ce41f3cfd619580e8f100d554056c7dd86168 --- /dev/null +++ b/xdisplayopt.pro @@ -0,0 +1,177 @@ +; $Id: xdisplayopt.pro, v 1.1 Apr 2000 e.d. $ +; +;+ +; NAME: +; XDISPLAYOPT +; +; PURPOSE: +; Modify display options for a 2D image. +; +; CATEGORY: +; Widgets. Data visualization. +; +; CALLING SEQUENCE: +; Result = XDISPLAYOPT(Image, Wnum) +; +; INPUTS: +; Image: 2D data array +; +; OPTIONAL INPUT PARAMETERS: +; Wnum: window number of an existing window +; +; KEYWORD PARAMETERS: +; OPTIONS: structure of current display options; the structure must be +; defined as in DEFAULT_DISPLAY_OPT +; +; GROUP: group leader of top level base +; +; NODISPLAY: avoid first image display. This keyword should be set only +; if the image is already displayed in its window (as in a call from +; DISPLAY_IMAGE) +; +; UVALUE: xdisplayopt user value +; +; OUTPUTS: +; Return structure of modified display options +; +; OPTIONAL OUTPUT PARAMETERS: +; Wnum: window number of new window, if undefined on input +; +; SIDE EFFECTS: +; 1) Initiates the XMANAGER if it is not already running. +; 2) Use WSET to activate the graphic window identified by Wnum. +; 3) Create a new graphic window if the input parameter Wnum is undefined. +; +; RESTRICTIONS: +; 1) If the parameter Wnum is a window number < 32 but the corresponding +; window does not exist (or has been deleted), the new window size is set +; by IDL and may not fit the image x- and y size. If Wnum >=32 and the +; corresponding window does not exists, an error occurs. +; 2) The Help menu opens the file '/starfinder/xdisplay_opt_help.txt'. +; +; PROCEDURE: +; Create and register the widget as a modal widget. +; Then let the user modify the image display options. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August 1999 +; Updates: +; 1) Enhanced error handling in event-handler +; (Emiliano Diolaiti, April 2000). +;- + + +; XDISPLAYOPT_EVENT: XDisplayOpt event handler. + +PRO xdisplayopt_event, event + + catch, error + if error ne 0 then begin + msg = dialog_message(/ERROR, !err_string) + child = widget_info(event.top, /CHILD) + widget_control, child, SET_UVALUE = info, /NO_COPY + return + endif + ; Get user value + child = widget_info(event.top, /CHILD) + widget_control, child, GET_UVALUE = info, /NO_COPY + ; Event case + event_id = event.id & id = (*info).id + case event_id of + id.min_level: (*info).options.range[0] = event.value + id.max_level: (*info).options.range[1] = event.value + id.stretch: (*info).options.stretch = event.value + id.reverse: (*info).options.reverse = not (*info).options.reverse and 1B + id.chop: (*info).options.chop = event.value + id.ct: (*info).options.color_table = event.index + id.apply: begin + widget_control, /HOURGLASS + display_image, (*info).image, (*info).wnum, OPTIONS = (*info).options + end + id.hlp: $ + xdispfile, file_name('starfinder', 'xdisplayopt_help.txt'), $ + TITLE = 'XDisplayOpt help', /MODAL + id.done: begin + widget_control, child, SET_UVALUE = info, /NO_COPY + widget_control, event.top, /DESTROY + end + endcase + ; Set user value + if event_id ne id.done then $ + widget_control, child, SET_UVALUE = info, /NO_COPY + return +end + + +; XDISPLAYOPT: XDisplayOpt widget definition module. + +FUNCTION xdisplayopt, image, wnum, OPTIONS = options, NODISPLAY = nodisplay, $ + GROUP = group, UVALUE = uvalue + + catch, display_error + if display_error ne 0 then begin + if n_elements(options) eq 0 then options = 0 + return, options + endif + ; Display image, retrieving default options if undefined + if not keyword_set(nodisplay) then $ + display_image, image, wnum, OPTIONS = options else $ + if n_elements(options) eq 0 then $ + options = default_display_opt(image) + ; Define group leader, if necessary + if n_elements(group) eq 0 then $ + group_id = widget_base() else group_id = group + ; Define modal top level base + if n_elements(uvalue) eq 0 then uvalue = 0B + base = widget_base(TITLE = 'XDisplayOpt', GROUP_LEADER = group_id, $ + /MODAL, /COLUMN, UVALUE = uvalue) + ; Define child, to store data structure + child = widget_base(base) + ; Define 'Intensity range' input fields + min_level = cw_form(base, '0, FLOAT,,WIDTH=12,LABEL_LEFT=Minimum intensity ') + widget_control, min_level, SET_VALUE = $ + {tag0: strcompress(string(options.range[0]), /REMOVE_ALL)} + max_level = cw_form(base, '0, FLOAT,,WIDTH=12,LABEL_LEFT=Maximum intensity ') + widget_control, max_level, SET_VALUE = $ + {tag0: strcompress(string(options.range[1]), /REMOVE_ALL)} + ; Define Stretch buttons + slabel = widget_label(base, VALUE = 'Stretch:', /ALIGN_LEFT) + stretch_opt = ['square', 'linear', 'square root', 'logarithm'] + stretch = cw_bgroup(base, stretch_opt, /EXCLUSIVE, /NO_RELEASE, $ + SET_VALUE = (where(stretch_opt eq options.stretch))[0], $ + BUTTON_UVALUE = stretch_opt) + reverse = cw_bgroup(base, ['reverse scale'], /NONEXCLUSIVE, $ + SET_VALUE = options.reverse and 1B, $ + BUTTON_UVALUE = 'reverse') + ; Define 'Chopping threshold' input field + chop = cw_form(base, '0, FLOAT,,WIDTH=12,LABEL_LEFT=Chopping threshold ') + widget_control, chop, SET_VALUE = $ + {tag0: strcompress(string(options.chop), /REMOVE_ALL)} + ; Define 'Color Table' list + ct_names = '' ; define ct_names for LoadCT + loadct, GET_NAMES = ct_names, /SILENT + ctlabel = widget_label(base, VALUE = 'Color Table:', /ALIGN_LEFT) + ct = widget_list(base, VALUE = ct_names, YSIZE = 5) + ; Define other buttons + base2 = widget_base(base, /ROW) + apply = widget_button(base2, VALUE = 'Apply options', /NO_RELEASE) + base3 = widget_base(base, /ROW) + hlp = widget_button(base3, VALUE = 'Help', /NO_RELEASE) + done = widget_button(base3, VALUE = 'Exit', /NO_RELEASE) + ; Realize widget, set user value, register, etc. + id = {min_level: min_level, max_level: max_level, $ + stretch: stretch, reverse: reverse, chop: chop, ct: ct, $ + apply: apply, hlp: hlp, done: done} ; IDs structure + data = {id: id, wnum: wnum, image: image, options: options} ; data structure + data = ptr_new(data, /NO_COPY) ; pointer to data structure + widget_control, child, SET_UVALUE = data + ; NOTE: /NO_COPY is not set here, to avoid losing the pointer to + ; the global data stored in the user-value of the 'child' base + widget_control, base, /REALIZE + xmanager, 'xdisplayopt', base, EVENT_HANDLER = 'xdisplayopt_event' + options = (*data).options & ptr_free, data + ; Destroy group leader base, if necessary + if n_elements(group) eq 0 then $ + widget_control, group_id, /DESTROY + return, options +end diff --git a/xdisplayopt_help.txt b/xdisplayopt_help.txt new file mode 100644 index 0000000000000000000000000000000000000000..c56477717d30a6f25654d765d106311984054270 --- /dev/null +++ b/xdisplayopt_help.txt @@ -0,0 +1,56 @@ + XDisplay_Opt help page + + + + GENERAL DESCRIPTION + + The XDisplay_Opt widget allows the user to define the + options to display a given image. + + + + PARAMETERS + + 'Minimum intensity': + Minimum intensity level to display. Values lower than + this bound are set to 'Minimum intensity' before display. + + 'Maximum intensity': + Maximum intensity level to display. + + 'Stretch': + Define display scale. Possible choices are + - square (maximum contrast) + - linear + - square root + - logarithm (minimum contrast) + + 'Reverse scale': + Reverse gray level scale: the brightest pixels will be + represented by the lowest gray level. + + 'Chopping threshold': + Pixels above this threshold are set to the minimum gray + level. This options is useful to highlight saturated + regions in the image. + + 'Color Table': + Select a pre-defined color table. A suitable choice is + 'STERN SPECIAL'. + This option might not work properly if the screen is not + set to 256 colors. + + + + CONTROLS/BUTTONS + + 'Apply options': + Display the input array with the currently defined + options. The options are applied to a copy of the input + array, which of course is not modified. + + 'Help': + Display this help page. + + 'Exit': + Quit XDisplay_Opt. \ No newline at end of file diff --git a/ximage_support.pro b/ximage_support.pro new file mode 100644 index 0000000000000000000000000000000000000000..394a90bac5c6b8d8cb436026779e9fcb62216321 --- /dev/null +++ b/ximage_support.pro @@ -0,0 +1,198 @@ +; $Id: ximage_support, v 1.1 Apr 1999 e.d. $ +; +;+ +; NAME: +; XIMAGE_SUPPORT +; +; PURPOSE: +; Widget interface for the CIRC_MASK and IMAGE_CORE functions. +; Modify the support of an image by applying a circular mask and/or +; extracting the principal connected component above a pre-fixed threshold. +; The reference pixel for CIRC_MASK and IMAGE_CORE is assumed to be the +; maximum intensity pixel of the input array. +; +; CATEGORY: +; Widgets. Signal processing. +; +; CALLING SEQUENCE: +; Result = XIMAGE_SUPPORT(Psf, Wnum, Display_opt) +; +; INPUTS: +; Psf: 2D data array, containing the image of the point source to smooth +; +; OPTIONAL INPUTS: +; Wnum: Window number of an existing window +; +; Display_opt: Structure of current display options; +; the structure must be defined as in DEFAULT_DISPLAY_OPT +; +; KEYWORD PARAMETERS: +; DEFAULT_PAR: Structure of default parameters for the widget's form. +; +; GROUP: XImage_Support group leader. +; +; UVALUE: XImage_Support user value. +; +; OUTPUTS: +; Result: Input Image with processed support +; +; OPTIONAL OUTPUTS: +; Wnum: window number of new window, if undefined on input +; +; Display_opt: Display options, as defined and/or modified by +; X_Psf_smooth +; +; DEFAULT_PAR: Set this keyword to a named variable to get the +; structure of parameters set by the widget's user. +; +; SIDE EFFECTS: +; 1) Initiates the XMANAGER if it is not already running. +; 2) Call DISPLAY_IMAGE to activate the graphic window identified by Wnum. +; 3) Create a new graphic window if not defined on input (see DISPLAY_IMAGE +; and XDISPLAYOPT for details). +; 4) Any image displayed on the window Wnum is overwritten first by the +; input image and then by the processed array. +; +; RESTRICTIONS: +; The Help menu opens the file +; '/starfinder/ximage_support_help.txt'. +; +; PROCEDURE: +; Create and register the widget as a modal widget. +; Then let the user define and or/modify the support options and apply +; them to the input image. As a new processed image is obtained applying +; the current options, it is displayed on the graphic window. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, September 1999 +; Updates: +; 1) Enhanced error handling in event-handler +; (Emiliano Diolaiti, April 2000). +;- + +; XIMAGE_SUPPORT_EVENT: XImage_Support event handler. + +PRO ximage_support_event, event + + catch, error + if error ne 0 then begin + msg = dialog_message(/ERROR, !err_string) + widget_control, event.id, SET_UVALUE = data, /NO_COPY + return + endif + widget_control, event.id, GET_UVALUE = data, /NO_COPY + event_type = strlowcase(event.tag) + case event_type of + 'mask': $ + widget_control, (*data).ids[0], SENSITIVE = (event.value eq 1) and 1B + 'comp': $ + widget_control, (*data).ids[1], SENSITIVE = (event.value eq 1) and 1B + 'proc': begin + widget_control, /HOURGLASS + widget_control, event.id, GET_VALUE = form + (*data).par.mask = form.mask + (*data).par.rad = form.rad + (*data).par.comp = form.comp + (*data).par.thresh = form.thresh + mask = (form.mask eq 1) and 1B + if mask then rad = form.rad + comp = (form.comp eq 1) and 1B + if comp then thresh = form.thresh + (*data).image_out = (*data).image_in + m = get_max((*data).image_out) + if mask then $ + (*data).image_out = circ_mask((*data).image_out, m[0], m[1], rad) + if comp then $ + (*data).image_out = image_core((*data).image_out, thresh, X = m[0], Y = m[1]) + display_image, (*data).image_out, (*data).wnum, OPT = (*data).display_opt + end + 'display': $ + (*data).display_opt = xdisplayopt((*data).image_out, (*data).wnum, $ + /NODISPLAY, OPTIONS = (*data).display_opt, GROUP = event.top) + 'help': $ + xdispfile, file_name('starfinder', 'ximage_support_help.txt'), $ + TITLE = 'XImage_Support help', /MODAL + 'exit': begin + widget_control, event.id, SET_UVALUE = data, /NO_COPY + widget_control, event.top, /DESTROY + end + else: + endcase + if event_type ne 'exit' then $ + widget_control, event.id, SET_UVALUE = data, /NO_COPY + return +end + +; XIMAGE_SUPPORT_PAR: define default parameters. + +PRO ximage_support_par, id, maxsiz, par + + if n_elements(par) ne 0 then begin + mask = par.mask + rad = par.rad + comp = par.comp + thresh = par.thresh + endif else begin + mask = 0 + rad = maxsiz + comp = 0 + thresh = 0 + par = {mask: mask, rad: rad, comp: comp, thresh: thresh} + endelse + init = {mask: mask, rad: strcompress(string(rad), /REMOVE_ALL), $ + comp: comp, thresh: strcompress(string(thresh), /REMOVE_ALL)} + widget_control, id, SET_VALUE = init + return +end + +; XIMAGE_SUPPORT: XImage_Support widget definition module. + +FUNCTION ximage_support, image, wnum, display_opt, DEFAULT_PAR = par, $ + GROUP = group, UVALUE = uvalue + + on_error, 2 + ; Display image and define display options + display_image, image, wnum, OPTIONS = display_opt + ; Create group leader if necessary + if n_elements(group) eq 0 then $ + group_id = widget_base() else group_id = group + ; Create modal base + if n_elements(uvalue) eq 0 then uvalue = 0B + base = widget_base(TITLE = 'XImage_Support', /MODAL, UVALUE = uvalue, $ + GROUP_LEADER = group_id) + ; Define form + desc = [ $ + '0, LABEL,Circular mask:', $ + '1, BASE,,COLUMN,FRAME', $ + '0, BUTTON,No|Yes,EXCLUSIVE,TAG=mask,ROW', $ + '2, INTEGER,,LABEL_LEFT=Circular mask radius,TAG=rad', $ + '0, LABEL,Principal component extraction:', $ + '1, BASE,,COLUMN,FRAME', $ + '0, BUTTON,No|Yes,EXCLUSIVE,TAG=comp,ROW', $ + '2, FLOAT,,LABEL_LEFT=Threshold for principal component extraction,TAG=thresh', $ + '1, BASE,,ROW', $ + '0, BUTTON,Processing...,NO_RELEASE,TAG=proc', $ + '2, BUTTON,Display Options,NO_RELEASE,TAG=display', $ + '1, BASE,,ROW', $ + '0, BUTTON,Help,NO_RELEASE,TAG=help', $ + '2, BUTTON,Exit,QUIT,NO_RELEASE,TAG=exit'] + form = cw_form(base, desc, IDS = ids, /COLUMN) + ximage_support_par, form, max(size52(image, /DIM)), par + widget_control, ids[3], SENSITIVE = par.mask eq 1 and 1B + widget_control, ids[7], SENSITIVE = par.comp eq 1 and 1B + ; Define pointer to auxiliary/output data + data = {par: par, image_in: image, image_out: image, $ + wnum: wnum, display_opt: display_opt, ids: [ids[3], ids[7]]} + data = ptr_new(data, /NO_COPY) + widget_control, form, SET_UVALUE = data + ; Realize, register, etc. + widget_control, base, /REALIZE + xmanager, 'ximage_support', base, EVENT_HANDLER = 'ximage_support_event' + ; De-reference output data + par = (*data).par & image_out = (*data).image_out & display_opt = (*data).display_opt + ptr_free, data + ; Destroy group leader if necessary + if n_elements(group) eq 0 then $ + widget_control, group_id, /DESTROY + return, image_out +end diff --git a/ximage_support_help.txt b/ximage_support_help.txt new file mode 100644 index 0000000000000000000000000000000000000000..e02e3b0c30dd4cca50cfe03b3030a5e4a71a8241 --- /dev/null +++ b/ximage_support_help.txt @@ -0,0 +1,41 @@ + XImage_Support help page + + + GENERAL DESCRIPTION + + The XImage_Support widget modifies the support of the input + image by either applying a circular mask centered on the + image maximum or extracting the principal connected component, + i.e. the set of pixels above a pre-fixed intensity threshold + and connected by a path to the maximum intensity pixel. + + + + PARAMETERS + + 'Circular mask radius': + Mask radius in pixel units. + + 'Threshold for principal component extraction': + Threshold representing the lower value to identify the region + of pixels connected by a path to the maximum intensity pixel. + + + + CONTROLS/BUTTONS + + 'Processing...': + Apply the currently defined options to the input image. + Whenever the 'Processing...' button is pressed, the previous + result is overwritten by the input image and the processing + is performed with the current options. The result of the + last processing is returned on output. + + 'Display Options': + Modify the display options of the displayed image. + + 'Help': + Display this help page. + + 'Exit': + Quit XImage_Support. \ No newline at end of file diff --git a/xmatch_coord.pro b/xmatch_coord.pro new file mode 100644 index 0000000000000000000000000000000000000000..fd0daf51f9b41965dfa21f7f5229795e4b542e61 --- /dev/null +++ b/xmatch_coord.pro @@ -0,0 +1,187 @@ +; $Id: xmatch_coord, v 1.1 Apr 2000 e.d. $ +; +;+ +; NAME: +; XMATCH_COORD +; +; PURPOSE: +; Widget interface for the MATCH_COORD procedure. +; Given two sets of coordinates on a plane, representing the same points +; in two different reference frames supposed to be reciprocally translated +; and rotated, find the optimal transformation between the two sets and +; map one of them onto the other. +; +; CATEGORY: +; Widgets. Spatial transformations. +; +; CALLING SEQUENCE: +; XMATCH_COORD, X1, Y1, X2, Y2, X_ref, Y_ref, X2_out, Y2_out +; +; INPUTS: +; X1, Y1: Coordinates of points in reference frame no. 1 +; +; X2, Y2: Coordinates of points in reference frame no. 2 +; +; X_ref, Y_ref: Coordinates of reference points, used to find the +; optimal transformation. (X_ref, Y_ref) may be a subset of (X1, Y1). +; +; KEYWORD PARAMETERS: +; GROUP: XMatch_Coord group leader. +; +; UVALUE: XMatch_Coord user value. +; +; OUTPUTS: +; X2_out, Y2_out: Coordinates (X2, Y2) mapped onto reference frame 1. +; These coordinates may be directly compared to (X1, Y1). +; A negative scalar value indicates an error condition +; +; OPTIONAL OUTPUTS: +; ORIGIN: Set this keyword to a named variable to retrieve a 2-components +; vector, representing the estimated position of the origin of reference +; frame 2 in reference frame 1. +; +; ANGLE: Set this keyword to a named variable to retrieve a scalar, +; representing the estimated angle (in radians) between the x- axis of +; reference frame 1 and the x- axis of reference frame 2, measured +; counter-clockwise from 1 to 2. +; +; SIDE EFFECTS: +; Initiates the XMANAGER if it is not already running. +; +; RESTRICTIONS: +; The Help menu opens the file +; '/starfinder/xmatch_coord_help.txt'. +; +; PROCEDURE: +; Create and register the widget as a modal widget. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, September 1999 +; Updates: +; 1) Enhanced error handling in event-handler +; (Emiliano Diolaiti, April 2000). +;- + +; XMATCH_COORD_EVENT: XMatch_Coord event handler. + +PRO xmatch_coord_event, event + + catch, error + if error ne 0 then begin + msg = dialog_message(/ERROR, !err_string) + widget_control, event.id, SET_UVALUE = data, /NO_COPY + return + endif + widget_control, event.id, GET_UVALUE = data, /NO_COPY + event_type = strlowcase(event.tag) + case event_type of + 'match': begin + widget_control, /HOURGLASS + ; Match coordinates + widget_control, event.id, GET_VALUE = form + origin = [form.ox, form.oy] + angle = form.angle * !pi/180. + match_coord, (*data).x1, (*data).y1, (*data).x2, (*data).y2, $ + (*data).x_ref, (*data).y_ref, x2_out, y2_out, $ + ORIGIN_0 = origin, ANGLE_0 = angle, origin, angle + ; Save results + if n_elements(x2_out) eq n_elements((*data).x2) then begin + msg = dialog_message(['The parameters of the transformation are: ', $ + 'x-shift = ' + strcompress(string(origin[0])), $ + 'y-shift = ' + strcompress(string(origin[1])), $ + 'rotation angle (deg.) = ' + $ + strcompress(string(angle/!pi*180))], /INFO) + *(*data).x2_out = x2_out & *(*data).y2_out = y2_out + (*data).origin = origin & (*data).angle = angle + endif else $ + msg = dialog_message('Unsuccessful matching', /ERROR) + end + 'help': $ + xdispfile, file_name('starfinder', 'xmatch_coord_help.txt'), $ + TITLE = 'XMatch_Coord help', /MODAL + 'exit': begin + widget_control, event.id, SET_UVALUE = data, /NO_COPY + widget_control, event.top, /DESTROY + end + else: + endcase + if event_type ne 'exit' then $ + widget_control, event.id, SET_UVALUE = data, /NO_COPY + return +end + +; XMATCH_COORD_DEF: define data structure. + +FUNCTION xmatch_coord_def, x1, y1, x2, y2, x_ref, y_ref + + data = {x1: x1, y1: y1, x2: x2, y2: y2, $ + x_ref: x_ref, y_ref: y_ref, $ + x2_out: ptr_new(/ALLOCATE), y2_out: ptr_new(/ALLOCATE), $ + origin: [0., 0.], angle: 0.} + return, data +end + +; XMATCH_COORD_DEL: de-reference and de-allocate heap variables. + +PRO xmatch_coord_del, data, x2_out, y2_out, origin, angle + + if n_elements(*(*data).x2_out) ne 0 and n_elements(*(*data).y2_out) ne 0 then begin + x2_out = *(*data).x2_out & y2_out = *(*data).y2_out + endif + origin = (*data).origin & angle = (*data).angle + ptr_free, (*data).x2_out, (*data).y2_out + ptr_free, data + return +end + +; XMATCH_COORD: XMatch_Coord widget definition module. + +PRO xmatch_coord, x1, y1, x2, y2, x_ref, y_ref, x2_out, y2_out, $ + ORIGIN = origin, ANGLE = angle, $ + GROUP = group, UVALUE = uvalue + + on_error, 2 + if n_elements(x1) eq 0 or n_elements(y1) eq 0 or $ + n_elements(x2) eq 0 or n_elements(y2) eq 0 then begin + msg = dialog_message('XMatch_Coord: missing data', /ERROR) + return + endif + if n_elements(x_ref) lt 2 or n_elements(y_ref) lt 2 then begin + msg = dialog_message('XMatch_Coord: at least 2 reference ' + $ + 'points must be supplied.', /ERROR) + return + endif + ; Create group leader if necessary + if n_elements(group) eq 0 then $ + group_id = widget_base() else group_id = group + ; Create modal base + if n_elements(uvalue) eq 0 then uvalue = 0B + base = widget_base(TITLE = 'XMatch_Coord', /MODAL, UVALUE = uvalue, $ + GROUP_LEADER = group_id) + ; Define form + desc = [ $ + '0, LABEL,Initial guess:,LEFT', $ + '1, BASE,,COLUMN,FRAME', $ + '0, FLOAT,0,LABEL_LEFT=x-axis translation,TAG=ox', $ + '0, FLOAT,0,LABEL_LEFT=y-axis translation,TAG=oy', $ + '2, FLOAT,0,LABEL_LEFT=rotation angle (deg.),TAG=angle', $ + '1, BASE,,ROW', $ + '2, BUTTON,Match,NO_RELEASE,TAG=match', $ + '1, BASE,,ROW', $ + '0, BUTTON,Help,NO_RELEASE,TAG=help', $ + '2, BUTTON,Exit,QUIT,NO_RELEASE,TAG=exit'] + form = cw_form(base, desc, /COLUMN) + ; Define pointer to auxiliary/output data + data = xmatch_coord_def(x1, y1, x2, y2, x_ref, y_ref) + data = ptr_new(data, /NO_COPY) + widget_control, form, SET_UVALUE = data + ; Realize, register, etc. + widget_control, base, /REALIZE + xmanager, 'xmatch_coord', base, EVENT_HANDLER = 'xmatch_coord_event' + ; De-reference output data and de-allocate heap variables + xmatch_coord_del, data, x2_out, y2_out, origin, angle + ; Destroy group leader if necessary + if n_elements(group) eq 0 then $ + widget_control, group_id, /DESTROY + return +end diff --git a/xmatch_coord_help.txt b/xmatch_coord_help.txt new file mode 100644 index 0000000000000000000000000000000000000000..c7315a93866947c42ba9eb96885714bd5068d0c8 --- /dev/null +++ b/xmatch_coord_help.txt @@ -0,0 +1,46 @@ + XMatch_Coord help page + + + + GENERAL DESCRIPTION + + Given two lists of points, referred to reciprocally translated and + rotated reference frames on a plane (x, y), the XMatch_Coord + application estimates the relative shift and rotation and applies + an inverse transformation to the points in list 2, in order to + map them onto list 1. + The estimation is performed by means of a Newton-like algorithm. + A list of reference points, representing a sub-set of list 1, must + be supplied, along with a guess of the relative shift and rotation + between the reference frames. + + + + PARAMETERS + + 'x-axis translation': + Guess of the x-coordinate of the origin of reference frame 2 + with respect to reference frame 1. + + 'y-axis translation': + Guess of the y-coordinate of the origin of reference frame 2 + with respect to reference frame 1. + + 'rotation angle': + Guess of the angle between the x-axis of reference frame 1 + and the x-axis of reference frame 2. It must be expressed in + degrees, measured counter-clockwise from 1 to 2. + + + + CONTROLS/BUTTONS + + 'Match': + Estimate shift and rotation angle between reference frames + and apply an inverse transformation to the points in list 2. + + 'Help': + Display this help page. + + 'Exit': + Quit XMatch_Coord. \ No newline at end of file diff --git a/xnoise.pro b/xnoise.pro new file mode 100644 index 0000000000000000000000000000000000000000..d5537a1a973180edb3a096f84740e0016f1a8578 --- /dev/null +++ b/xnoise.pro @@ -0,0 +1,344 @@ +; $Id: xnoise, v 1.2 May 2014 e.d. $ +; +;+ +; NAME: +; XNOISE +; +; PURPOSE: +; Widget interface to estimate the noise standard deviation for each +; pixel of a given image. +; The overall variance is the sum of the photon noise and of the +; variances due to the following noise sources: +; 1) read-out noise +; 2) dark current +; 3) thermal background +; 4) sky +; Noise source 1) follows a gaussian distribution of 0 mean. +; Noise sources 2), 3) and 4) strictly follow a Poisson distribution, +; but since they may be often considered roughly constant over the frame +; the Poisson distribution may be approximated by a gaussian one, +; spatially uniform across the frame itself. +; While noise source 1) introduces no positive bias in the recorded signal, +; sources 2), 3) and 4) have nonzero mean. In general the mean dark +; current level, the overall thermal background and the mean sky are +; subtracted in the pre-processing phase. +; The photon noise is estimated on the basis of the recorded signal: +; for this reason, the dark current level and the thermal background +; should be removed beforehand. The gaussian noise standard deviation is +; estimated as a function of the noise parameters 1) to 4), but it may be +; optionally computed by means of the XNoise_StDev widget program. +; The simplest way to use this widget program is to estimate the gaussian +; noise in the data, by means of XNoise_StDev, without considering the +; contribution of Poisson noise: in this way no other parameters must be +; supplied on input in the widget form. The retrieved noise estimate +; roughly represents the standard deviation of the 'mean intensity level' +; in the image: in a crowded stellar field this quantity is the major +; source of noise and may be associated to the background emission due +; to nebulosities, bright stars haloes and light from faint unresolved +; sources. +; +; CATEGORY: +; Widgets. Signal processing. +; +; CALLING SEQUENCE: +; XNOISE, Image, Noise_std +; +; INPUTS: +; Image: 2D data array +; +; KEYWORD PARAMETERS: +; WNUM: Window number of an existing window used by XNoise_StDev +; when the gaussian noise standard deviation must be computed +; since no input estimate is available (see keyword STDEV). +; +; PATH: Initial path for file browsing. If the argument is a named +; variable, its value is overwritten. +; +; DEFAULT_PAR: Structure of default parameters for the widget's form. +; +; G_NOISE_PAR: Structure of default parameters to compute the gaussian +; noise standard deviation (see XNoise_StDev for details). +; +; PLOT_PAR: Structure of default parameters to plot the data histogram +; (see XNoise_StDev and XPlot for details). +; +; GROUP: XNoise group leader. +; +; UVALUE: XNoise user value. +; +; OUTPUTS: +; Noise_std: 2D array, with the same size as the input Image, containing +; the noise standard deviation for each Image pixel. +; +; OPTIONAL OUTPUTS: +; STDEV: Standard deviation of gaussian noise, computed by XNoise, +; either as a function of the input values of read-out noise, dark +; current, etc. or as computed by means of XNoise_StDev. +; +; WNUM: Window number of new graphic window, created by XNoise_StDev if +; undefined on input. +; +; DEFAULT_PAR: Set this keyword to a named variable to get the +; structure of parameters set by the widget's user. +; +; G_NOISE_PAR: Set this keyword to a named variable to get the +; structure of default parameters used by XNoise_StDev. +; +; PLOT_PAR: Set this keyword to a named variable to get the +; structure of parameters used by XPlot through XNoise_StDev. +; +; SIDE EFFECTS: +; 1) Initiates the XMANAGER if it is not already running. +; 2) Might create a new graphic window, if WNUM is undefined on input +; (see XNoise_StDev for details). +; +; RESTRICTIONS: +; The Help menu opens the file +; '/starfinder/xnoise_help.txt'. +; +; PROCEDURE: +; Create and register the widget as a modal widget. +; Then let the user define the parameters used to estimate the noise +; standard deviation. The resulting array of noise may be saved to a +; FITS file. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, October 1999 +; 1) Enhanced error handling in event-handler +; (Emiliano Diolaiti, April 2000). +; 2) Modified units of field "Sky" in GUI from ADU to electrons +; (E. D., May 2014). +;- + +; XNOISE_EVENT: XNoise event handler. + +PRO xnoise_event, event + + catch, error + if error ne 0 then begin + msg = dialog_message(/ERROR, !err_string) + widget_control, event.id, SET_UVALUE = data, /NO_COPY + return + endif + widget_control, event.id, GET_UVALUE = data, /NO_COPY + event_type = strlowcase(event.tag) + case event_type of + 'eval': begin + widget_control, event.id, GET_VALUE = form + for id = 3, 6 do $ + widget_control, (*data).ids[id], $ + SENSITIVE = (form.eval eq 0) and 1B + for id = 12, 14 do $ + widget_control, (*data).ids[id], $ + SENSITIVE = (form.eval eq 0 or form.phnoise eq 1) and 1B + end + 'phnoise': begin + widget_control, event.id, GET_VALUE = form + for id = 3, 6 do $ + widget_control, (*data).ids[id], $ + SENSITIVE = (form.eval eq 0) and 1B + for id = 12, 14 do $ + widget_control, (*data).ids[id], $ + SENSITIVE = (form.eval eq 0 or form.phnoise eq 1) and 1B + end + 'compute': begin + ; Read out form + widget_control, event.id, GET_VALUE = form + nexp = form.nexp > 1 + el_per_adu = form.el_per_adu > 0 + ron = form.ron > 0 + dark = form.dark > 0 + therm = form.therm > 0 + sky = form.sky > 0 + avg = form.avg eq 0 and 1B + norm = 1. & if avg then norm = norm/nexp + eval = form.eval eq 1 and 1B + phnoise = (form.phnoise eq 1) and 1B + ; Store parameters + (*(*data).par).nexp = form.nexp + (*(*data).par).el_per_adu = form.el_per_adu + (*(*data).par).ron = form.ron + (*(*data).par).dark = form.dark + (*(*data).par).therm = form.therm + (*(*data).par).sky = form.sky + (*(*data).par).avg = form.avg + (*(*data).par).eval = form.eval + (*(*data).par).phnoise = form.phnoise + ; Compute noise + if eval then begin + msg = dialog_message(/INFO, $ + ['Calling XNnoise_StDev to estimate gaussian noise.', $ + 'Press OK to continue.']) + xnoise_stdev, *(*data).image, stdev, WNUM = *(*data).wnum, $ + DEFAULT_PAR = *(*data).g_noise_par, $ + PLOT_PAR = *(*data).plot_par, $ + PATH = *(*data).path, GROUP = event.top + endif else $ + stdev = instr_noise(ron, dark, therm, sky, el_per_adu, nexp, AVG = avg) + if n_elements(stdev) ne 0 then *(*data).stdev = stdev else stdev = 0. + if phnoise then begin + widget_control, /HOURGLASS + phn = photon_noise(*(*data).image, el_per_adu, nexp, AVG = avg) + *(*data).noise_std = sqrt(phn^2 + stdev^2) + if not eval then msg = dialog_message(/INFO, 'Done.') + endif else $ + if n_elements(*(*data).stdev) ne 0 then begin + widget_control, /HOURGLASS + siz = size52(*(*data).image, /DIM) + *(*data).noise_std = replicate(stdev[0], siz[0], siz[1]) + if not eval then msg = dialog_message(/INFO, 'Done.') + endif + end + 'save': $ + if n_elements(*(*data).noise_std) ne 0 then begin + file = dialog_pickfile(/WRITE, FILTER = '*.fits', $ + PATH = *(*data).path, GET_PATH = path) + if file ne '' then begin + widget_control, /HOURGLASS + if strpos(file, '.fits') lt 0 then file = file + '.fits' + *(*data).path = path + writefits, file, *(*data).noise_std + endif + endif + 'help': $ + xdispfile, file_name('starfinder', 'xnoise_help.txt'), $ + TITLE = 'XNoise help', /MODAL + 'exit': begin + widget_control, event.id, SET_UVALUE = data, /NO_COPY + widget_control, event.top, /DESTROY + end + else: + endcase + if event_type ne 'exit' then $ + widget_control, event.id, SET_UVALUE = data, /NO_COPY + return +end + +; XNOISE_DEF: define XNoise data structure. + +FUNCTION xnoise_def, par, g_noise_par, plot_par, ids, image, wnum, path + + return, {par: ptr_new(par, /NO_COPY), $ + g_noise_par: ptr_new(g_noise_par, /NO_COPY), $ + plot_par: ptr_new(plot_par, /NO_COPY), $ + image: ptr_new(image, /NO_COPY), $ + wnum: ptr_new(wnum, /NO_COPY), $ + path: ptr_new(path, /NO_COPY), $ + stdev: ptr_new(/ALLOCATE), $ + noise_std: ptr_new(/ALLOCATE), $ + ids: ids} +end + +; XNOISE_DEL: de-reference XNoise data structure. + +PRO xnoise_del, data, par, g_noise_par, plot_par, image, wnum, stdev, noise_std, path + + if n_elements(*(*data).par) ne 0 then par = *(*data).par + if n_elements(*(*data).g_noise_par) ne 0 then g_noise_par = *(*data).g_noise_par + if n_elements(*(*data).plot_par) ne 0 then plot_par = *(*data).plot_par + if n_elements(*(*data).image) ne 0 then image = *(*data).image + if n_elements(*(*data).wnum) ne 0 then wnum = *(*data).wnum + if n_elements(*(*data).stdev) ne 0 then stdev = *(*data).stdev + if n_elements(*(*data).noise_std) ne 0 then noise_std = *(*data).noise_std + if n_elements(*(*data).path) ne 0 then path = *(*data).path + ptr_free, (*data).par, (*data).g_noise_par, (*data).plot_par, $ + (*data).image, (*data).wnum, (*data).stdev, $ + (*data).noise_std, (*data).path + ptr_free, data + return +end + +; XNOISE_PAR: define default parameters. + +PRO xnoise_par, id, par + + if n_elements(par) ne 0 then begin + nexp = par.nexp + el_per_adu = par.el_per_adu + ron = par.ron + dark = par.dark + therm = par.therm + sky = par.sky + avg = par.avg + eval = par.eval + phnoise = par.phnoise + endif else begin + nexp = 1 + el_per_adu = 1. + ron = 0. + dark = 0. + therm = 0. + sky = 0. + avg = 0 + eval = 1 + phnoise = 0 + par = {nexp: nexp, el_per_adu: el_per_adu, ron: ron, dark: dark, $ + therm: therm, sky: sky, avg: avg, eval: eval, phnoise: phnoise} + endelse + init = {nexp: strcompress(string(nexp), /REMOVE_ALL), $ + el_per_adu: strcompress(string(el_per_adu), /REMOVE_ALL), $ + ron: strcompress(string(ron), /REMOVE_ALL), $ + dark: strcompress(string(dark), /REMOVE_ALL), $ + therm: strcompress(string(therm), /REMOVE_ALL), $ + sky: strcompress(string(sky), /REMOVE_ALL), $ + avg: avg, eval: eval, phnoise: phnoise} + widget_control, id, SET_VALUE = init + return +end + +; XNOISE: XNoise widget definition module. + +PRO xnoise, image, noise_std, WNUM = wnum, STDEV = stdev, $ + PATH = path, DEFAULT_PAR = par, G_NOISE_PAR = g_noise_par, $ + PLOT_PAR = plot_par, GROUP = group, UVALUE = uvalue + + on_error, 2 + ; Create group leader if necessary + if n_elements(group) eq 0 then $ + group_id = widget_base() else group_id = group + ; Create modal base + if n_elements(uvalue) eq 0 then uvalue = 0B + base = widget_base(TITLE = 'XNoise', /MODAL, UVALUE = uvalue, $ + GROUP_LEADER = group_id) + ; Define form + desc = [ $ + '0, LABEL,Gaussian noise:,LEFT', $ + '1, BASE,,COLUMN,FRAME', $ + '0, BUTTON,no|yes,EXCLUSIVE,LABEL_LEFT=Evaluate from data:,TAG=eval,ROW', $ + '0, FLOAT,,LABEL_LEFT=Read-out-noise (electrons),TAG=ron,WIDTH=8', $ + '0, FLOAT,,LABEL_LEFT=Dark current (electrons),TAG=dark,WIDTH=8', $ + '0, FLOAT,,LABEL_LEFT=Thermal background (electrons),TAG=therm,WIDTH=8', $ + '2, FLOAT,,LABEL_LEFT=Sky (electrons),TAG=sky,WIDTH=8', $ + '0, LABEL,Photon noise:,LEFT', $ + '1, BASE,,COLUMN,FRAME', $ + '2, BUTTON,no|yes,EXCLUSIVE,,LABEL_LEFT=Consider photon noise,TAG=phnoise,ROW', $ + '0, LABEL,General parameters:,LEFT', $ + '1, BASE,,COLUMN,FRAME', $ + '0, INTEGER,,LABEL_LEFT=Number of exposures,TAG=nexp,WIDTH=8', $ + '0, BUTTON,mean|sum,EXCLUSIVE,,LABEL_LEFT=Exposures combined by:,TAG=avg,ROW', $ + '2, FLOAT,,LABEL_LEFT=Electrons/ADU,TAG=el_per_adu,WIDTH=8', $ + '1, BASE,,ROW', $ + '0, BUTTON,Compute,NO_RELEASE,TAG=compute', $ + '2, BUTTON,Save,NO_RELEASE,TAG=save', $ + '1, BASE,,ROW', $ + '0, BUTTON,Help,NO_RELEASE,TAG=help', $ + '2, BUTTON,Exit,QUIT,NO_RELEASE,TAG=exit'] + form = cw_form(base, desc, IDS = ids, /COLUMN) + xnoise_par, form, par + for id = 3, 6 do $ + widget_control, ids[id], SENSITIVE = (par.eval eq 0) and 1B + for id = 12, 14 do $ + widget_control, ids[id], SENSITIVE = (par.eval eq 0 or par.phnoise eq 1) and 1B + ; Define pointer to auxiliary/output data + data = ptr_new(xnoise_def(par, g_noise_par, plot_par, ids, image, wnum, path), /NO_COPY) + widget_control, form, SET_UVALUE = data + ; Realize, register, etc. + widget_control, base, /REALIZE + xmanager, 'xnoise', base, EVENT_HANDLER = 'xnoise_event' + ; De-reference output data + xnoise_del, data, par, g_noise_par, plot_par, image, wnum, stdev, noise_std, path + ; Destroy group leader if necessary + if n_elements(group) eq 0 then $ + widget_control, group_id, /DESTROY + return +end diff --git a/xnoise_help.txt b/xnoise_help.txt new file mode 100644 index 0000000000000000000000000000000000000000..5813d451b4712c8c7f1ccb7f857bd47df366912e --- /dev/null +++ b/xnoise_help.txt @@ -0,0 +1,112 @@ + XNoise help page + + + + GENERAL DESCRIPTION + + The XNoise widget computes the noise standard deviation for + each pixel of a given image. + The input image is assumed to be the sum or the mean of a + set of equally exposed frames. The output is a noise array. + The overall variance of the noise is the sum of two terms: + - Gaussian noise, due to the following sources: + 1) read-out + 2) dark current + 3) thermal background + 4) sky + - Photon noise, associated to the 'astronomical' signal. + Noise source 1) follows a gaussian distribution of 0 mean. + Noise sources 2), 3) and 4) strictly follow a Poisson + distribution, but since they may be often considered roughly + constant over the frame, the Poisson distribution can be + approximated by a gaussian one, spatially uniform across the + frame itself. While noise source 1) introduces no bias in the + recorded signal, sources 2), 3) and 4) have nonzero mean. + In general the mean dark current level, the overall thermal + background and the mean sky are subtracted in the pre- + processing phase, so their contribution to the noise cannot + be estimated from the residual signal: this is the reason + why the corresponding noise levels should be supplied by + the user. + Optionally the gaussian noise can be estimated automatically + by means of a secondary widget, named XNoise_StDev. The + result is generally over-estimated and represents the overall + effect of noise sources 1)-4) combined with the (photon) noise + associated to the diffuse emission originated by nebulosities, + bright stars haloes and diffuse light from faint unresolved + sources. + The photon noise on the 'astronomical' signal is considered + proportional to the square root of the recorded signal in each + pixel. + The XNoise widget can be used in either of the following two + 'meaningful' modes: + - compute the overall noise per pixel, considering both the + contribution of sources 1)-4) and of photon noise; + - estimate the gaussian noise only by means of XNoise_StDev: + this is the suggested mode for very crowded fields when no + knowledge on sources 1)-4) is available. + + + + PARAMETERS + + 'Evaluate from data': + Select 'yes' to estimate the gaussian noise standard + deviation by means of XNoise_StDev. + The retrieved value is generally over-estimated, especially + in very crowded fields. + + 'Read-out-noise': + Read-out-noise for a single exposure. + + 'Dark current': + Dark current level for a single exposure. If the Poisson + noise is considered and the dark current has not been + subtracted (a rather unlikely situation!), this field should + be set to 0, because the dark current noise is accounted + for as Poisson noise. + + 'Thermal background': + Instrumental background for a single eexposure. If the + Poisson noise is considered and the thermal background + has not been removed, this field should be set to 0, + because the thermal noise is accounted for as Poisson noise. + + 'Sky': + Mean sky level for a single eexposure. If the Poisson noise + is considered and the sky has not been removed, this field + should be set to 0: the associated noise is accounted for + as Poisson noise. + + 'Consider photon noise': + Select 'yes' to consider the effects of both gaussian and + photon noise. + + 'Number of exposures': + Number of equally-exposed frames which have been combined + to form the input image. + + 'Exposures combined by': + Select 'mean' or 'sum' to specify how the individual exposures + have been combined. This will affect noise propagation from + each single frame to the final image. + + 'Electrons/ADU': + Number of electrons per ADU. + + + + CONTROLS/BUTTONS + + 'Compute': + Press this button to compute the noise array using the current + set of parameters. + + 'Save': + Save to a FITS file the noise array. + + 'Help': + Display this help page. + + 'Exit': + Quit XNoise. \ No newline at end of file diff --git a/xnoise_stdev.pro b/xnoise_stdev.pro new file mode 100644 index 0000000000000000000000000000000000000000..55de5b26ddd16b9ce930dfac9c6730848e3184ce --- /dev/null +++ b/xnoise_stdev.pro @@ -0,0 +1,254 @@ +; $Id: xnoise_stdev, v 1.1 Apr 2000 e.d. $ +; +;+ +; NAME: +; XNOISE_STDEV +; +; PURPOSE: +; Widget interface for the GAUSSIAN_NOISE_STD procedure. +; Compute the standard deviation of the gaussian noise in an image. +; +; CATEGORY: +; Widgets. Signal processing. +; +; CALLING SEQUENCE: +; XNOISE_STDEV, Image, Stdev +; +; INPUTS: +; Image: 2D data array +; +; KEYWORD PARAMETERS: +; WNUM: Window number of an existing window to plot the data histogram +; and the gaussian fit. It is used only if the 'Plot' button is +; pressed. +; +; PATH: Initial path for file browsing to save the histogram plot. +; +; DEFAULT_PAR: Structure of default parameters for the widget's form. +; +; PLOT_PAR: Structure of default parameters to plot histogram. +; +; GROUP: XNoise_StDev group leader. +; +; UVALUE: XNoise_StDev user value. +; +; OUTPUTS: +; Stdev: Scalar value of noise standard deviation. +; +; OPTIONAL OUTPUTS: +; WNUM: Set this keyword to a named variable to get the number of +; the new graphic window created by XPlot to plot the histogram if +; no window is defined on input. +; +; PATH: Set this keyword to a named variables to get the path of +; the file selected by the user to save the histogram plot. +; +; DEFAULT_PAR: Set this keyword to a named variables to get the +; structure of parameters set by the widget's user. +; +; PLOT_PAR: Set this keyword to a named variables to get the +; structure of parameters used to plot the histogram (see XPlot +; for details). +; +; SIDE EFFECTS: +; 1) Initiates the XMANAGER if it is not already running. +; 2) Call WSET to activate the graphic window identified by Wnum. +; 3) Create a new graphic window if not defined on input. +; 4) Any image displayed on the window Wnum is overwritten. +; +; RESTRICTIONS: +; The Help menu opens the file +; '/starfinder/xnoise_stdev_help.txt'. +; +; PROCEDURE: +; Create and register the widget as a modal widget. +; Then let the user define and or/modify the noise estimation options and +; apply them to the input image. As a new estimate is obtained applying +; the current options a dialog message appears reporting the value of the +; estimated noise standard deviation. +; The histogram and its fit may be plotted. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, September 1999 +; Updates: +; 1) Enhanced error handling in event-handler +; (Emiliano Diolaiti, April 2000). +;- + +; XNOISE_STDEV_EVENT: XNoise_StDev event handler. + +PRO xnoise_stdev_event, event + + catch, error + if error ne 0 then begin + msg = dialog_message(/ERROR, !err_string) + widget_control, event.id, SET_UVALUE = data, /NO_COPY + return + endif + widget_control, event.id, GET_UVALUE = data, /NO_COPY + event_type = strlowcase(event.tag) + case event_type of + 'process': begin + widget_control, /HOURGLASS + widget_control, event.id, GET_VALUE = form + patch = form.patch + frac = form.frac > 0 & if frac ne 0 then frac = 1/frac + n_std = form.n_std + hminsize = form.hmin + hmaxsize = form.hmax + nterms = 3 + form.nterms + (*(*data).par).patch = form.patch + (*(*data).par).frac = form.frac + (*(*data).par).n_std = form.n_std + (*(*data).par).hmin = form.hmin + (*(*data).par).hmax = form.hmax + (*(*data).par).nterms = form.nterms + gauss_noise_std, *(*data).image, PATCH = patch, POINT_FRAC = frac, $ + N_STD = n_std, HIST_MINSIZE = hminsize, HIST_MAXSIZE = hmaxsize, $ + NTERMS = nterms, mode, stdev, h, v, vm, hfit, COEFF = c + if stdev ge 0 then begin + *(*data).stdev = stdev + *(*data).h = h & *(*data).hfit = hfit & *(*data).v = v + *(*data).b = 0 + if nterms gt 3 then *(*data).b = replicate(c[3], n_elements(v)) + if nterms gt 4 then *(*data).b = *(*data).b + c[4] * v + if nterms gt 5 then *(*data).b = *(*data).b + c[5] * v^2 + msg = dialog_message(/INFO, ['Mode = ', strcompress(string(mode)), '', $ + 'Standard deviation = ', strcompress(string(stdev))]) + endif else $ + msg = dialog_message(/INFO, ['Computation failed.', $ + 'Try with a different fitting model.']) + end + 'plot': begin + if n_elements(*(*data).h) ne 0 then begin + if n_elements(*(*data).b) gt 1 then b = *(*data).b + xplot, *(*data).h, *(*data).v, OVERPLOT1 = *(*data).hfit, $ + OVERPLOT2 = b, WNUM = *(*data).wnum, PATH = *(*data).path, $ + DEFAULT_PAR = *(*data).plot_par, GROUP = event.top + endif + end + 'help': $ + xdispfile, file_name('starfinder', 'xnoise_stdev_help.txt'), $ + TITLE = 'XNoise_StDev help', /MODAL + 'exit': begin + widget_control, event.id, SET_UVALUE = data, /NO_COPY + widget_control, event.top, /DESTROY + end + else: + endcase + if event_type ne 'exit' then $ + widget_control, event.id, SET_UVALUE = data, /NO_COPY + return +end + +; XNOISE_STDEV_DEF: define XNoise_StDev data structure. + +FUNCTION xnoise_stdev_def, par, plot_par, image, wnum, path + + return, {par: ptr_new(par, /NO_COPY), $ + plot_par: ptr_new(plot_par, /NO_COPY), $ + image: ptr_new(image, /NO_COPY), $ + wnum: ptr_new(wnum, /NO_COPY), $ + path: ptr_new(path, /NO_COPY), $ + stdev: ptr_new(/ALLOCATE), $ + h: ptr_new(/ALLOCATE), hfit: ptr_new(/ALLOCATE), $ + v: ptr_new(/ALLOCATE), b: ptr_new(/ALLOCATE)} +end + +; XNOISE_STDEV_DEL: de-reference XNoise_StDev data structure. + +PRO xnoise_stdev_del, data, par, plot_par, image, wnum, stdev, path + + if n_elements(*(*data).par) ne 0 then par = *(*data).par + if n_elements(*(*data).plot_par) ne 0 then plot_par = *(*data).plot_par + if n_elements(*(*data).image) ne 0 then image = *(*data).image + if n_elements(*(*data).wnum) ne 0 then wnum = *(*data).wnum + if n_elements(*(*data).stdev) ne 0 then stdev = *(*data).stdev + if n_elements(*(*data).path) ne 0 then path = *(*data).path + ptr_free, (*data).par, (*data).plot_par, (*data).image, (*data).wnum, $ + (*data).stdev, (*data).path, (*data).h, (*data).hfit, (*data).v, (*data).b + ptr_free, data + return +end + +; XNOISE_STDEV_PAR: define default parameters. + +PRO xnoise_stdev_par, id, par + + if n_elements(par) ne 0 then begin + patch = par.patch + frac = par.frac + n_std = par.n_std + hmin = par.hmin + hmax = par.hmax + nterms = par.nterms + endif else begin + patch = 3 + frac = 1. + n_std = 3 + hmin = 5 + hmax = 5 + nterms = 0 + par = {patch: patch, frac: frac, n_std: n_std, $ + hmin: hmin, hmax: hmax, nterms: nterms} + endelse + init = {patch: strcompress(string(patch), /REMOVE_ALL), $ + frac: strcompress(string(frac), /REMOVE_ALL), $ + n_std: strcompress(string(n_std), /REMOVE_ALL), $ + hmin: strcompress(string(hmin), /REMOVE_ALL), $ + hmax: strcompress(string(hmax), /REMOVE_ALL), $ + nterms: nterms} + widget_control, id, SET_VALUE = init + return +end + +; XNOISE_STDEV: XNoise_StDev widget definition module. + +PRO xnoise_stdev, image, stdev, WNUM = wnum, PATH = path, $ + DEFAULT_PAR = par, PLOT_PAR = plot_par, $ + GROUP = group, UVALUE = uvalue + + on_error, 2 + ; Create group leader if necessary + if n_elements(group) eq 0 then $ + group_id = widget_base() else group_id = group + ; Create modal base + if n_elements(uvalue) eq 0 then uvalue = 0B + base = widget_base(TITLE = 'XNoise_StDev', /MODAL, UVALUE = uvalue, $ + GROUP_LEADER = group_id) + ; Define form + desc = [ $ + '0, LABEL,Pre-processing:,LEFT', $ + '1, BASE,,COLUMN,FRAME', $ + '0, INTEGER,,LABEL_LEFT=Patch size for median subtraction,TAG=patch', $ + '0, FLOAT,,LABEL_LEFT=Fraction of data point to use,TAG=frac', $ + '2, FLOAT,,LABEL_LEFT=Threshold to reject out-liers (St. Dev. units),TAG=n_std', $ + '0, LABEL,Histogram computation:,LEFT', $ + '1, BASE,,COLUMN,FRAME', $ + '0, FLOAT,,LABEL_LEFT=Minimum number of bins in one HWHM,TAG=hmin', $ + '2, FLOAT,,LABEL_LEFT=Size of histogram (FWHM units),TAG=hmax', $ + '0, LABEL,Histogram fitting:,LEFT', $ + '1, BASE,,COLUMN,FRAME', $ + '2, BUTTON,gaussian|gaussian + costant|gaussian + linear|gaussian + quadratic,' + $ + 'EXCLUSIVE,TAG=nterms,COLUMN', $ + '1, BASE,,ROW', $ + '0, BUTTON,Processing...,NO_RELEASE,TAG=process', $ + '2, BUTTON,Plot histogram,NO_RELEASE,TAG=plot', $ + '1, BASE,,ROW', $ + '0, BUTTON,Help,NO_RELEASE,TAG=help', $ + '2, BUTTON,Exit,QUIT,NO_RELEASE,TAG=exit'] + form = cw_form(base, desc, /COLUMN) + xnoise_stdev_par, form, par + ; Define pointer to auxiliary/output data + data = ptr_new(xnoise_stdev_def(par, plot_par, image, wnum, path), /NO_COPY) + widget_control, form, SET_UVALUE = data + ; Realize, register, etc. + widget_control, base, /REALIZE + xmanager, 'xnoise_stdev', base, EVENT_HANDLER = 'xnoise_stdev_event' + ; De-reference output data + xnoise_stdev_del, data, par, plot_par, image, wnum, stdev, path + ; Destroy group leader if necessary + if n_elements(group) eq 0 then $ + widget_control, group_id, /DESTROY + return +end diff --git a/xnoise_stdev_help.txt b/xnoise_stdev_help.txt new file mode 100644 index 0000000000000000000000000000000000000000..bcadb628b2406678e5190d8ef45e7eead14b7575 --- /dev/null +++ b/xnoise_stdev_help.txt @@ -0,0 +1,70 @@ + XNoise_StDev help page + + + + GENERAL DESCRIPTION + + The XNoise_StDev widget computes an estimate of the + gaussian noise standard deviation in a given image; the + estimator is the standard deviation of the best fit gaussian + to the data histogram. Before computing the histogram, the + input image is median-subtracted, in order to remove the + signal, leaving only pure noise. + + + + PARAMETERS + + 'Patch size for median subtraction': + Integer size of box for median smoothing of the image. + The median is subtracted from the image, in order to + remove the signal from each pixel. + + 'Fraction of data points to use': + To speed up the computation, a value < 1 may be + selected. In this case a sub-set of pixels is extracted + from the input data. + + 'Threshold to reject out-liers': + Before computing the histogram, the so-called out-liers + (pixels whose value is very different from the median or + the mean of the data) are rejected. + In practice the median of the data and the standard + deviation from the median are computed: the out-liers are + identified as those pixels whose absolute distance from + the median is larger than a multiple of the standard + deviation. + + 'Minimum number of bins in one HWHM': + The histogram bin is optimized in order to have a minimum + number of elements in the histogram width. This will + improve the accuracy of the fitting. + + 'Size of histogram': + Specify the size of the 'useful' part of the histogram + around the mode, which will be used for fitting. + + 'gaussian' or 'gaussian + constant', etc.: + Select suitable model for histogram fitting. + + + + CONTROLS/BUTTONS + + 'Processing...': + Compute and fit the histogram using the currently defined + parameters. At the end of the computation, a message + appears reporting the estimated standard deviation and + the histogram mode, which should be as close as possible + to 0. + + 'Plot histogram': + Call the XPlot widget to plot the histogram and the best + fit model. If any of the 'gaussian + background term' + models is used, the background itself is shown in the plot. + + 'Help': + Display this help page. + + 'Exit': + Quit XNoise_StDev. \ No newline at end of file diff --git a/xplot.pro b/xplot.pro new file mode 100644 index 0000000000000000000000000000000000000000..0f88d288c7a88a829fdc1de26e14cfb8f44cc0da --- /dev/null +++ b/xplot.pro @@ -0,0 +1,309 @@ +; $Id: xplot, v 1.1 Apr 2000 e.d. $ +; +;+ +; NAME: +; XPLOT +; +; PURPOSE: +; Widget interface to execute simple plots. +; +; CATEGORY: +; Widgets. Graphics. +; +; CALLING SEQUENCE: +; XPLOT, Y, X +; +; INPUTS: +; Y: 1D vector of ordinates to plot. +; +; OPTIONAL INPUTS: +; X: 1D vector of abscissae. The default is +; X = [0, 1, ..., N - 1], where N is the number of elements in Y. +; +; KEYWORD PARAMETERS: +; OVERPLOT1: 1D vector of additional values to plot over Y. +; +; OVERPLOT2: 1D vector of additional values to plot over Y. +; +; WNUM: Window number of an existing window. +; +; PATH: Initial path for file browsing to save plot. +; +; DEFAULT_PAR: Structure of default parameters for the widget's form. +; +; GROUP: XPlot group leader. +; +; UVALUE: XPlot user value. +; +; OUTPUTS: +; No particular output, except the plot which may be saved on a file. +; +; OPTIONAL OUTPUTS: +; WNUM: Set this keyword to a named variable to get the window number +; of the new created window, if not existing on input. +; +; DEFAULT_PAR: Set this keyword to a named variable to get the +; structure of parameters defined by the widget's user. +; +; PATH: Set this keyword to a named variable to get the path of the +; file selected by the user to save the plot. +; +; SIDE EFFECTS: +; 1) Initiates the XMANAGER if it is not already running. +; 2) Call WSET to activate the graphic window identified by Wnum. +; 3) Create a new graphic window if not defined on input. +; 4) Any previous display on the window Wnum is overwritten. +; +; RESTRICTIONS: +; 1) The Help menu opens the file +; '/starfinder/xplot_help.txt'. +; 2) The plot may be saved only as a GIF file. +; +; PROCEDURE: +; Create and register the widget as a modal widget. Then let the user +; define and or/modify the plotting options and apply them. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, September 1999. +; Updates: +; 1) Changed output from PS to GIF (Emiliano Diolaiti, Apr 2000). +; 2) Enhanced error handling in event-handler +; (Emiliano Diolaiti, April 2000). +;- + +; XPLOT_LP: determine line and point style. + +PRO xplot_lp, type, line, psym + + case type of + 0: line = 0 + 1: line = 2 + 2: line = 1 + 3: psym = 10 + 4: psym = 3 + 5: psym = 1 + endcase + return +end + +; XPLOT_DO: plot data for XPlot. + +PRO xplot_do, x, y, y1, y2, psym1, line1, psym2, line2, $ + xmin, xmax, ymin, ymax, title, xtitle, ytitle, $ + chsize, symsize + + on_error, 2 + font = !P.Font & !P.Font = -1 + plot, x, y, PSYM = psym1, LINE = line1, $ + XRANGE = [xmin, xmax], YRANGE = [ymin, ymax], $ + TITLE = title, XTITLE = xtitle, YTITLE = ytitle, $ + CHARSIZE = chsize, SYMSIZE = symsize + if n_elements(y1) gt 1 then oplot, x, y1, PSYM = psym2, LINE = line2 + if n_elements(y2) gt 1 then oplot, x, y2, PSYM = psym2, LINE = line2 + !P.Font = font + return +end + +; XPLOT_EVENT: XPlot event handler. + +PRO xplot_event, event + + catch, error + if error ne 0 then begin + msg = dialog_message(/ERROR, !err_string) + widget_control, event.id, SET_UVALUE = data, /NO_COPY + return + endif + widget_control, event.id, GET_UVALUE = data, /NO_COPY + event_type = strlowcase(event.tag) + case event_type of + 'process': begin + widget_control, /HOURGLASS + widget_control, event.id, GET_VALUE = form + xmin = form.xmin & xmax = form.xmax + ymin = form.ymin & ymax = form.ymax + title = form.title & xtitle = form.xtitle & ytitle = form.ytitle + chsize = form.chsize > 0 & symsize = form.symsize > 0 + type1 = form.type1 & type2 = form.type2 + xplot_lp, type1, line1, psym1 + if n_elements(*(*data).y1) ne 0 or n_elements(*(*data).y2) ne 0 then $ + xplot_lp, type2, line2, psym2 + *(*data).par = {xmin: xmin, xmax: xmax, ymin: ymin, ymax: ymax, $ + chsize: chsize, symsize: symsize, title: title, $ + xtitle: xtitle, ytitle: ytitle, type1: type1, type2: type2} + if n_elements(*(*data).wnum) eq 0 then begin + window, /FREE & *(*data).wnum = !D.window + endif else wset, *(*data).wnum + xplot_do, *(*data).x, *(*data).y, *(*data).y1, *(*data).y2, $ + psym1, line1, psym2, line2, xmin, xmax, ymin, ymax, $ + title, xtitle, ytitle, chsize, symsize + end + 'save': begin + if n_elements(*(*data).wnum) ne 0 and n_elements(*(*data).par) ne 0 then begin + file = dialog_pickfile(/WRITE, FILTER = '*.gif', $ + PATH = *(*data).path, GET_PATH = path) + if file ne '' then begin + widget_control, /HOURGLASS + if strpos(file, '.gif') lt 0 then file = file + '.gif' + *(*data).path = path + write_gif, file, bytscl(255 - tvrd()) + endif + endif + end + 'help': $ + xdispfile, file_name('starfinder', 'xplot_help.txt'), $ + TITLE = 'XPlot help', /MODAL + 'exit': begin + widget_control, event.id, SET_UVALUE = data, /NO_COPY + widget_control, event.top, /DESTROY + end + else: + endcase + if event_type ne 'exit' then $ + widget_control, event.id, SET_UVALUE = data, /NO_COPY + return +end + +; XPLOT_DEF: define XPlot data structure. + +FUNCTION xplot_def, par, x, y, y1, y2, wnum, path + + return, {par: ptr_new(par, /NO_COPY), $ + x: ptr_new(x, /NO_COPY), y: ptr_new(y, /NO_COPY), $ + y1: ptr_new(y1, /NO_COPY), y2: ptr_new(y2, /NO_COPY), $ + wnum: ptr_new(wnum, /NO_COPY), path: ptr_new(path, /NO_COPY)} +end + +; XPLOT_DEL: de-reference XPlot data structure. + +PRO xplot_del, data, par, x, y, y1, y2, wnum, path + + if n_elements(*(*data).par) ne 0 then par = *(*data).par + if n_elements(*(*data).x) ne 0 then x = *(*data).x + if n_elements(*(*data).y) ne 0 then y = *(*data).y + if n_elements(*(*data).y1) ne 0 then y1 = *(*data).y1 + if n_elements(*(*data).y2) ne 0 then y2 = *(*data).y2 + if n_elements(*(*data).wnum) ne 0 then wnum = *(*data).wnum + if n_elements(*(*data).path) ne 0 then path = *(*data).path + ptr_free, (*data).par, (*data).x, (*data).y, (*data).y1, (*data).y2, $ + (*data).wnum, (*data).path + ptr_free, data + return +end + +; XPLOT_PAR: define default parameters. + +PRO xplot_par, id, par, y, x + + if n_elements(par) ne 0 then begin + xmin = par.xmin + xmax = par.xmax + ymin = par.ymin + ymax = par.ymax + chsize = par.chsize + symsize = par.symsize + title = par.title + xtitle = par.xtitle + ytitle = par.ytitle + type1 = par.type1 + type2 = par.type2 + endif else begin + xmin = min(x) + xmax = max(x) + ymin = min(y) + ymax = max(y) + chsize = 1. + symsize = 1. + title = '' + xtitle = '' + ytitle = '' + type1 = 0 + type2 = 1 + par = {xmin: xmin, xmax: xmax, ymin: ymin, ymax: ymax, $ + chsize: chsize, symsize: symsize, title: title, $ + xtitle: xtitle, ytitle: ytitle, type1: type1, type2: type2} + endelse + init = {xmin: strcompress(string(xmin), /REMOVE_ALL), $ + xmax: strcompress(string(xmax), /REMOVE_ALL), $ + ymin: strcompress(string(ymin), /REMOVE_ALL), $ + ymax: strcompress(string(ymax), /REMOVE_ALL), $ + type1: type1, type2: type2, $ + chsize: strcompress(string(chsize), /REMOVE_ALL), $ + symsize: strcompress(string(symsize), /REMOVE_ALL), $ + title: title, xtitle: xtitle, ytitle: ytitle} + widget_control, id, SET_VALUE = init + return +end + +; XPLOT: XPlot widget definition module. + +PRO xplot, y, x, OVERPLOT1 = y1, OVERPLOT2 = y2, WNUM = wnum, $ + PATH = path, DEFAULT_PAR = par, GROUP = group, UVALUE = uvalue + + on_error, 2 + if n_elements(y) eq 0 then begin + msg = dialog_message(/ERROR, 'XPlot: missing data.') + return + endif + if n_elements(x) eq 0 then x = findgen(n_elements(y)) + ; Create group leader if necessary + if n_elements(group) eq 0 then $ + group_id = widget_base() else group_id = group + ; Create modal base + if n_elements(uvalue) eq 0 then uvalue = 0B + base = widget_base(TITLE = 'XPlot', /MODAL, UVALUE = uvalue, $ + GROUP_LEADER = group_id) + ; Define form + desc = [ $ + '0, LABEL,Range:,LEFT', $ + '1, BASE,,COLUMN,FRAME', $ + '1, BASE,,ROW', $ + '0, FLOAT,,LABEL_LEFT=X lower,WIDTH=12,TAG=xmin', $ + '2, FLOAT,,LABEL_LEFT=X upper,WIDTH=12,TAG=xmax', $ + '1, BASE,,ROW', $ + '0, FLOAT,,LABEL_LEFT=Y lower,WIDTH=12,TAG=ymin', $ + '2, FLOAT,,LABEL_LEFT=Y upper,WIDTH=12,TAG=ymax', $ + '2, BASE,,', $ + '0, LABEL,Labels:,LEFT', $ + '1, BASE,,COLUMN,FRAME', $ + '0, TEXT,,LABEL_LEFT=Main title,WIDTH=12,TAG=title', $ + '0, TEXT,,LABEL_LEFT=X-title,WIDTH=12,TAG=xtitle', $ + '2, TEXT,,LABEL_LEFT=Y-title,WIDTH=12,TAG=ytitle', $ + '0, LABEL,Type:,LEFT', $ + '1, BASE,,ROW,FRAME', $ + '1, BASE,,COLUMN', $ + '0, LABEL,Main plot:,LEFT', $ + '2, BUTTON,Continuous line|Dashed line|Dotted line|Steps|Dot|Plus sign,' + $ + 'EXCLUSIVE,SET_VALUE=0,COLUMN,NO_RELEASE,TAG=type1', $ + '1, BASE,,COLUMN', $ + '0, LABEL,Overplot:,LEFT', $ + '2, BUTTON,Continuous line|Dashed line|Dotted line|Steps|Dot|Plus sign,' + $ + 'EXCLUSIVE,SET_VALUE=2,COLUMN,NO_RELEASE,TAG=type2', $ + '2, BASE,,', $ + '0, LABEL,Style:,LEFT', $ + '1, BASE,,COLUMN,FRAME', $ + '0, FLOAT,,LABEL_LEFT=Character size,WIDTH=8,TAG=chsize', $ + '2, FLOAT,,LABEL_LEFT=Symbol size,WIDTH=8,TAG=symsize', $ + '1, BASE,,ROW', $ + '2, BUTTON,Plot,NO_RELEASE,TAG=process', $ + '1, BASE,,ROW', $ + '0, BUTTON,Save,NO_RELEASE,TAG=save', $ + '0, BUTTON,Help,NO_RELEASE,TAG=help', $ + '2, BUTTON,Exit,QUIT,NO_RELEASE,TAG=exit'] + form = cw_form(base, desc, IDS = ids, /COLUMN) + xplot_par, form, par, y, x + widget_control, ids[21], $ + SENSITIVE = (n_elements(y1) ne 0 or n_elements(y2) ne 0) and 1B + ; Define pointer to auxiliary/output data + data = ptr_new(xplot_def(par, x, y, y1, y2, wnum, path), /NO_COPY) + widget_control, form, SET_UVALUE = data + ; Realize, register, etc. + widget_control, base, /REALIZE + xmanager, 'xplot', base, EVENT_HANDLER = 'xplot_event' + ; De-reference output data + xplot_del, data, par, x, y, y1, y2, wnum, path + ; Destroy group leader if necessary + if n_elements(group) eq 0 then $ + widget_control, group_id, /DESTROY + return +end diff --git a/xplot_help.txt b/xplot_help.txt new file mode 100644 index 0000000000000000000000000000000000000000..c4c7b9edc07179705acf18f72933121f04aabd6b --- /dev/null +++ b/xplot_help.txt @@ -0,0 +1,60 @@ + XPlot help page + + + + GENERAL DESCRIPTION + + The XPlot widget plots a vector Y (vertical axis) vs. a vector + X (horizontal axis). It can optionally overplot up to two vectors + Y1 and Y2. + + + + PARAMETERS + + 'X lower', 'X upper': + Lower and upper values for horizontal axis. Notice that + X_upper can be smaller than X_upper. This is useful to + reverse the X-axis scale. + + 'Y lower', 'Y upper': + Lower and upper values for vertical axis. Notice that + Y_upper can be smaller than Y_upper. This is useful to + reverse the Y-axis scale. + + 'Main title': + Title of plot. + + 'X-title': + Label for X-axis. + + 'Y-title': + Label for Y-axis. + + 'Type of Main plot': + Select draw style (line style or points). + + 'Type of Overplot': + Select draw style for overplot(s). + + 'Character size': + Character size of title and labels. + + 'Symbol size': + Size of symbol, when draw style is 'Plus sign' (+). + + + + CONTROLS/BUTTONS + + 'Plot': + Execute plot using currently defined options. + + 'Save': + Save plot as PostScript file. + + 'Help': + Display this help page. + + 'Exit': + Quit XPlot. \ No newline at end of file diff --git a/xpsf_extract.pro b/xpsf_extract.pro new file mode 100644 index 0000000000000000000000000000000000000000..8a924902bcfce63a42afb1658be1b64598f15931 --- /dev/null +++ b/xpsf_extract.pro @@ -0,0 +1,562 @@ +; $Id: xpsf_extract, v 1.7 Apr 2012 e.d. $ +; +;+ +; NAME: +; XPSF_EXTRACT +; +; PURPOSE: +; Widget interface for the PSF_EXTRACT procedure. +; Given a stellar field image, extract an estimate of the PSF +; by combination of a set of stars selected by the user. +; +; CATEGORY: +; Widgets. Signal processing. +; +; CALLING SEQUENCE: +; XPSF_EXTRACT, Image, Rep, Psf, Psf_fwhm, X, Y, Background, Back_Box +; +; INPUTS: +; Image: Stellar field +; +; Rep: binary flag; 0 means that this is the first call to XPsf_Extract. +; When the flag is different from 0, the user is not prompted to select +; the secondary sources around the candidate PSF stars. +; +; KEYWORD PARAMETERS: +; DISPLAYIMAGE: Structure of current image display options; +; the structure must be defined as in DEFAULT_DISPLAY_OPT. +; +; DISPLAYPSF: Structure of current PSF display options; +; the structure must be defined as in DEFAULT_DISPLAY_OPT. +; +; PATH: Initial path for file browsing when saving reference stars. +; If the argument of the keyword is a named variable, its value +; is overwritten. +; +; DEFAULT_PAR: Structure of default parameters for the widget's form. +; +; GROUP: XPsf_Extract group leader. +; +; UVALUE: XPsf_Extract user value. +; +; OUTPUTS: +; Image: Same as input Image if no saturated stars are present. +; Otherwise it is the input Image with corrected saturated stars. +; +; Psf: PSF estimate. The size must be specified by the user filling +; the interactive form +; +; Psf_fwhm: FWHM of output PSF +; +; X, Y: Coordinates of 'PSF stars' +; +; Background: 2D array, with the same size as Image, containing an estimate +; of the background emission +; +; Back_Box: Size of box used for estimation of the Background array (see also +; the file IMAGE_BACKGROUND.PRO) +; +; OPTIONAL OUTPUTS: +; IMADISPLAYOPT: Set this keyword to a named variable to get +; the structure of image display options, as defined and/or +; modified by XPSF_EXTRACT. +; +; PSFDISPLAYOPT: Set this keyword to a named variable to get +; the structure of PSF display options, as defined and/or +; modified by XPSF_EXTRACT. +; +; DEFAULT_PAR: Set this keyword to a named variable to get the +; structure of parameters set by the widget's user. +; +; SIDE EFFECTS: +; Initiates the XMANAGER if it is not already running. +; +; RESTRICTIONS: +; The Help menu opens the file +; '/starfinder/xpsf_extract_help.txt'. +; +; PROCEDURE: +; Create and register the widget as a modal widget. +; Then let the user define and or/modify the PSF extraction options and apply +; them to the input image. As a PSF estimate is obtained applying the current +; options, it is displayed on the graphic window. Then the user may exit or +; repeat the extraction procedure with different parameters. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, September 1999 +; Updates: +; 1) Enhanced error handling in event-handler +; (Emiliano Diolaiti, April 2000). +; 2) Removed call to obsolete routine APPEND_ELEMENTS +; (Emiliano Diolaiti, June 2001). +; 3) Modified widget parameters +; (Emiliano Diolaiti, September 2001). +; 4) Renamed keywords IMAGE_DISPLAY_OPT and PSF_DISPLAY_OPT +; (Emiliano Diolaiti, September 2001). +; 5) Added parameter Back_Box (E. D., August 2004). +; 6) Skip 'secondary stars selection' step (E. D., August 2004). +; 7) Restored 'secondary stars selection' step (E. D., January 2006). +; 8) Added binary flag Rep (see INPUTS) (E. D., February 2006). +; 9) Three options for average type: mean, min and median (E. D., July 2006). +; 10) Size of fitting box to remove secondary sources disabled after first +; call to XPSF_EXTRACT (E. D., April 2012). +; 11) Added 'Sources Selection' option in the GUI. A list of PSF stars can be upload +; from a file (L. S., May 2021) +;- + +; XPSF_EXTRACT_GET_PRINCIPAL: auxiliary routine to select 'PSF stars'. + +PRO xpsf_extract_get_principal, image, wnum, display_opt, upper_lev, psf_file, $;----------- + x_in, y_in, x, y, same_stars + + on_error, 2 + same_stars = 0B + + if n_elements(x_in) ne 0 and n_elements(y_in) ne 0 then begin + if psf_file eq 1 then begin ;----------------------------------------- + msg = dialog_message('Do you want to use the same stars as before?', /QUESTION) + if strlowcase(msg) eq 'yes' then begin + x = x_in & y = y_in & same_stars = 1B & return + endif +;----------------------------------------- + endif else begin + x = x_in & y = y_in & return + endelse +;----------------------------------------- + endif + ; Select stars + display_image, image, wnum, OPTIONS = display_opt + id = dialog_message(['Select the stars to form the PSF.', '', $ + 'Use the left button of your mouse; ' + $ + 'push the right button to exit.'], /INFO) + if n_elements(upper_lev) ne 0 then upper = upper_lev + click_on_max, image, /MARK, /SILENT, UPPER = upper, $ + SYMSIZE = 3, /DARK, x_click, y_click + nstars = n_elements(x_click) + if nstars eq 0 then return + ; Is there at least one unsaturated star? + if n_elements(upper_lev) ne 0 then begin + w = where(image[x_click, y_click] lt upper_lev, count) + if count eq 0 then begin + id = dialog_message(/ERROR, 'Please select at least one unsaturated star.') + return + endif + endif + ; Sort them in order of decreasing intensity + x = x_click & y = y_click + if nstars ne 0 then begin + sorted = reverse(sort(image[x, y])) & x = x[sorted] & y = y[sorted] + endif + return +end + +; XPSF_EXTRACT_CONFIRM: auxiliary routine to select secondary sources. + +PRO xpsf_extract_confirm, image, wnum, display_opt, x_in, y_in, $ + same_stars, psfsize, x_out, y_out + + on_error, 2 + if n_elements(x_in) eq 0 or n_elements(y_in) eq 0 then return + if same_stars then begin + x_out = x_in & y_out = y_in & return + endif + sub_arrays, image, x_in, y_in, psfsize, stack + nstars = n_elements(x_in) + for n = 0L, nstars - 1 do begin + xn = -1 & yn = -1 + opt = default_display_opt(stack[*,*,n]) + opt.reverse = display_opt.reverse + opt.stretch = display_opt.stretch + opt.color_table = display_opt.color_table + display_image, stack[*,*,n], wnum, OPTIONS = opt + msg = dialog_message('Confirm this star?', /QUESTION) + if strlowcase(msg) eq 'no' then begin + x_in[n] = -1 & y_in[n] = -1 + endif + endfor + w = where(x_in ge 0 and y_in ge 0, n_confirm) + if n_confirm ne 0 then begin + x_out = x_in[w] & y_out = y_in[w] + endif + display_image, image, wnum, OPTIONS = display_opt + return +end + +; XPSF_EXTRACT_GET_SECONDARY: auxiliary routine to select secondary sources. + +PRO xpsf_extract_get_secondary, image, wnum, display_opt, x, y, $ + same_stars, psfsize, x2_in, y2_in, x2, y2 + + on_error, 2 + if n_elements(x) eq 0 or n_elements(y) eq 0 then return + msg = dialog_message(['Do you want to select and subtract the ', $ + 'secondary sources around the selected stars?'], /QUESTION) + if strlowcase(msg) eq 'no' then return + if same_stars and n_elements(x2_in) ne 0 and n_elements(y2_in) ne 0 then begin + msg = dialog_message('Select the same secondary sources as before?', /QUESTION) + if strlowcase(msg) eq 'yes' then begin + x2 = x2_in & y2 = y2_in & return + endif + endif + sub_arrays, image, x, y, psfsize, stack + nstars = n_elements(x) + for n = 0L, nstars - 1 do begin + xn = -1 & yn = -1 + opt = default_display_opt(stack[*,*,n]) + opt.reverse = display_opt.reverse + opt.stretch = display_opt.stretch + opt.color_table = display_opt.color_table + display_image, stack[*,*,n], wnum, OPTIONS = opt + msg = dialog_message(['Select the main secondary sources around ' + $ + 'the displayed star.', '', 'Use the left ' + $ + 'button of your mouse; push the right button ' + $ + 'to exit.'], /INFO) + click_on_max, stack[*,*,n], /MARK, /SILENT, $ + BOXSIZE = 3, SYMSIZE = 3, xn, yn + if xn[0] ne -1 and yn[0] ne -1 then begin + xn = xn + x[n] - psfsize/2 & yn = yn + y[n] - psfsize/2 + if n_elements(x2) eq 0 then begin + x2 = xn & y2 = yn + endif else begin + x2 = [x2, xn] + y2 = [y2, yn] + endelse + endif + endfor + ; Sort secondary stars in order of decreasing intensity + if n_elements(x2) ne 0 then begin + sorted = reverse(sort(image[x2, y2])) + x2 = x2[sorted] & y2 = y2[sorted] + endif + ; Restore previous display + display_image, image, wnum, OPTIONS = display_opt + return +end + +; XPSF_EXTRACT_EVENT: XPsf_Extract event handler. + +PRO xpsf_extract_event, event + + catch, error + if error ne 0 then begin + msg = dialog_message(/ERROR, !err_string) + widget_control, event.id, SET_UVALUE = data, /NO_COPY + return + endif + widget_control, event.id, GET_UVALUE = data, /NO_COPY + event_type = strlowcase(event.tag) + case event_type of + 'upper_lev': begin + widget_control, event.id, GET_VALUE = form + image_max = max(*(*data).image) + for id = 11, 13 do $ + widget_control, (*data).ids[id], $ + SENSITIVE = form.upper_lev le image_max and 1B + end + 'process': begin + widget_control, event.id, GET_VALUE = form + if form.psf_size ne 0 then begin + ; Define input parameters of PSF_EXTRACT + psf_size = form.psf_size + back_box = form.back_box + n_fwhm_fit = form.n_fwhm_fit + norm_rad = form.norm_rad + avg = form.avg +;----------------------------------------- + psf_file = form.psf_file +;----------------------------------------- + satur = form.upper_lev le max(*(*data).image) + if satur then upper_lev = form.upper_lev + n_fwhm_match = form.n_fwhm_match + n_width = form.n_width + mag_fac = form.mag_fac + ; Save parameters + (*data).par.psf_size = psf_size + (*data).par.back_box = back_box + (*data).back_box = back_box + (*data).par.n_fwhm_fit = n_fwhm_fit + (*data).par.norm_rad = norm_rad + (*data).par.avg = avg +;----------------------------------------- + (*data).par.psf_file = psf_file +;----------------------------------------- + (*data).par.upper_lev = form.upper_lev + (*data).par.n_fwhm_match = n_fwhm_match + (*data).par.n_width = n_width + (*data).par.mag_fac = mag_fac +;----------------------------------------- + if psf_file eq 0 then begin + file = dialog_pickfile(/READ, PATH = *(*data).path, GET_PATH = path) + xyf = read_float_data(file, 2) + x_input = transpose(xyf[0,*]) + y_input = transpose(xyf[1,*]) + *(*data).x = x_input & *(*data).y = y_input + endif +;----------------------------------------- + ; Select 'PSF stars' + xpsf_extract_get_principal, *(*data).image, (*data).wnum, *(*data).ima_disp, $ + upper_lev, psf_file, *(*data).x, *(*data).y, x_0, y_0, same_stars + xpsf_extract_confirm, *(*data).image, (*data).wnum, *(*data).ima_disp, $ + x_0, y_0, same_stars, psf_size, x, y + if (*data).rep eq 0 then $ + xpsf_extract_get_secondary, *(*data).image, (*data).wnum, *(*data).ima_disp, $ + x, y, same_stars, psf_size, *(*data).x2, *(*data).y2, x2_0, y2_0 + ; Estimate PSF + if n_elements(x_0) ne 0 and n_elements(y_0) ne 0 then begin + x = x_0 & y = y_0 + *(*data).x = x & *(*data).y = y + if n_elements(x2_0) ne 0 and n_elements(y2_0) ne 0 then begin + compare_lists, x, y, x2_0, y2_0, MAX_DISTANCE = sqrt(2) * 1.5, $ + x_1, y_1, x2_1, y2_1, SUB2 = s2 + if s2[0] ge 0 then begin + x2 = x2_0[s2] & y2 = y2_0[s2] + *(*data).x2 = x2 & *(*data).y2 = y2 + endif + endif + avgtype = 2 - avg + widget_control, /HOURGLASS + psf_extract, *(*data).x, *(*data).y, x2, y2, *(*data).image, $ + psf_size, *(*data).psf, psf_fwhm, *(*data).background, $ + N_FWHM_BACK = back_box, N_FWHM_FIT = n_fwhm_fit, $ + INTERP_TYPE = 'I', UPPER_LEVEL = upper_lev, $ + N_FWHM_MATCH = n_fwhm_match, N_WIDTH = n_width, $ + MAG_FAC = mag_fac, AVGTYPE = avgtype, $ + RAD_NORM = norm_rad, _EXTRA = *(*data).extra + ; Save outputs + if n_elements(psf_fwhm) ne 0 then begin + *(*data).psf_fwhm = psf_fwhm + msg = dialog_message(/INFO, 'Done.') + endif + endif + endif else $ + msg = dialog_message(/ERROR, 'Please select a PSF size.') + end + 'disp_ima': begin + widget_control, /HOURGLASS + display_image, *(*data).image, (*data).wnum, OPTIONS = *(*data).ima_disp + (*data).last_disp = (*data).image + (*data).last_disp_opt = (*data).ima_disp + end + 'disp_psf': if n_elements(*(*data).psf) ne 0 then begin + widget_control, /HOURGLASS + display_image, *(*data).psf, (*data).wnum, OPTIONS = *(*data).psf_disp + (*data).last_disp = (*data).psf + (*data).last_disp_opt = (*data).psf_disp + endif + 'disp_opt': if n_elements(*(*data).last_disp) ne 0 then $ + *(*data).last_disp_opt = xdisplayopt(*(*data).last_disp, (*data).wnum, $ + OPTIONS = *(*data).last_disp_opt, GROUP = event.top) + 'help': $ + xdispfile, file_name('starfinder', 'xpsf_extract_help.txt'), $ + TITLE = 'XPsf_Extract help', /MODAL + 'exit': begin + if n_elements(*(*data).x) ne 0 then begin + msg = dialog_message(/QUESTION, 'Save PSF stars?') + if strlowcase(msg) eq 'yes' then begin + file = dialog_pickfile(/WRITE, FILTER = '*.txt', $ + PATH = *(*data).path, GET_PATH = path) + if file ne '' then begin + widget_control, /HOURGLASS + if strpos(file, '.txt') lt 0 then file = file + '.txt' + *(*data).path = path + out = [transpose(*(*data).x), transpose(*(*data).y)] + openw, lun, file, /GET_LUN + printf, lun, out & free_lun, lun + endif + endif + endif + widget_control, event.id, SET_UVALUE = data, /NO_COPY + widget_control, event.top, /DESTROY + end + else: + endcase + if event_type ne 'exit' then $ + widget_control, event.id, SET_UVALUE = data, /NO_COPY + return +end + +; XPSF_EXTRACT_DEF: define data structure. + +FUNCTION xpsf_extract_def, ids, par, image, psf, psf_fwhm, x, y, background, back_box, $ + rep, wnum, ima_disp, psf_disp, path, extra + + return, { ids: ids, par: par, $ + image: ptr_new(image, /NO_COPY), $ + psf: ptr_new(psf, /NO_COPY), psf_fwhm: ptr_new(psf_fwhm, /NO_COPY), $ + x: ptr_new(x, /NO_COPY), y: ptr_new(y, /NO_COPY), $ + x2: ptr_new(/ALLOCATE), y2: ptr_new(/ALLOCATE), $ + background: ptr_new(background, /NO_COPY), back_box: back_box, $ + rep: rep, $ + wnum: wnum, $ + ima_disp: ptr_new(ima_disp, /NO_COPY), $ + psf_disp: ptr_new(psf_disp, /NO_COPY), $ + last_disp: ptr_new(/ALLOCATE), last_disp_opt: ptr_new(/ALLOCATE), $ + path: ptr_new(path, /NO_COPY), $ + extra: ptr_new(extra, /NO_COPY) } +end + +; XPSF_EXTRACT_DEL: de-reference and de-allocate heap variables. + +PRO xpsf_extract_del, data, par, image, psf, psf_fwhm, x, y, background, back_box, $ + ima_disp, psf_disp, path, extra + + par = (*data).par + image = *(*data).image + if n_elements(*(*data).psf) ne 0 then psf = *(*data).psf + if n_elements(*(*data).psf_fwhm) ne 0 then psf_fwhm = *(*data).psf_fwhm + if n_elements(*(*data).x) ne 0 and n_elements(*(*data).y) ne 0 then begin + x = *(*data).x & y = *(*data).y + endif + if n_elements(*(*data).background) ne 0 then background = *(*data).background + back_box = (*data).back_box + if n_elements(*(*data).ima_disp) ne 0 then ima_disp = *(*data).ima_disp + if n_elements(*(*data).psf_disp) ne 0 then psf_disp = *(*data).psf_disp + if n_elements(*(*data).path) ne 0 then path = *(*data).path + if n_elements(*(*data).extra) ne 0 then extra = *(*data).extra + ptr_free, (*data).image, (*data).psf, (*data).psf_fwhm, $ + (*data).x, (*data).y, (*data).x2, (*data).y2, $ + (*data).background, (*data).ima_disp, (*data).psf_disp, $ + (*data).last_disp, (*data).last_disp_opt, (*data).path, (*data).extra + ptr_free, data + heap_gc + return +end + +; XPSF_EXTRACT_PAR: define default parameters. + +PRO xpsf_extract_par, id, par, image_max, back_box + + if n_elements(par) ne 0 then begin + psf_size = par.psf_size + if n_elements(back_box) eq 0 then back_box = par.back_box + n_fwhm_fit = par.n_fwhm_fit + norm_rad = par.norm_rad + avg = par.avg +;----------------------------------------- + psf_file = 1 +;----------------------------------------- + upper_lev = par.upper_lev + n_fwhm_match = par.n_fwhm_match + n_width = par.n_width + mag_fac = par.mag_fac + endif else begin + psf_size = 0 + if n_elements(back_box) eq 0 then back_box = 9 + n_fwhm_fit = 2 + norm_rad = 1. + avg = 0 +;----------------------------------------- + psf_file = 1 +;----------------------------------------- + upper_lev = 1e6 + while upper_lev le image_max do upper_lev = 10 * upper_lev + n_fwhm_match = 1 + n_width = 3 + mag_fac = 2 + endelse + par = {psf_size: psf_size, back_box: back_box, n_fwhm_fit: n_fwhm_fit, $ + norm_rad: norm_rad, avg: avg, psf_file: psf_file, upper_lev: upper_lev, $;------------------ + n_fwhm_match: n_fwhm_match, n_width: n_width, mag_fac: mag_fac} + init = {psf_size: strcompress(string(psf_size), /REMOVE_ALL), $ + back_box: strcompress(string(back_box), /REMOVE_ALL), $ + n_fwhm_fit: strcompress(string(n_fwhm_fit), /REMOVE_ALL), $ + norm_rad: strcompress(string(norm_rad), /REMOVE_ALL), $ + avg: avg, psf_file: psf_file,$ ;----------------------------------------- + upper_lev: strcompress(string(upper_lev), /REMOVE_ALL), $ + n_fwhm_match: strcompress(string(n_fwhm_match), /REMOVE_ALL), $ + n_width: strcompress(string(n_width), /REMOVE_ALL), $ + mag_fac: strcompress(string(mag_fac), /REMOVE_ALL)} + widget_control, id, SET_VALUE = init + return +end + +; XPSF_EXTRACT: XPsf_Extract widget definition module. + +PRO xpsf_extract, image, rep, psf, psf_fwhm, x, y, background, back_box, _EXTRA = extra, $ + DISPLAYIMAGE = ima_disp, DISPLAYPSF = psf_disp, $ + PATH = path, DEFAULT_PAR = par, GROUP = group, UVALUE = uvalue + + catch, error + if error ne 0 then begin + xpsf_extract_del, data, par, image, psf, psf_fwhm, x, y, background, back_box, $ + ima_disp, psf_disp, path, extra + if n_elements(group) eq 0 then widget_control, group_id, /DESTROY + return + endif + ; Create group leader if necessary + if n_elements(group) eq 0 then $ + group_id = widget_base() else group_id = group + ; Create modal base + if n_elements(uvalue) eq 0 then uvalue = 0B + base = widget_base(TITLE = 'XPsf_Extract', GROUP_LEADER = group_id, $ + /MODAL, UVALUE = uvalue, COLUMN = 2) + left_base = widget_base(base, /GRID_LAYOUT) + right_base = widget_base(base, /GRID_LAYOUT) + ; Define draw window (in the right part of base) + s = round(0.7 * min(get_screen_size())) + draw = widget_draw(right_base, SCR_XSIZE = s, SCR_YSIZE = s, $ + /ALIGN_CENTER, RETAIN = 2) + ; Define form with PSF extraction parameters + desc = [ $ + '0, LABEL,Boxes:,LEFT', $ + '1, BASE,,FRAME,COLUMN', $ + '0, INTEGER,,LABEL_LEFT=Size of output PSF,TAG=psf_size', $ + '0, FLOAT,,LABEL_LEFT=Box size for background estimation (FWHM units),' + $ + 'TAG=back_box', $ + '2, FLOAT,,LABEL_LEFT=Fitting box size (FWHM units),TAG=n_fwhm_fit', $ + '0, LABEL,Point Sources Combination:,LEFT', $ + '1, BASE,,FRAME,COLUMN', $ + '0, FLOAT,,LABEL_LEFT=Normalization radius (FWHM units),TAG=norm_rad', $ + '2, BUTTON,median|minimum|mean,EXCLUSIVE, ' + $ + 'LABEL_LEFT=Average type:,NO_RELEASE,ROW,TAG=avg', $ +;----------------------------------------------- + '0, LABEL,Sources Selection:,LEFT', $ + '1, BASE,,FRAME,COLUMN', $ + '2, BUTTON,yes|no,EXCLUSIVE, ' + $ + 'LABEL_LEFT=Load PSF stars coordinates from file:,NO_RELEASE,ROW,TAG=psf_file', $ +;------------------------------------- + '0, LABEL,Saturated stars:,LEFT', $ + '1, BASE,,FRAME,COLUMN', $ + '0, FLOAT,,LABEL_LEFT=Saturation threshold,WIDTH=12,TAG=upper_lev', $ + '0, FLOAT,,LABEL_LEFT=Search box to optimize correlation (FWHM units),' + $ + 'TAG=n_fwhm_match', $ + '0, FLOAT,,LABEL_LEFT=Repair box (saturated core units),TAG=n_width', $ + '2, INTEGER,,LABEL_LEFT=Sub-pixel positioning accuracy,TAG=mag_fac', $ + '1, BASE,,ROW', $ + '2, BUTTON,Processing...,NO_RELEASE,TAG=process', $ + '1, BASE,,COLUMN', $ + '0, BUTTON,Display Image,NO_RELEASE,TAG=disp_ima', $ + '0, BUTTON,Display PSF,NO_RELEASE,TAG=disp_psf', $ + '2, BUTTON,Display Options,NO_RELEASE,TAG=disp_opt', $ + '1, BASE,,ROW', $ + '0, BUTTON,Help,NO_RELEASE,TAG=help', $ + '2, BUTTON,Exit,QUIT,NO_RELEASE,TAG=exit'] + form = cw_form(left_base, desc, /COLUMN, IDS = ids) + image_max = max(image) + xpsf_extract_par, form, par, image_max, back_box + widget_control, ids[3], SENSITIVE = n_elements(background) eq 0 and 1B + widget_control, ids[4], SENSITIVE = rep eq 0 and 1B + for id = 12, 14 do $ + widget_control, ids[id], SENSITIVE = par.upper_lev le image_max and 1B + ; Realize widget + widget_control, base, /REALIZE + ; Display image and define display options + widget_control, draw, GET_VALUE = wnum + display_image, image, wnum, OPTIONS = ima_disp + ; Define pointer to auxiliary/output data + data = xpsf_extract_def(ids, par, image, psf, psf_fwhm, x, y, background, back_box, $ + rep, wnum, ima_disp, psf_disp, path, extra) + data = ptr_new(data, /NO_COPY) + (*data).last_disp = (*data).image + (*data).last_disp_opt = (*data).ima_disp + widget_control, form, SET_UVALUE = data + ; Register + xmanager, 'xpsf_extract', base, EVENT_HANDLER = 'xpsf_extract_event' + ; De-reference output data and de-allocate heap variables + xpsf_extract_del, data, par, image, psf, psf_fwhm, x, y, background, back_box, $ + ima_disp, psf_disp, path, extra + ; Destroy group leader if necessary + if n_elements(group) eq 0 then $ + widget_control, group_id, /DESTROY + return +end diff --git a/xpsf_extract_help.txt b/xpsf_extract_help.txt new file mode 100644 index 0000000000000000000000000000000000000000..2154bbb1b1d7d6fd1f8fc1f9a1a858c1acab7fe6 --- /dev/null +++ b/xpsf_extract_help.txt @@ -0,0 +1,146 @@ + XPsf_Extract help page + + + + GENERAL DESCRIPTION + + The XPsf_Extract widget extracts a PSF estimate from a + stellar field image, as a superposition of a set of stars. + The user is prompted to select the candidate sources by + clicking with the mouse: each click is allowed to range + in a 5x5 box centered on the maximum of the object. After + selection, a blow-up of each source is shown, to allow + rejection of undesired candidates. Stars too close to the + image edge (e.g. closer than half the size of the output + PSF) should be avoided. + The PSF stars are cleaned from the contaminating sources. + This task is accomplished in two ways. During the first + PSF estimation on a given image, the user may optionally + select the secondary sources around the candidate PSF stars, + by clicking with the mouse; when the PSF extraction procedure + is called after having already analyzed the image, the known + contaminating sources around the PSF stars are automatically + subtracted, using the information collected so far by the + program. Then the PSF stars are background-subtracted, + centered with sub-pixel accuracy, normalized, stacked and + finally combined by a pixel-by-pixel average with median, + minimum or mean operation. Centering involves interpolation, + which is reliable only on well-sampled data. + If present, saturated stars might be included as PSF + stars, because they provide useful information on the PSF + halo. To ensure correct normalization, the core of these + stars is approximately repaired, by replacing it with a scaled + replica of the PSF: accurate positioning is achieved by + correlation maximization, whereas the scaling factor is + determined by fitting the wings of the saturated source. + Nevertheless the repaired region is masked when forming + the PSF. Saturated stars, if not repaired, would be rejected + by the detection algorithm, compromising the analysis of + fainter sources around. + The XPsf_Extract widget returns the estimated PSF and, as + a by-product, the input image with repaired saturated stars. + Notice that saturated stars, if present, are repaired only + if selected for PSF extraction. + + + + PARAMETERS + + 'Size of output PSF': + Set the size (in pixels units) of the output PSF array. + + 'Box size for background estimation': + The background is estimated by interpolating an array of + local measurements, relative to a set of image sub-regions + arranged in a regular grid. This parameter specifies the + size (in units of PSF FWHM) of each sub-region. + This parameter is only available in the first iteration + of the PSF extraction for a given image: in later iterations, + the procedure adopts the background estimate provided by + the last call to XStarFinder_Run. + + 'Fitting box size (FWHM units)': + Size of box to fit the secondary sources to subtract. + This parameter is only available in the first iteration + of the PSF extraction for a given image: in the following + iterations, secondary sources around the PSF stars are + automatically subtracted on the basis of the previous + astrometric and photometric analysis. + + 'Normalization radius': + The PSF stars are normalized in a way that the integrated + flux within a circular region centered on the stellar peak is + unit. This parameter specifies the radius of this normalization + region. Of course, the final PSF estimate is again normalized + to a total flux of 1. + + 'Average type': + Select the method to combine the PSF stars into a single image. + + 'Source selection': + It is possible to load an input list of stars. The list must be in an + ASCII file with two columns: X position, Y position. + The positions are in pixels. There is no need to re-load the list + for a new iteration. + + 'Saturation threshold': + Estimate of the saturation level in the input image. This + field is set by default to a very high value (larger than + the image maximum), indicating no saturation. A saturation + threshold smaller than the image maximum indicates the + presence of saturated stars. If one or more selected + stars have their maximum above this level, they are + considered saturated and repair is attempted. + If the brightest star in the image was saturated and + has been repaired, it is recommended to display the image + again and change the display options (see 'Display Image' + and 'Display Options' buttons below): the upper intensity + level of the display should be increased. + + 'Search box to optimize correlation': + Accurate positioning of the PSF onto the core of + saturated stars is performed by correlation maximization. + The 'search box' specifies the region inside which the + PSF is positioned to maximize the correlation. The center + of the 'search box' is represented by the pixel clicked + on by the user when selecting the star. Notice that, when + clicking on a saturated star, no tolerance is fixed on + the position of the click and no search for the nearest + maximum is performed: the presumed center of the + saturated star coincides with the click itself. + + 'Repair box': + Size of the region to use when fitting the wings of a + saturated star, in order to determine the proper scaling + factor. This box size is expressed in units of the + saturated core diameter. + + 'Sub-pixel positioning accuracy': + When saturated stars are only slightly corrupted, it is + possible to achieve sub-pixel accuracy in positioning the + PSF onto the saturated core. This parameter indicates the + number of sub-pixel offsets for correlation maximization. + + + + CONTROLS/BUTTONS + + 'Processing...': + Extract a PSF estimate applying the currently defined options. + + 'Display Image': + Display the image array. + + 'Display PSF': + Display the PSF array. + + 'Display Options': + Call the widget application XDisplay_Opt to modify the display + options of the currently displayed array (either Image or PSF). + + 'Help': + Display this help page. + + 'Exit': + Quit XPsf_Extract. Before quitting, the user is prompted to + create an ASCII file with the positions of the PSF stars. \ No newline at end of file diff --git a/xpsf_smooth.pro b/xpsf_smooth.pro new file mode 100644 index 0000000000000000000000000000000000000000..1ba3d80a5fe3fc0285f722bc6c8d695477959a36 --- /dev/null +++ b/xpsf_smooth.pro @@ -0,0 +1,186 @@ +; $Id: xpsf_smooth, v 1.1 Apr 2000 e.d. $ +; +;+ +; NAME: +; XPSF_SMOOTH +; +; PURPOSE: +; Widget interface for the HALO_SMOOTH function. +; Apply a variable box size median filter to the halo of a PSF image. +; +; CATEGORY: +; Widgets. Signal processing. +; +; CALLING SEQUENCE: +; Result = XPSF_SMOOTH(Psf, Wnum, Display_opt) +; +; INPUTS: +; Psf: 2D data array, containing the image of the point source to smooth +; +; OPTIONAL INPUTS: +; Wnum: Window number of an existing window +; +; Display_opt: Structure of current display options; +; the structure must be defined as in DEFAULT_DISPLAY_OPT +; +; KEYWORD PARAMETERS: +; DEFAULT_PAR: Structure of default parameters for the widget's form. +; +; GROUP: XPsf_smooth group leader. +; +; UVALUE: XPsf_smooth user value. +; +; OUTPUTS: +; Result: Halo-smoothed Psf +; +; OPTIONAL OUTPUTS: +; Wnum: window number of new window, if undefined on input +; +; Display_opt: Display options, as defined and/or modified by +; X_Psf_smooth +; +; DEFAULT_PAR: Set this keyword to a named variable to get the +; structure of parameters set by the widget's user. +; +; SIDE EFFECTS: +; 1) Initiates the XMANAGER if it is not already running. +; 2) Call DISPLAY_IMAGE to activate the graphic window identified by Wnum. +; 3) Create a new graphic window if not defined on input (see DISPLAY_IMAGE +; and XDISPLAYOPT for details). +; 4) Any image displayed on the window Wnum is overwritten first by the +; input image to smooth and then by the result of the processing performed +; by XPsf_Smooth. +; +; RESTRICTIONS: +; The Help menu opens the file +; '/starfinder/xpsf_smooth_help.txt'. +; +; PROCEDURE: +; Create and register the widget as a modal widget. +; Then let the user define and or/modify the smoothing options and apply +; them to the input image. As a new processed image is obtained applying +; the current options, it is displayed on the graphic window. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, September 1999 +; Updates: +; 1) Enhanced error handling in event-handler +; (Emiliano Diolaiti, April 2000). +;- + +; XPSF_SMOOTH_EVENT: XPsf_Smooth event handler. + +PRO xpsf_smooth_event, event + + catch, error + if error ne 0 then begin + msg = dialog_message(/ERROR, !err_string) + widget_control, event.id, SET_UVALUE = data, /NO_COPY + return + endif + widget_control, event.id, GET_UVALUE = data, /NO_COPY + event_type = strlowcase(event.tag) + case event_type of + 'process': begin + widget_control, /HOURGLASS + widget_control, event.id, GET_VALUE = form + (*data).par.rad = form.rad + (*data).par.rw = form.rw + (*data).par.rp = form.rp + (*data).par.aw = form.aw + (*data).par.ap = form.ap + (*data).par.pad = form.pad + r = form.rad & rw = form.rw & rp = form.rp + aw = form.aw * !pi / 180. & ap = form.ap + pad = (form.pad eq 1) and 1B + (*data).psf_out = halo_smooth((*data).psf_in, r, PAD_0 = pad, $ + R_WIDTH = rw, A_WIDTH = aw, R_EXP = rp, A_EXP = ap) + display_image, (*data).psf_out, (*data).wnum, OPT = (*data).display_opt + end + 'display': (*data).display_opt = $ + xdisplayopt((*data).psf_out, (*data).wnum, /NODISPLAY, $ + OPTIONS = (*data).display_opt, GROUP = event.top) + 'help': $ + xdispfile, file_name('starfinder', 'xpsf_smooth_help.txt'), $ + TITLE = 'XPsf_Smooth help', /MODAL + 'exit': begin + widget_control, event.id, SET_UVALUE = data, /NO_COPY + widget_control, event.top, /DESTROY + end + else: + endcase + if event_type ne 'exit' then $ + widget_control, event.id, SET_UVALUE = data, /NO_COPY + return +end + +; XPSF_SMOOTH_PAR: define default parameters. + +PRO xpsf_smooth_par, id, minsiz, par + + if n_elements(par) ne 0 then begin + rad = par.rad & rw = par.rw & rp = par.rp + aw = par.aw & ap = par.ap & pad = par.pad + endif else begin + rad = round(minsiz/4.) & rw = round(rad/2.) & rp = 2 + aw = 22.5 & ap = 3 & pad = 0 + par = {rad: rad, rw: rw, rp: rp, aw: aw, ap: ap, pad: pad} + endelse + init = {rad: strcompress(string(rad), /REMOVE_ALL), $ + rw: strcompress(string(rw), /REMOVE_ALL), $ + rp: strcompress(string(rp), /REMOVE_ALL), $ + aw: strcompress(string(aw), /REMOVE_ALL), $ + ap: strcompress(string(ap), /REMOVE_ALL), $ + pad: pad} + widget_control, id, SET_VALUE = init + return +end + +; XPSF_SMOOTH: XPsf_Smooth widget definition module. + +FUNCTION xpsf_smooth, psf, wnum, display_opt, DEFAULT_PAR = par, $ + GROUP = group, UVALUE = uvalue + + on_error, 2 + ; Display image and define display options + display_image, psf, wnum, OPTIONS = display_opt + ; Create group leader if necessary + if n_elements(group) eq 0 then $ + group_id = widget_base() else group_id = group + ; Create modal base + if n_elements(uvalue) eq 0 then uvalue = 0B + base = widget_base(TITLE = 'XPsf_Smooth', /MODAL, UVALUE = uvalue, $ + GROUP_LEADER = group_id) + ; Define form + desc = [ $ + '0, INTEGER,,LABEL_LEFT=Inner radius,TAG=rad', $ + '0, INTEGER,,LABEL_LEFT=Radial width of smoothing box,TAG=rw', $ + '0, INTEGER,,LABEL_LEFT=Radial power,TAG=rp', $ + '0, FLOAT,,LABEL_LEFT=Angular width of smoothing box (deg.),TAG=aw', $ + '0, INTEGER,,LABEL_LEFT=Angular power,TAG =ap', $ + '0, BUTTON,No|Yes,EXCLUSIVE,LABEL_LEFT=Pad,NO_RELEASE,ROW,TAG=pad', $ + '1, BASE,,ROW', $ + '0, BUTTON,Processing...,NO_RELEASE,TAG=process', $ + '2, BUTTON,Display Options,NO_RELEASE,TAG=display', $ + '1, BASE,,ROW', $ + '0, BUTTON,Help,NO_RELEASE,TAG=help', $ + '2, BUTTON,Exit,QUIT,NO_RELEASE,TAG=exit'] + form = cw_form(base, desc, /COLUMN) + xpsf_smooth_par, form, min(size52(psf, /DIM)), par + ; Define pointer to auxiliary/output data + data = {par: par, psf_in: psf, wnum: wnum, $ + display_opt: display_opt, psf_out: psf} + data = ptr_new(data, /NO_COPY) + widget_control, form, SET_UVALUE = data + ; Realize, register, etc. + widget_control, base, /REALIZE + xmanager, 'xpsf_smooth', base, EVENT_HANDLER = 'xpsf_smooth_event' + ; De-reference output data + par = (*data).par & psf_out = (*data).psf_out + display_opt = (*data).display_opt + ptr_free, data + ; Destroy group leader if necessary + if n_elements(group) eq 0 then $ + widget_control, group_id, /DESTROY + return, psf_out +end diff --git a/xpsf_smooth_help.txt b/xpsf_smooth_help.txt new file mode 100644 index 0000000000000000000000000000000000000000..cbe147c76c7e3fee1b55c435c3ec11affa22e00b --- /dev/null +++ b/xpsf_smooth_help.txt @@ -0,0 +1,58 @@ + XPsf_Smooth help page + + + GENERAL DESCRIPTION + + The XPsf_Smooth widget applies a variable box size median + smoothing technique to the halo of a PSF image. The smoothing + is performed only on the pixels lying outside a pre-fixed + distance from the image maximum. The size of the smoothing + box increases outwards as a power-law function of the radial + distance from the center. + + + + PARAMETERS + + 'Inner radius': + Minimum distance from the PSF maximum for median smoothing. + + 'Radial width of smoothing box': + Radial width of smoothing box at a distance 2 * (Inner radius) + from the center. Expressed in pixels. + + 'Radial power': + Exponent of power law expressing the radial width of the box + as a function of the distance from the center. + + 'Angular width of smoothing box': + Radial width of smoothing box at a distance 2 * (Inner radius) + from the center. Expressed in degrees. + + 'Angular power': + Exponent of power law expressing the angular width of the box + as a function of the distance from the center. + + 'Pad': + Set 'yes' to pad the array with a frame of 0s before smoothing. + It may be useful to prevent edge effects. Not always necessary. + + + + CONTROLS/BUTTONS + + 'Processing...': + Apply the currently defined options to the input image. + Whenever the 'Processing...' button is pressed, the previous + result is overwritten by the input image and the processing + is performed with the current options. The result of the + last processing is returned on output. + + 'Display Options': + Modify the display options of the displayed image. + + 'Help': + Display this help page. + + 'Exit': + Quit XPsf_Smooth. \ No newline at end of file diff --git a/xreplace_pix.pro b/xreplace_pix.pro new file mode 100644 index 0000000000000000000000000000000000000000..81aa061367477b677977ab201cd89ddda5bd0868 --- /dev/null +++ b/xreplace_pix.pro @@ -0,0 +1,205 @@ +; $Id: xreplace_pix, v 1.1 Apr 2000 e.d. $ +; +;+ +; NAME: +; XREPLACE_PIX +; +; PURPOSE: +; Widget interface for the REPLACE_PIX function. +; Replace given pixels in a 2D array with the median of a suitable +; neighborhood. The pixels to replace are excluded from the computation +; of the local median. +; +; CATEGORY: +; Widgets. Signal processing. +; +; CALLING SEQUENCE: +; Result = XREPLACE_PIX(Array, X_pix, Y_pix) +; +; INPUTS: +; Array: 2D data array, containing pixels to replace +; +; OPTIONAL INPUTS: +; X_pix, Y_pix: Coordinates of pixels to replace +; +; KEYWORD PARAMETERS: +; PATH: Initial path for file browsing when reading bad pixels mask. +; If the argument of the keyword is a named variable, its value +; is overwritten. +; +; WNUM: Number of existing graphic window. +; +; DISPLAY_OPT: Display options for Array. +; +; GROUP: Xreplace_Pix group leader. +; +; UVALUE: Xreplace_Pix user value. +; +; OUTPUTS: +; Result: Array with replaced pixels +; +; OPTIONAL OUTPUTS: +; X_pix, Y_pix: Coordinates of pixels to replace +; +; WNUM: window number of new window, if undefined on input. +; +; DISPLAY_OPT: Display options, as defined and/or modified by +; Xreplace_Pix. +; +; SIDE EFFECTS: +; 1) Initiates the XMANAGER if it is not already running. +; 2) Call DISPLAY_IMAGE to activate the graphic window identified by Wnum. +; 3) Create a new graphic window if not defined on input (see DISPLAY_IMAGE +; and XDISPLAYOPT for details). +; 4) Any image displayed on the window Wnum is overwritten first by the +; input array and then by the result of the processing. +; +; RESTRICTIONS: +; 1) The pixels to replace must be marked by 0s in a binary mask which +; is read from a FITS file by Xreplace_Pix. +; 2) The Help menu opens the file +; '/starfinder/xreplace_pix_help.txt'. +; +; PROCEDURE: +; Create and register the widget as a modal widget. +; Then let the user define and or/modify the smoothing options and apply +; them to the input array. As a new processed array is obtained, it is +; displayed on the graphic window. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, September 1999 +; Updates: +; 1) Enhanced error handling in event-handler +; (Emiliano Diolaiti, April 2000). +;- + +; XREPLACE_PIX_EVENT: XReplace_Pix event handler. + +PRO xreplace_pix_event, event + + catch, error + if error ne 0 then begin + msg = dialog_message(/ERROR, !err_string) + widget_control, event.id, SET_UVALUE = data, /NO_COPY + return + endif + widget_control, event.id, GET_UVALUE = data, /NO_COPY + event_type = strlowcase(event.tag) + case event_type of + 'replace': begin + if n_elements(*(*data).x_pix) ne 0 then begin + form = cw_form(['0,INTEGER,3,LABEL_LEFT=Enter box size to compute median,TAG=box', $ + '2, BUTTON,OK,QUIT,NO_RELEASE'], TITLE = 'Box size to compute median') + box = form.box > 3 + widget_control, /HOURGLASS + *(*data).array_out = replace_pix(*(*data).array_in, $ + *(*data).x_pix, *(*data).y_pix, BOXSIZE = box) + display_image, *(*data).array_out, *(*data).wnum, OPT = *(*data).display_opt + endif else $ + msg = dialog_message(/ERROR, 'Please read bad pixels mask.') + end + 'load': begin + file = dialog_pickfile(/READ, FILTER = '*.fits', $ + PATH = *(*data).path, GET_PATH = path) + if file ne '' then begin + widget_control, /HOURGLASS + *(*data).path = path + fits_read, file, mask + w = where(mask eq 0, count) + if count ne 0 then $ + subs_to_coord, w, (size52(mask, /DIM))[0], *(*data).x_pix, *(*data).y_pix + endif + end + 'display': *(*data).display_opt = $ + xdisplayopt(*(*data).array_out, *(*data).wnum, /NODISPLAY, $ + OPTIONS = *(*data).display_opt, GROUP = event.top) + 'help': $ + xdispfile, file_name('starfinder', 'xreplace_pix_help.txt'), $ + TITLE = 'XReplace_Pix help', /MODAL + 'exit': begin + widget_control, event.id, SET_UVALUE = data, /NO_COPY + widget_control, event.top, /DESTROY + end + else: + endcase + if event_type ne 'exit' then $ + widget_control, event.id, SET_UVALUE = data, /NO_COPY + return +end + +; XREPLACE_PIX_DEF: define data structure. + +FUNCTION xreplace_pix_def, array, x_pix, y_pix, path, wnum, display_opt + + return, {array_in: ptr_new(array), path: ptr_new(path, /NO_COPY), $ + wnum: ptr_new(wnum, /NO_COPY), $ + display_opt: ptr_new(display_opt, /NO_COPY), $ + array_out: ptr_new(array), $ + x_pix: ptr_new(x_pix, /NO_COPY), y_pix: ptr_new(y_pix, /NO_COPY)} +end + +; XREPLACE_PIX_DEL: de-reference output data. + +PRO xreplace_pix_del, data, path, wnum, display_opt, array_out, x_pix, y_pix + + if n_elements(*(*data).path) ne 0 then path = *(*data).path + if n_elements(*(*data).wnum) ne 0 then wnum = *(*data).wnum + if n_elements(*(*data).display_opt) ne 0 then display_opt = *(*data).display_opt + if n_elements(*(*data).array_out) ne 0 then array_out = *(*data).array_out + if n_elements(*(*data).x_pix) ne 0 then begin + x_pix = *(*data).x_pix & y_pix = *(*data).y_pix + endif + ptr_free, (*data).array_in, (*data).path, (*data).wnum, (*data).display_opt, $ + (*data).array_out, (*data).x_pix, (*data).y_pix + ptr_free, data + return +end + +; XREPLACE_PIX: XReplace_Pix widget definition module. + +FUNCTION xreplace_pix, array, x_pix, y_pix, PATH = path, $ + WNUM = wnum, DISPLAY_OPT = display_opt, $ + GROUP = group, UVALUE = uvalue + + on_error, 2 + if n_elements(array) eq 0 then begin + mgs = dialog_message(/ERROR, 'Missing data array.') + return, array + endif + ; Display image and define display options + display_image, array, wnum, OPTIONS = display_opt + ; Create group leader if necessary + if n_elements(group) eq 0 then $ + group_id = widget_base() else group_id = group + ; Create modal base + if n_elements(uvalue) eq 0 then uvalue = 0B + base = widget_base(TITLE = 'XReplace_Pix', /MODAL, UVALUE = uvalue, $ + GROUP_LEADER = group_id) + ; Define form + desc = [ $ + '1, BASE,,ROW', $ + '1, BASE,,COLUMN', $ + '0, BUTTON,Read bad pixels,NO_RELEASE,TAG=load', $ + '2, BUTTON,Replace,NO_RELEASE,TAG=replace', $ + '1, BASE,,COLUMN', $ + '2, BUTTON,Display Options,NO_RELEASE,TAG=display', $ + '2, BASE,,', $ + '1, BASE,,ROW', $ + '1, BASE,,ROW', $ + '0, BUTTON,Help,NO_RELEASE,TAG=help', $ + '2, BUTTON,Exit,QUIT,NO_RELEASE,TAG=exit', $ + '2, BASE,,'] + form = cw_form(base, desc, /COLUMN) + ; Define pointer to auxiliary/output data + data = ptr_new(xreplace_pix_def(array, x_pix, y_pix, path, wnum, display_opt), /NO_COPY) + widget_control, form, SET_UVALUE = data + ; Realize, register, etc. + widget_control, base, /REALIZE + xmanager, 'xreplace_pix', base, EVENT_HANDLER = 'xreplace_pix_event' + ; De-reference output data + xreplace_pix_del, data, path, wnum, display_opt, array_out, x_pix, y_pix + ; Destroy group leader if necessary + if n_elements(group) eq 0 then $ + widget_control, group_id, /DESTROY + return, array_out +end diff --git a/xreplace_pix_help.txt b/xreplace_pix_help.txt new file mode 100644 index 0000000000000000000000000000000000000000..0e0c1a0bf15ed957c8b48390105385ef0f307288 --- /dev/null +++ b/xreplace_pix_help.txt @@ -0,0 +1,29 @@ + XReplace_Pix help page + + + GENERAL DESCRIPTION + + The XReplace_Pix widget replaces bad values in a given image + with the median of the good data in a suitable neighborhood of + each bad pixel. + + + + CONTROLS/BUTTONS + + 'Read bad pixels': + Enter name of FITS file containing a binary mask of bad + pixels, marked by 0s. The bad pixels array must have the + same size as the input image. + + 'Display Options': + Modify the display options of the currently displayed image. + + 'Replace': + Replace bad pixels. + + 'Help': + Display this help page. + + 'Exit': + Quit XHelp. \ No newline at end of file diff --git a/xstarfinder.pro b/xstarfinder.pro new file mode 100644 index 0000000000000000000000000000000000000000..1dc352c502ecc88724c0446abc401bb824e4d72b --- /dev/null +++ b/xstarfinder.pro @@ -0,0 +1,914 @@ +; $Id: xstarfinder.pro, v 1.8 Apr 2012 e.d. $ +; +;+ +; NAME: +; XSTARFINDER +; +; PURPOSE: +; Package for stellar fields analysis. +; +; CATEGORY: +; Widgets. +; +; CALLING SEQUENCE: +; XSTARFINDER +; +; KEYWORD PARAMETERS: +; GROUP: Group leader, i.e. identifier of the calling widget program +; +; BLOCK: Set this keyword to a nonzero value to have the IDL command +; line blocked when this application is registered +; +; UVALUE: User value to be assigned to XStarFinder +; +; SIDE EFFECTS: +; Initiates the XMANAGER if it is not already running. +; +; PROCEDURE: +; Create and register the widget and then exit. The additional data +; required by the program are stored as a structure of pointers in the +; user value of a child of the top level base. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, August-September 1999. +; Updates: +; 1) Enhanced error handling in event-handler +; (Emiliano Diolaiti, April 2000). +; 2) Modified 'Repeat PSF extraction' task +; (Emiliano Diolaiti, September 2001). +; 3) 'Repeat PSF extraction' task replaced by a call to the +; PSF extraction procedure, to allow a more flexible tuning +; of the parameters (Emiliano Diolaiti, January 2002). +; 4) Fixed problem with parameter 'Box for Background estimation' +; (E. D., August 2004). +; 5) When a new image is loaded, all the existing data are erased, while +; all the configuration parameters are preserved (E. D., August 2004). +; 6) The PSF is automatically normalized whenever it is loaded, extracted or +; processed (E. D., August 2004). +; 7) Added task to display detected sources in 'Image' menu (E. D., August 2006). +; 8) Output text file with parameters of detected sources is now on 7 columns +; (E. D., March 2012). +; 9) Added task to display residual image (image - synthetic field) +; (E. D., April 2012). +;- + + + +;;; EVENT PROCEDURES + +; LOAD_IMAGE_EV: load FITS format image data from input file and display it. + +PRO load_image_ev, data_p, path_p, HDR = hdr_p, $ + DISPLAY = display, drawid, display_par_p, $ + LOADED = loaded, FILE = file_p + + on_error, 2 + file = dialog_pickfile(/READ, FILTER = '*.fits', $ + PATH = *path_p, GET_PATH = path) + loaded = file ne '' + if not loaded then return + widget_control, /HOURGLASS + fits_read, file, data, hdr + if n_elements(path) ne 0 then *path_p = path + if n_elements(data) ne 0 then *data_p = data + if ptr_valid(file_p) then *file_p = file + if ptr_valid(hdr_p) then *hdr_p = hdr + if keyword_set(display) then begin +; *display_par_p = default_display_opt(data) + display_image_ev, drawid, path_p, display_par_p, data_p + endif + return +end + +; DISPLAY_IMAGE_EV: display image data using current options. + +PRO display_image_ev, drawid, path_p, display_par_p, data_p, $ + HDR = hdr_p, FILE = file_p + + on_error, 2 + no_data = not ptr_valid(data_p) + if no_data then data_p = ptr_new(/ALLOCATE) + if n_elements(*data_p) eq 0 then begin + if not ptr_valid(hdr_p) then hdr_p = ptr_new(/ALLOCATE) + load_image_ev, data_p, path_p, HDR = hdr_p, FILE = file_p + endif + if size52(*data_p, /N_DIM) ne 2 then return + widget_control, /HOURGLASS + if n_elements(*display_par_p) eq 0 then $ + *display_par_p = default_display_opt(*data_p) + display_image, *data_p, drawid, OPTIONS = *display_par_p + return +end + +; DISPLAY_OPTIONS_EV: modify display options by means of XDisplayOpt. + +PRO display_options_ev, drawid, last_data_p, display_par_p, group_leader + + on_error, 2 + if n_elements(*last_data_p) eq 0 then begin + msg = dialog_message('Please select data', /ERROR) + return + endif + widget_control, /HOURGLASS + if size52(*last_data_p, /TYPE) eq 7 then $ + fits_read, *last_data_p, data else data = *last_data_p + *display_par_p = xdisplayopt(data, drawid, /NODISPLAY, $ + OPTIONS = *display_par_p, GROUP = group_leader) + return +end + +; SAVE_IMAGE_EV: save image data in FITS-format output file. + +PRO save_image_ev, data_p, path_p, HDR = hdr_p + + on_error, 2 + if n_elements(*data_p) eq 0 then return + file = dialog_pickfile(/WRITE, FILTER = '*.fits', $ + PATH = *path_p, GET_PATH = path) + if file eq '' then return + widget_control, /HOURGLASS + if strpos(file, '.fits') lt 0 then file = file + '.fits' + *path_p = path + if ptr_valid(hdr_p) then $ + if n_elements(*hdr_p) ne 0 then header = *hdr_p + writefits, file, *data_p, header + return +end + +; NOISE_STD_EV: compute st. dev. of gaussian noise in the image. + +PRO noise_std_ev, data, group_leader + + on_error, 2 + if n_elements(*data.image.image) eq 0 then begin + msg = dialog_message('Please select data', /ERROR) + return + endif + xnoise, *data.image.image, *data.image.noise_std, WNUM = *data.draw, $ + PATH = *data.path, DEFAULT_PAR = *data.image.noise_par, $ + G_NOISE_PAR = *data.image.g_noise_par, PLOT_PAR = $ + *data.image.plot_par, GROUP = group_leader + return +end + +; REPLACE_BAD_EV: replace bad pixels in the input image. + +PRO replace_badpix_ev, data, group_leader + + on_error, 2 + if n_elements(*data.image.image) eq 0 then begin + msg = dialog_message('Please select data', /ERROR) + return + endif + *data.image.image = xreplace_pix(*data.image.image, $ + *data.image.x_badpix, *data.image.y_badpix, $ + PATH = *data.path, WNUM = *data.draw, $ + DISPLAY_OPT = *data.image.image_display_par, $ + GROUP = group_leader) + return +end + +; PSF_EXTRACT_EV: extract PSF from image. + +PRO psf_extract_ev, data, group_leader + + on_error, 2 + if n_elements(*data.image.image) eq 0 then begin + msg = dialog_message('Please select data', /ERROR) + return + endif + rep = n_elements(*data.image.x_stars) ne 0 and $ + n_elements(*data.image.y_stars) ne 0 and $ + n_elements(*data.image.f_stars) ne 0 and $ + n_elements(*data.image.stars) ne 0 and $ + n_elements(*data.psf.psf) ne 0 + xpsf_extract, *data.image.image, rep, *data.psf.psf, *data.psf.psf_fwhm, $ + *data.psf.x_psf, *data.psf.y_psf, *data.image.background, *data.image.backbox, $ + STARS = *data.image.stars, PSF = *data.psf.psf, $ + X_STARS = *data.image.x_stars, Y_STARS = *data.image.y_stars, $ + FLUXES = *data.image.f_stars, $ + DEFAULT_PAR = *data.psf.extract_par, $ + DISPLAYIMAGE = *data.image.image_display_par, $ + DISPLAYPSF = *data.psf.psf_display_par, $ + PATH = *data.path, GROUP = group_leader + if n_elements(*data.psf.psf) ne 0 then $ + for rep = 1, 2 do *data.psf.psf = *data.psf.psf / total(*data.psf.psf) +; xpsf_extract, *data.image.image, *data.psf.psf, *data.psf.psf_fwhm, $ +; *data.psf.x_psf, *data.psf.y_psf, *data.image.background, $ +; DEFAULT_PAR = *data.psf.extract_par, $ +; DISPLAYIMAGE = *data.image.image_display_par, $ +; DISPLAYPSF = *data.psf.psf_display_par, $ +; PATH = *data.path, GROUP = group_leader +; if n_elements(*data.psf.psf_fwhm) ne 0 then begin +; if n_elements(*data.psf.extract_par) ne 0 then $ +; n_fwhm = (*data.psf.extract_par).n_fwhm_back else n_fwhm = 9 +; *data.image.backbox = round(n_fwhm * *data.psf.psf_fwhm) +; endif + return +end + +; PSF_REPEAT_EV: repeat extraction procedure after 1st analysis. + +;PRO psf_repeat_ev, data + +; on_error, 2 +; if n_elements(*data.image.image) eq 0 then begin +; msg = dialog_message('Load image first', /ERROR) +; return +; endif +; if n_elements(*data.image.stars) eq 0 then begin +; msg = dialog_message('Analyze the image before', /ERROR) +; return +; endif +; if n_elements(*data.psf.extract_par) * $ +; n_elements(*data.psf.x_psf) eq 0 then begin +; msg = dialog_message('Run PSF extraction procedure first', /ERROR) +; return +; endif +; widget_control, /HOURGLASS +; xpsf_extract, *data.image.image, *data.psf.psf, *data.psf.psf_fwhm, $ +; *data.psf.x_psf, *data.psf.y_psf, *data.image.background, *data.image.backbox, $ +; STARS = *data.image.stars, PSF = *data.psf.psf, $ +; X_STARS = *data.image.x_stars, Y_STARS = *data.image.y_stars, $ +; FLUXES = *data.image.f_stars, $ +; DEFAULT_PAR = *data.psf.extract_par, $ +; DISPLAYIMAGE = *data.image.image_display_par, $ +; DISPLAYPSF = *data.psf.psf_display_par, $ +; PATH = *data.path, GROUP = group_leader +; if n_elements(*data.psf.psf) ne 0 then $ +; for rep = 1, 2 do *data.psf.psf = *data.psf.psf / total(*data.psf.psf) +; return +;end + +; PSF_SUPPORT_EV: PSF support event. + +PRO psf_support_ev, data, group_leader + + on_error, 2 + if n_elements(*data.psf.psf) eq 0 then begin + msg = dialog_message('Please define PSF', /ERROR) + return + endif + *data.psf.psf = ximage_support(*data.psf.psf, *data.draw, $ + *data.psf.psf_display_par, $ + DEFAULT_PAR = *data.psf.support_par, $ + GROUP = group_leader) + for rep = 1, 2 do *data.psf.psf = *data.psf.psf / total(*data.psf.psf) + return +end + +; PSF_SMOOTH_EV: PSF halo smooth event. + +PRO psf_smooth_ev, data, group_leader + + on_error, 2 + if n_elements(*data.psf.psf) eq 0 then begin + msg = dialog_message('Please define PSF', /ERROR) + return + endif + *data.psf.psf = xpsf_smooth(*data.psf.psf, *data.draw, $ + *data.psf.psf_display_par, $ + DEFAULT_PAR = *data.psf.smooth_par, $ + GROUP = group_leader) + for rep = 1, 2 do *data.psf.psf = *data.psf.psf / total(*data.psf.psf) + return +end + +; NORMALIZE_PSF_EV: PSF normalization event. + +PRO normalize_psf_ev, data + + on_error, 2 + if n_elements(*data.psf.psf) eq 0 then begin + msg = dialog_message('Please define PSF', /ERROR) + return + endif + widget_control, /HOURGLASS + for rep = 1, 2 do *data.psf.psf = *data.psf.psf / total(*data.psf.psf) + return +end + +; SELECT_REF_EV: select and save positions of reference stars in a given image. + +PRO select_ref_ev, data + + on_error, 2 + if n_elements(*data.image.image) eq 0 then begin + msg = dialog_message('Please select data', /ERROR) + return + endif + display_image, *data.image.image, *data.draw, $ + OPTIONS = *data.image.image_display_par + msg = dialog_message(['Select the reference stars.', '', $ + 'Use the left button of your mouse; ' + $ + 'push the right button to exit.'], /INFO) + if n_elements(*data.psf.psf_fwhm) ne 0 then $ + click_box = round(*data.psf.psf_fwhm) else click_box = 3 + click_on_max, *data.image.image, /MARK, /SILENT, x, y, $ + BOXSIZE = click_box, SYMSIZE = click_box + if n_elements(x) eq 0 or n_elements(y) eq 0 then return + file = dialog_pickfile(/WRITE, FILTER = '*.txt', PATH = *data.path, $ + GET_PATH = path, TITLE = 'Select file to save reference stars') + if file ne '' then begin + widget_control, /HOURGLASS + if strpos(file, '.txt') lt 0 then file = file + '.txt' + *data.path = path + out = [transpose(x),transpose(y)] + openw, lun, file, /GET_LUN + printf, lun, out & free_lun, lun + endif + return +end + +; ASTROM_PHOTOM_EV: astrometry and photometry event. + +PRO astrom_photom_event, data, group_leader + + on_error, 2 + if n_elements(*data.image.image) eq 0 then begin + msg = dialog_message('Load Image', /ERROR) + return + endif + if n_elements(*data.psf.psf) eq 0 then begin + msg = dialog_message('Load or estimate PSF', /ERROR) + return + endif + if n_elements(*data.psf.psf_fwhm) eq 0 then $ + *data.psf.psf_fwhm = fwhm(*data.psf.psf, /CUBIC, MAG = 3) + if n_elements(*data.image.backbox) eq 0 then $ + *data.image.backbox = 9 + xstarfinder_run, *data.image.image, *data.psf.psf, *data.psf.psf_fwhm, $ + *data.image.background, *data.image.backbox, $ + NOISE_STD = *data.image.noise_std, $ + X_BAD = *data.image.x_badpix, Y_BAD = *data.image.y_badpix, $ + STARS = *data.image.stars, $ + *data.image.x_stars, *data.image.y_stars, *data.image.f_stars, $ + *data.image.sx_stars, *data.image.sy_stars, *data.image.sf_stars, $ + *data.image.c_stars, DEFAULT_PAR = *data.image.run_par, $ + GROUP = group_leader, PATH = *data.path;, LOADFILE = *data.loadfile + if n_elements(*data.image.f_stars) ne 0 then begin + *data.image.syn_field = *data.image.stars + if n_elements(*data.image.background) ne 0 then $ + *data.image.syn_field = *data.image.syn_field + *data.image.background + *data.image.res = *data.image.image - *data.image.syn_field + endif + return +end + +; DISPLAY_STARS_EV: display detected sources on graphic window. + +PRO display_stars_ev, data + + if n_elements(*data.image.f_stars) eq 0 then begin + msg = dialog_message('Please run Astrometry and Photometry task before.', /ERROR) + return + endif + erase + display_image, *data.last_display, *data.draw, OPTIONS = *data.last_display_par + crosses, *data.image.image, /EX, *data.image.x_stars, *data.image.y_stars + return +end + + +; SAVE_LIST_EV: save list of detected stars. + +PRO save_list_ev, data + + if n_elements(*data.image.f_stars) eq 0 then begin + msg = dialog_message('Please run Astrometry and Photometry task before.', /ERROR) + return + endif + file = dialog_pickfile(/WRITE, FILTER = '*.txt', PATH = *data.path, $ + GET_PATH = path, TITLE = 'Select file to save list of stars') + if file ne '' then begin + widget_control, /HOURGLASS + if strpos(file, '.txt') lt 0 then file = file + '.txt' + *data.path = path + out = [transpose(*data.image.x_stars), $ + transpose(*data.image.y_stars), $ + transpose(*data.image.f_stars), $ + transpose(*data.image.sx_stars), $ + transpose(*data.image.sy_stars), $ + transpose(*data.image.sf_stars), $ + transpose(*data.image.c_stars)] + openw, lun, file, /GET_LUN, WIDTH = 200 + printf,lun, '# x y f x_err y_err f_err corr ' + printf, lun, out & free_lun, lun + endif + return +end + + +; INIT_DATA: initialize pointers to image or PSF data. + +PRO init_data, data, field + + on_error, 2 + widget_control, /HOURGLASS + case field of + 'image': begin + ptr_free, $ + data.image.background, $ + data.image.back_hdr, data.image.noise_std, $ + data.image.x_badpix, data.image.y_badpix, $ + data.image.x_stars, data.image.y_stars, data.image.f_stars, $ + data.image.sx_stars, data.image.sy_stars, data.image.sf_stars, $ + data.image.c_stars, data.image.stars, data.image.syn_field, $ + data.psf.psf, data.psf.psf_hdr, data.psf.psf_fwhm, data.psf.x_psf, data.psf.y_psf + data.image.background = ptr_new(/ALLOCATE) + data.image.back_hdr = ptr_new(/ALLOCATE) + data.image.noise_std = ptr_new(/ALLOCATE) + data.image.x_badpix = ptr_new(/ALLOCATE) + data.image.y_badpix = ptr_new(/ALLOCATE) + data.image.x_stars = ptr_new(/ALLOCATE) + data.image.y_stars = ptr_new(/ALLOCATE) + data.image.f_stars = ptr_new(/ALLOCATE) + data.image.sx_stars = ptr_new(/ALLOCATE) + data.image.sy_stars = ptr_new(/ALLOCATE) + data.image.sf_stars = ptr_new(/ALLOCATE) + data.image.c_stars = ptr_new(/ALLOCATE) + data.image.stars = ptr_new(/ALLOCATE) + data.image.syn_field = ptr_new(/ALLOCATE) + data.image.res = ptr_new(/ALLOCATE) + data.psf.psf = ptr_new(/ALLOCATE) + data.psf.psf_hdr = ptr_new(/ALLOCATE) + data.psf.psf_fwhm = ptr_new(/ALLOCATE) + data.psf.x_psf = ptr_new(/ALLOCATE) + data.psf.y_psf = ptr_new(/ALLOCATE) + end + 'psf': begin + ptr_free, data.psf.psf_fwhm, data.psf.x_psf, data.psf.y_psf + data.psf.psf_fwhm = ptr_new(/ALLOCATE) + data.psf.x_psf = ptr_new(/ALLOCATE) + data.psf.y_psf = ptr_new(/ALLOCATE) + end + endcase + return +end + +; CHECK_PTR: check undefined heap variables in data structure. + +PRO check_ptr, data, action + + on_error, 2 + widget_control, /HOURGLASS + case action of + 'in': begin + if not ptr_valid(data.draw) then data.draw = ptr_new(/ALLOCATE) + if not ptr_valid(data.path) then data.path = ptr_new(/ALLOCATE) +; if not ptr_valid(data.loadfile) then data.loadfile = ptr_new(/ALLOCATE) + if not ptr_valid(data.file) then data.file = ptr_new(/ALLOCATE) + if not ptr_valid(data.last_display) then data.last_display = ptr_new(/ALLOCATE) + if not ptr_valid(data.last_display_par) then data.last_display_par = ptr_new(/ALLOCATE) + if not ptr_valid(data.other_display_par) then data.other_display_par = ptr_new(/ALLOCATE) + if not ptr_valid(data.image.image) then data.image.image = ptr_new(/ALLOCATE) + if not ptr_valid(data.image.image_display_par) then data.image.image_display_par = ptr_new(/ALLOCATE) + if not ptr_valid(data.image.image_hdr) then data.image.image_hdr = ptr_new(/ALLOCATE) + if not ptr_valid(data.image.background) then data.image.background = ptr_new(/ALLOCATE) + if not ptr_valid(data.image.back_display_par) then data.image.back_display_par = ptr_new(/ALLOCATE) + if not ptr_valid(data.image.back_hdr) then data.image.back_hdr = ptr_new(/ALLOCATE) + if not ptr_valid(data.image.backbox) then data.image.backbox = ptr_new(/ALLOCATE) + if not ptr_valid(data.image.noise_par) then data.image.noise_par = ptr_new(/ALLOCATE) + if not ptr_valid(data.image.g_noise_par) then data.image.g_noise_par = ptr_new(/ALLOCATE) + if not ptr_valid(data.image.plot_par) then data.image.plot_par = ptr_new(/ALLOCATE) + if not ptr_valid(data.image.noise_std) then data.image.noise_std = ptr_new(/ALLOCATE) + if not ptr_valid(data.image.x_badpix) then data.image.x_badpix = ptr_new(/ALLOCATE) + if not ptr_valid(data.image.y_badpix) then data.image.y_badpix = ptr_new(/ALLOCATE) + if not ptr_valid(data.image.run_par) then data.image.run_par = ptr_new(/ALLOCATE) + if not ptr_valid(data.image.x_stars) then data.image.x_stars = ptr_new(/ALLOCATE) + if not ptr_valid(data.image.y_stars) then data.image.y_stars = ptr_new(/ALLOCATE) + if not ptr_valid(data.image.f_stars) then data.image.f_stars = ptr_new(/ALLOCATE) + if not ptr_valid(data.image.sx_stars) then data.image.sx_stars = ptr_new(/ALLOCATE) + if not ptr_valid(data.image.sy_stars) then data.image.sy_stars = ptr_new(/ALLOCATE) + if not ptr_valid(data.image.sf_stars) then data.image.sf_stars = ptr_new(/ALLOCATE) + if not ptr_valid(data.image.c_stars) then data.image.c_stars = ptr_new(/ALLOCATE) + if not ptr_valid(data.image.stars) then data.image.stars = ptr_new(/ALLOCATE) + if not ptr_valid(data.image.stars_display_par) then data.image.stars_display_par = ptr_new(/ALLOCATE) + if not ptr_valid(data.image.syn_field) then data.image.syn_field = ptr_new(/ALLOCATE) + if not ptr_valid(data.image.syn_display_par) then data.image.syn_display_par = ptr_new(/ALLOCATE) + if not ptr_valid(data.image.res) then data.image.res = ptr_new(/ALLOCATE) + if not ptr_valid(data.image.res_display_par) then data.image.res_display_par = ptr_new(/ALLOCATE) + if not ptr_valid(data.psf.psf) then data.psf.psf = ptr_new(/ALLOCATE) + if not ptr_valid(data.psf.extract_par) then data.psf.extract_par = ptr_new(/ALLOCATE) + if not ptr_valid(data.psf.support_par) then data.psf.support_par = ptr_new(/ALLOCATE) + if not ptr_valid(data.psf.smooth_par) then data.psf.smooth_par = ptr_new(/ALLOCATE) + if not ptr_valid(data.psf.psf_display_par) then data.psf.psf_display_par = ptr_new(/ALLOCATE) + if not ptr_valid(data.psf.psf_hdr) then data.psf.psf_hdr = ptr_new(/ALLOCATE) + if not ptr_valid(data.psf.psf_fwhm) then data.psf.psf_fwhm = ptr_new(/ALLOCATE) + if not ptr_valid(data.psf.x_psf) then data.psf.x_psf = ptr_new(/ALLOCATE) + if not ptr_valid(data.psf.y_psf) then data.psf.y_psf = ptr_new(/ALLOCATE) + end + 'out': begin + if n_elements(*data.draw) eq 0 then data.draw = ptr_new() + if n_elements(*data.path) eq 0 then data.path = ptr_new() +; if n_elements(*data.loadfile) eq 0 then data.loadfile = ptr_new() + if n_elements(*data.file) eq 0 then data.file = ptr_new() + if n_elements(*data.last_display) eq 0 then data.last_display = ptr_new() + if n_elements(*data.last_display_par) eq 0 then data.last_display_par = ptr_new() + if n_elements(*data.other_display_par) eq 0 then data.other_display_par = ptr_new() + if n_elements(*data.image.image) eq 0 then data.image.image = ptr_new() + if n_elements(*data.image.image_display_par) eq 0 then data.image.image_display_par = ptr_new() + if n_elements(*data.image.image_hdr) eq 0 then data.image.image_hdr = ptr_new() + if n_elements(*data.image.background) eq 0 then data.image.background = ptr_new() + if n_elements(*data.image.back_display_par) eq 0 then data.image.back_display_par = ptr_new() + if n_elements(*data.image.back_hdr) eq 0 then data.image.back_hdr = ptr_new() + if n_elements(*data.image.backbox) eq 0 then data.image.backbox = ptr_new() + if n_elements(*data.image.noise_par) eq 0 then data.image.noise_par = ptr_new() + if n_elements(*data.image.g_noise_par) eq 0 then data.image.g_noise_par = ptr_new() + if n_elements(*data.image.plot_par) eq 0 then data.image.plot_par = ptr_new() + if n_elements(*data.image.noise_std) eq 0 then data.image.noise_std = ptr_new() + if n_elements(*data.image.x_badpix) eq 0 then data.image.x_badpix = ptr_new() + if n_elements(*data.image.y_badpix) eq 0 then data.image.y_badpix = ptr_new() + if n_elements(*data.image.run_par) eq 0 then data.image.run_par = ptr_new() + if n_elements(*data.image.x_stars) eq 0 then data.image.x_stars = ptr_new() + if n_elements(*data.image.y_stars) eq 0 then data.image.y_stars = ptr_new() + if n_elements(*data.image.f_stars) eq 0 then data.image.f_stars = ptr_new() + if n_elements(*data.image.sx_stars) eq 0 then data.image.sx_stars = ptr_new() + if n_elements(*data.image.sy_stars) eq 0 then data.image.sy_stars = ptr_new() + if n_elements(*data.image.sf_stars) eq 0 then data.image.sf_stars = ptr_new() + if n_elements(*data.image.c_stars) eq 0 then data.image.c_stars = ptr_new() + if n_elements(*data.image.stars) eq 0 then data.image.stars = ptr_new() + if n_elements(*data.image.stars_display_par) eq 0 then data.image.stars_display_par = ptr_new() + if n_elements(*data.image.syn_field) eq 0 then data.image.syn_field = ptr_new() + if n_elements(*data.image.syn_display_par) eq 0 then data.image.syn_display_par = ptr_new() + if n_elements(*data.image.res) eq 0 then data.image.res = ptr_new() + if n_elements(*data.image.res_display_par) eq 0 then data.image.res_display_par = ptr_new() + if n_elements(*data.psf.psf) eq 0 then data.psf.psf = ptr_new() + if n_elements(*data.psf.extract_par) eq 0 then data.psf.extract_par = ptr_new() + if n_elements(*data.psf.support_par) eq 0 then data.psf.support_par = ptr_new() + if n_elements(*data.psf.smooth_par) eq 0 then data.psf.smooth_par = ptr_new() + if n_elements(*data.psf.psf_display_par) eq 0 then data.psf.psf_display_par = ptr_new() + if n_elements(*data.psf.psf_hdr) eq 0 then data.psf.psf_hdr = ptr_new() + if n_elements(*data.psf.psf_fwhm) eq 0 then data.psf.psf_fwhm = ptr_new() + if n_elements(*data.psf.x_psf) eq 0 then data.psf.x_psf = ptr_new() + if n_elements(*data.psf.y_psf) eq 0 then data.psf.y_psf = ptr_new() + end + endcase + return +end + +; SESSION_EV: save or restore XStarFinder session. + +PRO session_ev, data, action + + on_error, 2 + case action of + 'save': begin + file = dialog_pickfile(/WRITE, FILTER = '*.sav', $ + PATH = *data.path, GET_PATH = path) + if file ne '' then begin + widget_control, /HOURGLASS + if strpos(file, '.sav') lt 0 then file = file + '.sav' + *data.path = path + check_ptr, data, 'out' + save, FILENAME = file, data + check_ptr, data, 'in' + endif + end + 'restore': begin + file = dialog_pickfile(/READ, FILTER = '*.sav', $ + PATH = *data.path, GET_PATH = path) + if file ne '' then begin + widget_control, /HOURGLASS + restore, file + check_ptr, data, 'in' + *data.path = path + endif + end + endcase + return +end + +; XSTARFINDER_HELP_EV: display help file. + +PRO xstarfinder_help_ev, name, group + + on_error, 2 + file = '_help.txt' & title = ' help' + case strlowcase(name) of + 'xstarfinder': begin + file = 'xstarfinder' + file + title = 'XStarFinder' + title + end + 'image': begin + file = 'image' + file + title = 'Image' + title + end + 'psf': begin + file = 'psf' + file + title = 'PSF' + title + end + 'display': begin + file = 'display' + file + title = 'Display' + title + end + endcase + xdispfile, file_name('starfinder', file), TITLE = title, GROUP = group + return +end + +; XSTARFINDER_EVENT: XStarFinder event handler. + +PRO xstarfinder_event, event + + catch, error + if error ne 0 then begin + msg = dialog_message(/ERROR, !err_string) + child = widget_info(event.top, /CHILD) + widget_control, child, SET_UVALUE = data, /NO_COPY + return + endif + child = widget_info(event.top, /CHILD) + widget_control, child, GET_UVALUE = data, /NO_COPY + quit_confirm = 0B + + case event.value of + + 'Image.Load....Image': begin + load_image_ev, data.image.image, data.path, $ + HDR = data.image.image_hdr, /DISPLAY, $ + *data.draw, data.image.image_display_par, $ + LOADED = loaded + if loaded then begin + init_data, data, 'image' + data.last_display = data.image.image + data.last_display_par = data.image.image_display_par + endif + end + 'Image.Load....Background': begin + load_image_ev, data.image.background, data.path, $ + HDR = data.image.back_hdr, /DISPLAY, $ + *data.draw, data.image.back_display_par + data.last_display = data.image.background + data.last_display_par = data.image.back_display_par + end + 'Image.Noise.Load': load_image_ev, data.image.noise_std, data.path + 'Image.Noise.Compute': noise_std_ev, data, event.top + 'Image.Bad Pixels': replace_badpix_ev, data, event.top + 'Image.Reference sources': select_ref_ev, data + 'Image.Help': xstarfinder_help_ev, 'image', event.top + 'Image.Save.Image': $ + save_image_ev, data.image.image, data.path, $ + HDR = data.image.image_hdr + 'Image.Save.Background': $ + save_image_ev, data.image.background, data.path, $ + HDR = data.image.back_hdr + 'Image.Save.Detected stars': $ + save_image_ev, data.image.stars, data.path + 'Image.Save.Synthetic field': $ + save_image_ev, data.image.syn_field, data.path + 'Image.Save.Residual image': $ + save_image_ev, data.image.res, data.path + 'Image.Save.List of stars': save_list_ev, data + + 'PSF.Load...': begin + load_image_ev, data.psf.psf, data.path, $ + HDR = data.psf.psf_hdr, /DISPLAY, $ + *data.draw, data.psf.psf_display_par, $ + LOADED = loaded + if loaded then begin + init_data, data, 'psf' + data.last_display = data.psf.psf + data.last_display_par = data.psf.psf_display_par + for rep = 1, 2 do *data.psf.psf = *data.psf.psf / total(*data.psf.psf) + endif + end + 'PSF.Extract from image': psf_extract_ev, data, event.top + 'PSF.Post process.Support': psf_support_ev, data, event.top + 'PSF.Post process.Halo smoothing': psf_smooth_ev, data, event.top +; 'PSF.Normalize': normalize_psf_ev, data + 'PSF.Help': xstarfinder_help_ev, 'psf', event.top + 'PSF.Save': $ + save_image_ev, data.psf.psf, data.path, HDR = data.psf.psf_hdr + + 'Astrometry and Photometry': astrom_photom_event, data, event.top + + 'Display detected sources': display_stars_ev, data + + 'Compare Lists': $ + xcompare_lists, *data.draw, PATH = *data.path, GROUP = event.top + + 'Display.Select data.Image': begin + display_image_ev, *data.draw, data.path, $ + data.image.image_display_par, $ + data.image.image, HDR = data.image.image_hdr + data.last_display = data.image.image + data.last_display_par = data.image.image_display_par + end + 'Display.Select data.PSF': begin + display_image_ev, *data.draw, data.path, $ + data.psf.psf_display_par, $ + data.psf.psf, HDR = data.psf.psf_hdr + data.last_display = data.psf.psf + data.last_display_par = data.psf.psf_display_par + end + 'Display.Select data.Background': begin + display_image_ev, *data.draw, data.path, $ + data.image.back_display_par, $ + data.image.background, HDR = data.image.back_hdr + data.last_display = data.image.background + data.last_display_par = data.image.back_display_par + end + 'Display.Select data.Detected stars': begin + display_image_ev, *data.draw, data.path, $ + data.image.stars_display_par, $ + data.image.stars + data.last_display = data.image.stars + data.last_display_par = data.image.stars_display_par + end + 'Display.Select data.Synthetic field': begin + display_image_ev, *data.draw, data.path, $ + data.image.syn_display_par, $ + data.image.syn_field + data.last_display = data.image.syn_field + data.last_display_par = data.image.syn_display_par + end + 'Display.Select data.Residual image': begin + display_image_ev, *data.draw, data.path, $ + data.image.res_display_par, $ + data.image.res + data.last_display = data.image.res + data.last_display_par = data.image.res_display_par + end + 'Display.Select data.Other...': begin + ptr_free, data.other_display_par + data.other_display_par = ptr_new(/ALLOCATE) + display_image_ev, *data.draw, data.path, $ + data.other_display_par, FILE = data.file + data.last_display = data.file + data.last_display_par = data.other_display_par + end + 'Display.Options': $ + display_options_ev, *data.draw, data.last_display, $ + data.last_display_par, event.top + 'Display.Help': xstarfinder_help_ev, 'display', event.top + + 'Session.Save': session_ev, data, 'save' + 'Session.Restore': session_ev, data, 'restore' + + 'Help': xstarfinder_help_ev, 'xstarfinder', event.top + + 'Quit': begin + msg = dialog_message(/QUESTION, 'Really quit XStarFinder?') + quit_confirm = strlowcase(msg) eq 'yes' + if quit_confirm then begin + xstarfinder_del, data ; de-allocates heap variables + widget_control, event.top, /DESTROY + endif + end + + else: + + endcase + if event.value ne 'Quit' or $ + event.value eq 'Quit' and not quit_confirm then $ + widget_control, child, SET_UVALUE = data, /NO_COPY + return +end + + + +;;; WIDGET DEFINITION PROCEDURES/FUNCTIONS + +; XSTARFINDER_PROCESSING_MENU: define menu 1. + +FUNCTION xstarfinder_processing_menu + + return, ['1\Image', $ + '1\Load...', '0\Image', '2\Background', $ + '1\Noise', '0\Compute', '2\Load', $ + '0\Bad Pixels', $ + '0\Reference sources', $ + '1\Save', '0\Image', '0\Background', '0\Detected stars', $ + '0\Synthetic field', '0\Residual image', '2\List of stars', $ + '2\Help', $ ; end of Image sub-menu + '1\PSF', $ + '0\Load...', '0\Extract from image', $ + '1\Post process', '0\Support', '2\Halo smoothing', $ +; '0\Save', '2\Help', $ ; end of PSF sub-menu + '0\Normalize', '0\Save', '2\Help', $ ; end of PSF sub-menu + '0\Astrometry and Photometry', $ ; Astrometry - Photometry button + '0\Display detected sources', $ ; Display detected sources button + '0\Compare Lists'] ; Compare Lists button +end + +; XSTARFINDER_UTILITIES_MENU: define menu 2. + +FUNCTION xstarfinder_utilities_menu + + return, ['1\Display', $ + '1\Select data', '0\Image', '0\PSF', '0\Background', $ + '0\Detected stars', '0\Synthetic field', '0\Residual image', '2\Other...', $ + '0\Options', '2\Help', $ + '1\Session', '0\Save', '2\Restore', '0\Help', '0\Quit'] +end + +; XSTARFINDER_DEF: define structure of pointers to the data +; required by the program. + +FUNCTION xstarfinder_def + + return, $ + { image: $ ; image data + {image: ptr_new(/ALLOCATE), $ + image_display_par: ptr_new(/ALLOCATE), $ + image_hdr: ptr_new(/ALLOCATE), $ + background: ptr_new(/ALLOCATE), $ + back_display_par: ptr_new(/ALLOCATE), $ + back_hdr: ptr_new(/ALLOCATE), $ + backbox: ptr_new(/ALLOCATE), $ + noise_par: ptr_new(/ALLOCATE), $ + g_noise_par: ptr_new(/ALLOCATE), $ + plot_par: ptr_new(/ALLOCATE), $ + noise_std: ptr_new(/ALLOCATE), $ + x_badpix: ptr_new(/ALLOCATE), y_badpix: ptr_new(/ALLOCATE), $ + run_par: ptr_new(/ALLOCATE), $ + x_stars: ptr_new(/ALLOCATE), $ + y_stars: ptr_new(/ALLOCATE), $ + f_stars: ptr_new(/ALLOCATE), $ + sx_stars: ptr_new(/ALLOCATE), $ + sy_stars: ptr_new(/ALLOCATE), $ + sf_stars: ptr_new(/ALLOCATE), $ + c_stars: ptr_new(/ALLOCATE), $ + stars: ptr_new(/ALLOCATE), stars_display_par: ptr_new(/ALLOCATE), $ + syn_field: ptr_new(/ALLOCATE), syn_display_par: ptr_new(/ALLOCATE), $ + res: ptr_new(/ALLOCATE), res_display_par: ptr_new(/ALLOCATE)}, $ + psf: $ ; PSF data + {psf: ptr_new(/ALLOCATE), $ + extract_par: ptr_new(/ALLOCATE), $ + support_par: ptr_new(/ALLOCATE), $ + smooth_par: ptr_new(/ALLOCATE), $ + psf_display_par: ptr_new(/ALLOCATE), $ + psf_hdr: ptr_new(/ALLOCATE), $ + psf_fwhm: ptr_new(/ALLOCATE), $ + x_psf: ptr_new(/ALLOCATE), y_psf: ptr_new(/ALLOCATE)}, $ + draw: ptr_new(/ALLOCATE), $ + path: ptr_new(/ALLOCATE), $ +; loadfile: ptr_new(/ALLOCATE), $ + file: ptr_new(/ALLOCATE), $ + last_display: ptr_new(/ALLOCATE), $ + other_display_par: ptr_new(/ALLOCATE), $ + last_display_par: ptr_new(/ALLOCATE) } +end + +; XSTARFINDER_DEL: delete heap variables. + +PRO xstarfinder_del, data + + on_error, 2 + ; de-allocate memory pointed by data structure fields + ptr_free, data.image.image, data.image.image_display_par, $ + data.image.image_hdr, data.image.background, $ + data.image.back_display_par, data.image.back_hdr, $ + data.image.backbox, data.image.noise_par, $ + data.image.g_noise_par, data.image.plot_par, $ + data.image.noise_std, data.image.x_badpix, data.image.y_badpix, $ + data.image.run_par, data.image.x_stars, data.image.y_stars, $ + data.image.f_stars, data.image.sx_stars, data.image.sy_stars, $ + data.image.sf_stars, data.image.c_stars, data.image.stars, $ + data.image.stars_display_par, data.image.syn_field, $ + data.image.syn_display_par, $ + data.image.res_display_par, $ + data.psf.psf, data.psf.extract_par, data.psf.support_par, $ + data.psf.smooth_par, data.psf.psf_display_par, $ + data.psf.psf_hdr, data.psf.psf_fwhm, $ + data.psf.x_psf, data.psf.y_psf, $ + data.draw, data.path, data.file, data.last_display, $ +; data.draw, data.path, data.loadfile, data.file, data.last_display, $ + data.other_display_par, data.last_display_par + ; de-allocate any inaccessible heap variables + heap_gc + return +end + +; XSTARFINDER: XStarFinder widget definition module. + +PRO xstarfinder, GROUP = group, BLOCK = block, UVALUE = uvalue + + on_error, 2 + ; define top level base, partitioned into two parts: left and right + if n_elements(uvalue) eq 0 then uvalue = 0B + s = round(1.0 * min(get_screen_size())) + base = widget_base(TITLE = 'XStarFinder', COLUMN = 2, UVALUE = uvalue) + left_base = widget_base(base, ROW = 2) + right_base = widget_base(base) + ; define commands (in left part of base) + menu = xstarfinder_processing_menu() + processing = cw_pdmenu(left_base, menu, /COLUMN, /RETURN_FULL_NAME) + menu = xstarfinder_utilities_menu() + util = cw_pdmenu(left_base, menu, /COLUMN, /RETURN_FULL_NAME) + ; define draw window (in right part of base) + draw = widget_draw(right_base, SCR_XSIZE = s, SCR_YSIZE = s, $ + /ALIGN_CENTER, RETAIN = 2) + ; realize widgets + widget_control, base, /REALIZE + widget_control, draw, GET_VALUE = wnum + ; allocate memory for global data and store it in user value + ; of the left base + data = xstarfinder_def() & *data.draw = wnum + widget_control, left_base, SET_UVALUE = data, /NO_COPY + ; register with XManager + xmanager, 'xstarfinder', base, EVENT_HANDLER = 'xstarfinder_event', $ + GROUP_LEADER = group, NO_BLOCK = not keyword_set(block) and 1B + return +end diff --git a/xstarfinder_help.txt b/xstarfinder_help.txt new file mode 100644 index 0000000000000000000000000000000000000000..9481f4fac0d358fe78a102d7c6700d5674d3f342 --- /dev/null +++ b/xstarfinder_help.txt @@ -0,0 +1,199 @@ + XSTARFINDER - v1.8 + + + + 1) GENERAL DESCRIPTION. + + XStarFinder is a general purpose astrometry and photometry + toolkit for crowded stellar fields analysis. + It has been designed for well-sampled high and low Strehl + images. The current version considers the PSF constant over + the field of view; a method to handle anisoplanatic effects + in wide-field Adaptive Optics observations is under + development. + + + + 2) MAIN MENU DESCRIPTION. + + The controls in the main menu are: + + 'Image' pull-down menu: + Load image from FITS file, display it and perform basic + operations (compute standard deviation of gaussian noise, + repair bad pixels, select reference sources by mouse click). + Save by-products of the analysis (e.g. image after repair + of saturated stars, synthetic field, background estimate, + etc.). + + 'PSF' pull-down menu: + Load PSF from FITS file or extract it directly from the + image. The PSF extraction task allows the user to repair + the saturated stars in the input image. + The 'PSF' pull-down menu includes tasks for basic post- + processing of the extracted PSF (e.g. support masking, + halo smoothing, normalization) and allows one to save + the retrieved estimate of the PSF. + + 'Astrometry and Photometry' button: + Detect stars in the input image, performing astrometry and + photometry. The PSF, either load from a file or extracted + from the image, is used as a rigid template which may be + shifted and scaled to match other objects in the field. + The list of detected stars can be saved to an ASCII file. + + 'Display detected sources' button: + After running the 'Astrometry and Photometry' task, this + command displays the detected sources with a '+' symbol, + superimposed on the last displayed image. + + 'Compare Lists' button: + Compare two data sets representing the results obtained + on different images of the same field (e.g. in different + filters) and create a simple color-magnitude diagram. + The data sets are supposed to be reciprocally translated + and rotated. The plotted diagram can be saved as + PostScript file. + + 'Display' pull-down menu: + Define display options (intensity range, stretch, chopping + threshold, color table) for the currently displayed image. + + 'Session' pull-down menu: + Save current session or restore a previously saved one. + + 'Help' button: + Display this help file. Notice that every secondary widget + application is provided with its own help page. + + 'Quit' button: + Exit XStarFinder. + + + + 3) NOTES ON VARIABLES. + + XStarFinder is a compound application. The main widget which + appears on the screen (called XStarFinder) is nothing more than + an interface allowing the user to call different widget-based + applications, in order to perform various operations on the + stellar field image. + The main widget defines a memory area to store some 'global' + variables, namely + - image: the stellar field + - PSF: array containing an image of the Point Spread Function + - noise: array, with the same size as the image, containing the + noise standard deviation for each image pixel + - background: array, with the same size as the image, with an + estimate of the background emission. Available after PSF + extraction or stellar field analysis + - detected stars: synthetic image, containing one replica of the + PSF for each detected star in the field. Available after + stars detection + - synthetic field: sum of background array and detected stars + - residual image: difference of image and synthetic field + - list of detected stars: positions, fluxes, formal errors and + correlation coefficients. Available after stars detection + - other data and parameters. + The global variables are maintained and modified in the course + of the current session. As one of these variables is processed + by some application, its previous value is overwritten: if the + user wishes to keep track of the values assumed by the variable + in the course of the session, he/she should save the variable + itself or the entire session whenever necessary. + All the global data are basically divided into two groups: + those associated to the image (image, background, noise array, + stellar field model, list of detected stars, ...) and those + associated to the PSF (PSF array, coordinates of stars selected + by the user to form the PSF, ...). Whenever the user loads a new + image or a new PSF, all the associated data are erased, to avoid + 'mixing' different data. The only exception is represented by + the default parameters of the secondary widgets applications + (see below): in this case the user selected parameters are saved. + Every secondary widget application has a set of default + parameters, which can be modified interactively by the user. + The user-defined values become the new default values of the + widget's set of parameters until the user quits XStarFinder + without saving the current session. This feature may help the + user to analyze two images of the same field. Of course, if the + observations were taken at different wavelengths, all the + 'structural' parameters (e.g. those related to the PSF FWHM, + as the box size for PSF extraction) must be scaled properly. + Nevertheless, saving the user-defined values of the widgets' + parameters might be tricky in some situations, for instance + when two images of different targets and/or in different + observing conditions are analyzed in sequence. In these cases + it is recommended to close the current XStarFinder session + and open a new one, in order to restore the default values of + the parameters. + + + + 4) NOTES ON INPUT/OUTPUT. + + XStarFinder can load/save images from/to FITS files. Every + output FITS file has the same header as the corresponding input + file. A minimal default header is written when the saved item + has no corresponding input (e.g. the PSF when it is extracted + from the stellar field). + It is strongly recommended to avoid overwriting existing FITS + files, to avoid losing important header information. + + + + 5) MODIFICATIONS. + + * Main menu + The button 'Display detected sources' allows to display the + detected stars after running the 'Astrometry and Photometry' task. + Each detected source is marked by a '+' sign. + + * PSF extraction + The task "Repeat PSF extraction" has been removed. Now, in order + to repeat the PSF estimation, the normal task "Extract from image" + has to be used instead. This allows greater flexibility in the + selection of the parameters. + The possibility to select by mouse-click the contaminating sources + around the candidate stars for PSF estimation, previously removed, + has been restored. However this option is only available in the + first PSF extraction. Once the image has been analyzed and the + stellar sources have been detected by the program, if the user + wants to obtain a new PSF estimate she/he is not prompted to select + the contaminating sources around the PSF stars: these secondary + sources are automatically subtracted, based upon the information + available from the previous image analysis. + Three strategies are now available to combine pixel-by-pixel the + stars selected for the PSF estimation: median, minimum and mean. + + * Astrometry/Photometry + The "Deblend" option, previously removed, has been restored. + The minimum acceptable distance between any two acceptable stars + (this parameter was coded in the STARFINDER.PRO module) has now + been added as a parameter in the GUI. The default is 1, i.e. 1 PSF + FWHM is the default minimum acceptable distance between two sources. + Two options concerning the background have been added in the GUI: + the possibility to keep the background map (used in the detection + and correlation analysis of the sources) fixed and the option to + avoid fitting the local background below a given source with a + tilted plane (in this case, the local background is extracted from + the global background map and kept fixed in the fitting). + Finally, it is possible to load a known list of sources and avoid + the detection phase. The loaded sources are just fitted. If the fit + of a given source fails, the source is rejected. + + * Background + The background is estimated by default by interpolating a set + of local background measurements carried out on a grid of + sub-regions in the image. This method is applied by + XStarFinder_Run on the image cleaned from the sources known so + far. + An alternative background estimation method is available in + XStarFinder_Run: it consists of a median filtering of the image. + This method, when applied on the image cleaned from the known + stars, may be useful to estimate the background contribution + when this is very irregular. If applied in the very first pass + of XStarFinder_Run, this method may overestimate the background + below bright sources. + The background estimate calculated by XStarFinder_Run is also + passed to XPsf_Extract, when this task is called to refine the PSF + estimate. \ No newline at end of file diff --git a/xstarfinder_run.pro b/xstarfinder_run.pro new file mode 100644 index 0000000000000000000000000000000000000000..e8d83cbf0f4d89ebe175994f71cc46856b9c95dd --- /dev/null +++ b/xstarfinder_run.pro @@ -0,0 +1,467 @@ +; $Id: xstarfinder_run, v 1.6 Apr 2012 e.d. $ +; +;+ +; NAME: +; XSTARFINDER_RUN +; +; PURPOSE: +; Widget interface for the STARFINDER procedure. +; Detect stars in a stellar field and determine astrometry and photometry. +; +; CATEGORY: +; Widgets. Signal processing. +; +; CALLING SEQUENCE: +; XSTARFINDER_RUN, Image, Psf, Psf_fwhm, Background, X, Y, F, Sx, Sy, Sf, C +; +; INPUTS: +; Image: Stellar field +; +; Psf: 2D array, containing the Point Spread Function. +; +; Psf_fwhm: PSF FWHM. +; +; Background: 2D array, containing an initial guess of the image background +; +; Back_Box: Box size to compute the background. +; +; KEYWORD PARAMETERS: +; X_BAD, Y_BAD: Coordinates of bad pixels. +; +; PATH: Initial path for file browsing when saving the results. +; If the argument of the keyword is a named variable, its value +; is overwritten. +; +; DEFAULT_PAR: Structure of default parameters for the widget's form. +; +; GROUP: XPsf_Extract group leader. +; +; UVALUE: XPsf_Extract user value. +; +; OUTPUTS: +; Background: 2D array, containing the updated image background +; +; X, Y: Positions of detected stars. Origin is at (0, 0). +; +; F: Stellar fluxes, referred to the normalization of the input Psf. +; +; Sx, Sy, Sf: Formal errors on astrometry and photometry. Available +; only if the necessary information on the image noise are supplied. +; +; C: Correlation coefficient of each detected star. The objects detected +; and analyzed as 'blends' have the correlation coefficient set to +; -1 by default. +; +; OPTIONAL OUTPUTS: +; NOISE_STD: 2D array, containing an estimate of the noise standard +; deviation for each pixel in the input image. If undefined on input, +; it can be read from file. +; +; STARS: Image model released by StarFinder, given by a superposition +; of shifted scaled PSFs, one for eaach detected star. +; +; DEFAULT_PAR: Set this keyword to a named variable to get the +; structure of parameters set by the widget's user. +; +; SIDE EFFECTS: +; Initiates the XMANAGER if it is not already running. +; +; RESTRICTIONS: +; The Help menu opens the file +; '/starfinder/xstarfinder_run_help.txt'. +; +; PROCEDURE: +; Create and register the widget as a modal widget. +; Then let the user define and or/modify the analysis options and apply +; them to the input image. The results may be saved on a text a file. +; +; MODIFICATION HISTORY: +; Written by: Emiliano Diolaiti, September 1999 +; Updates: +; 1) Enhanced error handling in event-handler +; (Emiliano Diolaiti, April 2000). +; 2) BACKGROUND and BACK_BOX are now parameters (E. D., August 2004). +; 3) Removed DEBLEND options (E. D., August 2004). +; 4) Added following options in the GUI: +; * Upgrade background map +; * Fit local background with tilted plane +; * Set minimum source distance (E. D., July 2006). +; 5) Added 'Load list' command, to load an input list of sources to +; be fitted, without any search (E. D., August 2006). +; 6) Modified some keyword settings in call to STARFINDER (E.D., March 2012). +; Modified settings are: +; a) Background estimation --> removed /SKY_MEDIAN, set CUBIC = -0.5 +; 7) Output text file with parameters of detected sources is now on 7 columns +; (E.D., March 2012). +; 8) Restored DEBLEND options. Two modes available: "Deblend detected sources" +; and "Deblend rejected sources" (E. D., April 2012). +; 9) Added 'Fit only fluxes' command, available when list of sources is loaded +; (E. D., April 2012). +; 10) Slightly changed widget design (E. D., April 2012). +; 11) Added option to estimate background by median filtering. Default is +; local estimate + interpolation (E. D., April 2012). +;- + +; XSTARFINDER_RUN_EVENT: XStarFinder_Run event handler. + +PRO xstarfinder_run_event, event + + catch, error + if error ne 0 then begin + msg = dialog_message(/ERROR, !err_string) + child = widget_info(event.top, /CHILD) + widget_control, child, SET_UVALUE = data, /NO_COPY + close, /ALL + return + endif + ; Get user value + child = widget_info(event.top, /CHILD) + widget_control, child, GET_UVALUE = data, /NO_COPY + ; Event case + event_id = event.id & id = (*data).id + case event_id of + id.noise: begin + widget_control, id.noise, GET_VALUE = noise +; (*data).par.noise = noise + widget_control, id.rel_thresh, GET_VALUE = rel_thresh + rel_thresh = (rel_thresh eq 1 and noise eq 1) and 1B +; (*data).par.rel_thresh = rel_thresh + widget_control, id.rel_thresh, SET_VALUE = rel_thresh, $ + SENSITIVE = (noise eq 1) and 1B + end + id.bkg: begin + widget_control, id.bkg, GET_VALUE = bkg +; (*data).par.bkg = bkg + widget_control, id.bbox, SENSITIVE = bkg and 1B + widget_control, id.bmeth, SENSITIVE = bkg and 1B + end + id.load: begin + file = dialog_pickfile(/READ, PATH = *(*data).path, GET_PATH = path) + if file ne '' then begin + *(*data).loadfile = file + *(*data).path = path + widget_control, id.fluxopt, SENSITIVE = 1B + widget_control, id.cthresh, SENSITIVE = 0B + widget_control, id.csub, SENSITIVE = 0B + widget_control, id.bkg, SENSITIVE = 0B + widget_control, id.bbox, SENSITIVE = 0B + widget_control, id.bmeth, SENSITIVE = 0B + widget_control, id.deblend, SENSITIVE = 0B + widget_control, id.deblost, SENSITIVE = 0B + endif + end + id.proc: begin + ; Read out form and store parameters + widget_control, id.dthresh, GET_VALUE = threshold + str_threshold = threshold.tag0 + (*data).par.dthresh = str_threshold + threshold = float(str_sep(strcompress(str_threshold, /REMOVE_ALL), ',')) + widget_control, id.rel_thresh, GET_VALUE = rel_thresh + (*data).par.rel_thresh = rel_thresh + rel_thresh = (rel_thresh eq 1) and 1B + widget_control, id.cthresh, GET_VALUE = min_correlation + min_correlation = min_correlation.tag0 + (*data).par.cthresh = min_correlation + widget_control, id.csub, GET_VALUE = correl_mag + correl_mag = correl_mag.tag0 > 1 + (*data).par.csub = correl_mag + widget_control, id.noise, GET_VALUE = noise + (*data).par.noise = noise + if noise eq 1 then noise_std = *(*data).noise_std + widget_control, id.bkg, GET_VALUE = bkg + (*data).par.bkg = bkg + widget_control, id.bbox, GET_VALUE = bbox + bbox = bbox.tag0 + (*data).par.bbox = bbox + (*data).back_box = bbox + if bkg and 1B then back_box = round(bbox * (*data).psf_fwhm) + widget_control, id.bmeth, GET_VALUE = bmeth + (*data).par.bmeth = bmeth + sky_median = (bmeth eq 1) and 1B + widget_control, id.slant, GET_VALUE = slant + (*data).par.slant = slant + no_slant = (not slant) and 1B + widget_control, id.mindist, GET_VALUE = mindist + mindist = mindist.tag0 + (*data).par.mindist = mindist + widget_control, id.deblend, GET_VALUE = deblend + (*data).par.deblend = deblend + deblend = (deblend eq 1) and 1B + widget_control, id.deblost, GET_VALUE = deblost + (*data).par.deblost = deblost + deblost = (deblost eq 1) and 1B + widget_control, id.niter, GET_VALUE = niter + niter = niter.tag0 + (*data).par.niter = niter + widget_control, id.fluxopt, GET_VALUE = fluxopt + (*data).par.fluxopt = fluxopt + fluxopt = (fluxopt eq 1) and (n_elements(*(*data).loadfile) ne 0) and 1B + if n_elements(*(*data).loadfile) ne 0 then begin + xyf = read_float_data(*(*data).loadfile, 3) + x_input = transpose(xyf[0,*]) + y_input = transpose(xyf[1,*]) + f_input = transpose(xyf[2,*]) + endif + ; Call STARFINDER and store results + if str_threshold eq '' then $ + msg = dialog_message(/ERROR, ['Enter one or more detection thresholds', $ + 'separated by commas.']) $ + else begin + widget_control, /HOURGLASS +; minif = round((*data).psf_fwhm / 2) + starfinder, *(*data).image, *(*data).psf, $ + X_BAD = *(*data).x_bad, Y_BAD = *(*data).y_bad, $ +; BACKGROUND = *(*data).background, BACK_BOX = back_box, /SKY_MEDIAN, $ +; BACKGROUND = *(*data).background, BACK_BOX = back_box, CUBIC = -0.5, $;/SKY_MEDIAN, $ + BACKGROUND = *(*data).background, BACK_BOX = back_box, $ + CUBIC = -0.5, SKY_MEDIAN = sky_median, $ + threshold, REL_THRESHOLD = rel_thresh, /PRE_SMOOTH, $ + NOISE_STD = *(*data).noise_std, min_correlation, $ + CORREL_MAG = correl_mag, INTERP_TYPE = 'I', $ + NO_SLANT = no_slant, MIN_DISTANCE = mindist, $ + DEBLEND = deblend, DEBLOST = deblost, $ + N_ITER = niter, /SILENT, $ + X_INPUT = x_input, Y_INPUT = y_input, F_INPUT = f_input, $ + FLUXOPT = fluxopt, $ + x, y, f, sx, sy, sf, c, STARS = *(*data).stars + nstars = n_elements(f) + msg = dialog_message(/INFO, strcompress(string(nstars)) + ' detected stars.') + if nstars ne 0 then begin + *(*data).x = [x] & *(*data).sx = [sx] + *(*data).y = [y] & *(*data).sy = [sy] + *(*data).f = [f] & *(*data).sf = [sf] + *(*data).c = [c] + endif + endelse + end + id.sav: begin + if n_elements(*(*data).f) ne 0 then begin + file = dialog_pickfile(/WRITE, FILTER = '*.txt', $ + PATH = *(*data).path, GET_PATH = path) + if file ne '' then begin + widget_control, /HOURGLASS + out = [transpose(*(*data).x), $ + transpose(*(*data).y), $ + transpose(*(*data).f), $ + transpose(*(*data).sx), $ + transpose(*(*data).sy), $ + transpose(*(*data).sf), $ + transpose(*(*data).c)] + if strpos(file, '.txt') lt 0 then file = file + '.txt' + *(*data).path = path + openw, lun, file, /GET_LUN, WIDTH = 200 + printf,lun, '# x y f x_err y_err f_err corr ' + printf, lun, out & free_lun, lun + endif + endif + end + id.hlp: $ + xdispfile, file_name('starfinder', 'xstarfinder_run_help.txt'), $ + TITLE = 'XStarFinder_Run help', /MODAL + id.ex: begin + widget_control, child, SET_UVALUE = data, /NO_COPY + widget_control, event.top, /DESTROY + end + else: + endcase + if event_id ne id.ex then $ + widget_control, child, SET_UVALUE = data, /NO_COPY + return +end + +; XSTARFINDER_RUN_DEF: define data structure. + +FUNCTION xstarfinder_run_def, id, par, image, psf, background, back_box, noise_std, $ + x_bad, y_bad, psf_fwhm, path, loadfile + + data = {id: id, par: par, path: ptr_new(path), loadfile: ptr_new(loadfile), $ + image: ptr_new(image, /NO_COPY), $ + psf: ptr_new(psf, /NO_COPY), psf_fwhm: psf_fwhm, $ + background: ptr_new(background, /NO_COPY), back_box: back_box, $ + noise_std: ptr_new(noise_std, /NO_COPY), $ + stars: ptr_new(/ALLOCATE), $ + x_bad: ptr_new(x_bad, /NO_COPY), y_bad: ptr_new(y_bad, /NO_COPY), $ + x: ptr_new(/ALLOCATE), sx: ptr_new(/ALLOCATE), $ + y: ptr_new(/ALLOCATE), sy: ptr_new(/ALLOCATE), $ + f: ptr_new(/ALLOCATE), sf: ptr_new(/ALLOCATE), c: ptr_new(/ALLOCATE)} + return, data +end + +; XSTARFINDER_RUN_DEL: de-reference and de-allocate heap variables. + +PRO xstarfinder_run_del, data, image, psf, background, back_box, noise_std, x_bad, y_bad, $ + stars, x, y, f, sx, sy, sf, c, path, loadfile, par + + if not ptr_valid(data) then begin + heap_gc & return + endif + if n_elements(*(*data).image) ne 0 then $ + image = *(*data).image + if n_elements(*(*data).psf) ne 0 then $ + psf = *(*data).psf + if n_elements(*(*data).background) ne 0 then $ + background = *(*data).background + back_box = (*data).back_box + if n_elements(*(*data).noise_std) ne 0 then $ + noise_std = *(*data).noise_std + if n_elements(*(*data).x_bad) ne 0 then begin + x_bad = *(*data).x_bad & y_bad = *(*data).y_bad + endif + if n_elements(*(*data).stars) ne 0 then $ + stars = *(*data).stars + if n_elements(*(*data).f) ne 0 then begin + x = *(*data).x & sx = *(*data).sx + y = *(*data).y & sy = *(*data).sy + f = *(*data).f & sf = *(*data).sf + c = *(*data).c + endif + if n_elements(*(*data).path) ne 0 then path = *(*data).path + if n_elements(*(*data).loadfile) ne 0 then loadfile = *(*data).loadfile + par = (*data).par + ptr_free, (*data).image, (*data).psf, (*data).background, $ + (*data).noise_std, (*data).x_bad, (*data).y_bad, $ + (*data).stars, (*data).x, (*data).y, (*data).f, $ + (*data).sx, (*data).sy, (*data).sf, (*data).c, $ + (*data).path, (*data).loadfile + ptr_free, data + return +end + +; XSTARFINDER_RUN_PAR: define default parameters. + +PRO xstarfinder_run_par, id, noise_def, back_box, par + + if n_elements(par) ne 0 then begin + dthresh = par.dthresh + cthresh = par.cthresh + csub = par.csub + noise = (par.noise eq 1 and noise_def) and 1B + rel_thresh = (par.rel_thresh eq 1 and noise) and 1B + bkg = par.bkg + bbox = back_box + bmeth = par.bmeth + slant = par.slant + mindist = par.mindist + deblend = par.deblend + deblost = par.deblost + niter = par.niter + fluxopt = par.fluxopt + endif else begin + if noise_def then dthresh = '3., 3.' else dthresh = '0., 0.' + rel_thresh = noise_def + cthresh = 0.7 + csub = 2 + noise = noise_def + bkg = 1B + bbox = back_box + bmeth = 0 + slant = 1B + mindist = 1.0 + deblend = 0 + deblost = 0 + niter = 2 + fluxopt = 0 + par = {dthresh: dthresh, rel_thresh: rel_thresh, $ + cthresh: cthresh, csub: csub, noise: noise, $ + bkg: bkg, bbox: bbox, bmeth: bmeth, slant: slant, mindist: mindist, $ + niter: niter, deblend: deblend, deblost: deblost, fluxopt: fluxopt} +; bkg: bkg, bbox: bbox, slant: slant, mindist: mindist, niter: niter} +; bbox: bbox, deblend: deblend, niter: niter} + endelse + widget_control, id.dthresh, SET_VALUE = {tag0: strcompress(string(dthresh), /REMOVE_ALL)} + widget_control, id.rel_thresh, SET_VALUE = rel_thresh, SENSITIVE = noise and 1B + widget_control, id.cthresh, SET_VALUE = {tag0: strcompress(string(cthresh), /REMOVE_ALL)} + widget_control, id.csub, SET_VALUE = {tag0: strcompress(string(csub), /REMOVE_ALL)} + widget_control, id.noise, SET_VALUE = noise, SENSITIVE = noise_def + widget_control, id.bkg, SET_VALUE = bkg + widget_control, id.bbox, SET_VALUE = {tag0: strcompress(string(bbox), /REMOVE_ALL)}, SENSITIVE = bkg and 1B + widget_control, id.bmeth, SET_VALUE = bmeth, SENSITIVE = bkg and 1B + widget_control, id.slant, SET_VALUE = slant + widget_control, id.mindist, SET_VALUE = {tag0: strcompress(string(mindist), /REMOVE_ALL)} + widget_control, id.deblend, SET_VALUE = deblend + widget_control, id.deblost, SET_VALUE = deblost + widget_control, id.niter, SET_VALUE = {tag0: strcompress(string(niter), /REMOVE_ALL)} + widget_control, id.fluxopt, SET_VALUE = fluxopt, SENSITIVE = 0B + return +end + +; XSTARFINDER_RUN: XStarFinder_Run widget definition module. + +PRO xstarfinder_run, image, psf, psf_fwhm, background, back_box, $ + X_BAD = x_bad, Y_BAD = y_bad, NOISE_STD = noise_std, $ + STARS = stars, x, y, f, sx, sy, sf, c, DEFAULT_PAR = par, $ + PATH = path, GROUP = group, UVALUE = uvalue +; PATH = path, LOADFILE = loadfile, GROUP = group, UVALUE = uvalue + + catch, error + if error ne 0 then begin + xstarfinder_run_del, data, image, psf, background, back_box, noise_std, x_bad, y_bad, $ + stars, x, y, f, sx, sy, sf, c, path, loadfile, par + if n_elements(group) eq 0 then $ + widget_control, group_id, /DESTROY + return + endif + ; Create group leader if necessary + if n_elements(group) eq 0 then $ + group_id = widget_base() else group_id = group + ; Create modal base + if n_elements(uvalue) eq 0 then uvalue = 0B + base = widget_base(TITLE = 'XStarFinder_Run', /MODAL, UVALUE = uvalue, $ + GROUP_LEADER = group_id, /COLUMN) + ; Define child, to store data structure + child = widget_base(base) + ; Define Search parameters + lab_s = widget_label(base, VALUE = 'Search:', /ALIGN_LEFT) + s_base = widget_base(base, /FRAME, /COLUMN) + dthresh = cw_form(s_base, '0,TEXT,,WIDTH=16,LABEL_LEFT=Detection threshold(s)') + rel_thresh = cw_bgroup(s_base, 'Relative threshold', /NONEXCLUSIVE, /ROW) + ; Define Correlation parameters + lab_c = widget_label(base, VALUE = 'Correlation:', /ALIGN_LEFT) + c_base = widget_base(base, /FRAME, /COLUMN) + cthresh = cw_form(c_base, '0,FLOAT,,WIDTH=8,LABEL_LEFT=Correlation threshold') + csub = cw_form(c_base, '0,INTEGER,,WIDTH=8,LABEL_LEFT=No. of sub-pixel offsets') + ; Define Noise and Background parameters + lab_o = widget_label(base, VALUE = 'Noise and background:', /ALIGN_LEFT) + o_base = widget_base(base, /FRAME, /COLUMN) + noise = cw_bgroup(o_base, 'Use noise estimate', /NONEXCLUSIVE) + bkg = cw_bgroup(o_base, 'Upgrade background', /NONEXCLUSIVE) + bbox = cw_form(o_base, '0,INTEGER,,WIDTH=8,LABEL_LEFT=Box size for background estimation (FWHM units)') + bmeth = cw_bgroup(o_base, 'Estimate background by median filtering', /NONEXCLUSIVE) + lab_f = widget_label(base, VALUE = 'Fitting and deblending:', /ALIGN_LEFT) + f_base = widget_base(base, /FRAME, /COLUMN) + slant = cw_bgroup(f_base, 'Fit background below sources', /NONEXCLUSIVE) + mindist = cw_form(f_base, '0,FLOAT,,WIDTH=8,LABEL_LEFT=Minimum distance of close sources (FWHM units)') + deblend = cw_bgroup(f_base, 'Deblend detected sources', /NONEXCLUSIVE) + deblost = cw_bgroup(f_base, 'Deblend rejected sources', /NONEXCLUSIVE) + niter = cw_form(f_base, '0,INTEGER,,WIDTH=8,LABEL_LEFT=Final re-fitting iterations') + fluxopt = cw_bgroup(f_base, 'Fit only fluxes', /NONEXCLUSIVE) + u_base1 = widget_base(base, /ROW) + load = widget_button(u_base1, VALUE = 'Load list') + proc = widget_button(u_base1, VALUE = 'Processing') + sav = widget_button(u_base1, VALUE = 'Save results') + u_base2 = widget_base(base, /ROW) + hlp = widget_button(u_base2, VALUE = 'Help') + ex = widget_button(u_base2, VALUE = 'Exit') + ; Define pointer to auxiliary/output data + id = {dthresh: dthresh, rel_thresh: rel_thresh, $ + cthresh: cthresh, csub: csub, noise: noise, $ + bkg: bkg, bbox: bbox, bmeth: bmeth, slant: slant, mindist: mindist, niter: niter, $ + deblend: deblend, deblost: deblost, fluxopt: fluxopt, $ +; bbox: bbox, deblend: deblend, niter: niter, $ + load: load, proc: proc, sav: sav, hlp: hlp, ex: ex} + xstarfinder_run_par, id, n_elements(noise_std) ne 0 and 1B, back_box, par + data = xstarfinder_run_def(id, par, image, psf, background, back_box, noise_std, $ + x_bad, y_bad, psf_fwhm, path, loadfile) + data = ptr_new(data, /NO_COPY) + widget_control, child, SET_UVALUE = data + ; Realize, register, etc. + widget_control, base, /REALIZE + xmanager, 'xstarfinder_run', base, EVENT_HANDLER = 'xstarfinder_run_event' + ; De-reference output data and de-allocate heap variables + xstarfinder_run_del, data, image, psf, background, back_box, noise_std, x_bad, y_bad, $ + stars, x, y, f, sx, sy, sf, c, path, loadfile, par + ; Destroy group leader if necessary + if n_elements(group) eq 0 then $ + widget_control, group_id, /DESTROY + return +end diff --git a/xstarfinder_run_help.txt b/xstarfinder_run_help.txt new file mode 100644 index 0000000000000000000000000000000000000000..4795eeff81ff81d9c9a4c219c2195fd84140fa3f --- /dev/null +++ b/xstarfinder_run_help.txt @@ -0,0 +1,227 @@ + XStarFinder_Run help page + + + + GENERAL DESCRIPTION + + Given a stellar field and an estimate of the Point Spread + Function (PSF), the XStarFinder_Run application detects the + stellar sources and estimate their position and flux. The image + may be contaminated by a smooth background emission. + + The basic analysis procedure consists of 3 phases: + 1) detection of candidate stars above a given threshold in the + background-removed image + 2) check and analysis of detected objects, sorted by + decreasing intensity + 3) re-fitting + The analysis of each object (step 2) includes the following + steps: + - object re-identification, after subtraction of already known + stars, to reject spurious detections associated to PSF features + of brighter sources + - correlation check, to measure the similarity of the object + with the PSF + - local fitting, to determine position and flux; fitting takes + into account the contribution of other stars; the contribution + of the local background may be taken into account by fitting a + tilted plane or as a fixed contribution, taken from the current + background map + - upgrading of a 'stellar field model', which contains a + copy of the PSF for each detected star; it is basically + used to take into account the contribution of bright sources + when analyzing fainter and fainter ones. + + The procedure above (phases 1-3) may be iterated: a new list of + objects is formed by searching in the image after subtraction of + the previously detected stars. Then the analysis proceeds on the + original frame. This iteration is very useful to detect close + binaries and resolve crowded groups, down to separations + comparable to the PSF FWHM. + + At the end of the last iteration an optional de-blending + strategy may be applied to look for very close sources. This + strategy skips the correlation check. + Two deblending modes are available: deblending of detected sources + (looking for close companions around previously detected sources) and + deblending of rejected sources (trying to resolve previously lost + objects into groups of close companions). + + At the end of the analysis, all the detected sources are re-fitted + a given number of times to improve astrometry and photometry. + + If the approximate positions and fluxes are known, it is possible + to load a list of sources, skipping the detection and analysis + steps 1)+2) described above. In this case, only step 3) is performed + and the input sources are just fitted a pre-fixed number of times. The + outcome of the fitting process of each source undergoes the same + validation checks carried out in a normal fitting process (i.e. flux + above threshold, distance between two close sources above minimum + distance). If the fit of a given source fails, the source is + removed from the list. + + + + PARAMETERS + + 'Detection threshold(s)': + Enter one or more detection thresholds separated by + commas. + The number of thresholds specifies also the number of + iterations of the basic analysis procedure (see steps 1, + 2, 3 in the GENERAL DESCRIPTION). + A detection threshold represents the minimum central + intensity of an acceptable star, after removing the local + background contribution. + An effective choice is to select two detection levels, + both equal to (3 * Sigma), where Sigma is an estimate of + the noise standard deviation. + A threshold of 0 should always be avoided. + + 'Relative threshold': + If this option is set, the detection threshold is considered + as a relative threshold, in units of the noise standard + deviation. This button is active only if the noise is + defined on input, i.e. if the noise computation procedure + has been run before using this application. + + 'Correlation threshold': + Scalar value, representing the minimum correlation to accept + an object. + + 'No. of sub-pixel offsets': + The correlation of the object with the PSF is affected by the + relative off-centering between the two patterns; this parameter + specifies the number of sub-pixel offsets to maximize the + correlation. + + 'Use noise estimate': + Set this option to use the input noise array (or the noise + estimated on the data) to perform a weighted PSF fitting on + the presumed objects. When this option is applied, an estimate + of the formal errors on astrometry and photometry will be + computed. + + 'Upgrade background': + Set this option to upgrade the background estimate (see next + parameter for details). If this option is disabled, the + background map is kept fixed, equal to the input value. + + 'Box size for background estimation': + An accurate background estimate is necessary for accurate + objects detection and for a more reliable computation of + the correlation coefficient of each object with the PSF. + By default the background is estimated on the current residual + image (input image - stars detected so far) by interpolating an + array of local measurements, carried out on a set of image + sub-regions arranged in a regular grid. This parameter + specifies the size (in units of PSF FWHM) of each sub-region. + This parameter is set by default to the same value that has + been used in the PSF extraction procedure (XPsf_Extract). + + 'Estimate background by median filtering' + Set this option to estimate the background by median filtering + of the current residual image, instead of using the default + method explained above. The filtering is carried out on the + same box size defined above. + Estimating the background by median filtering may be useful + when the background contribution is very irregular. This + method should be applied after a first run of the program: + at the very first run, this method may overestimate the + background below bright sources. + Typically this method for background estimation is slower + than the default method. + + 'Fit background below sources': + Set this option to take into account the local background + by means of a tilted plane, which is optimized together with + the parameters of the source(s) being fit. If this option is + not set, the local background estimate is derived from the + background map described above (see previous parameter for + details) and kept fixed in the fitting process. + + 'Minimum distance of close sources': + Very close sources are fit together. A check is performed + on the outcome of the fit: if the sources are closer than + this distance (in units of PSF FHWM), then the fit is + considered unacceptable and the last detected source in the + group is rejected as a false detection. Usually a minimum + distance equal to the PSF FWHM is a safe limit. A smaller + threshold should be used only when the PSF estimate is very + accurate and in good signal-to-noise conditions. + It is recommended to set this parameter to 0 (or to a small + value) when an input list of sources is loaded for fitting. + + 'Deblend detected sources' + Set this option to look for close companions around + previously detected sources that have not been found by + iterating the basic procedure (steps 1, 2, 3) described above. + Candidate sources above the detection threshold are just fit + (no correlation check is carried out). + + 'Deblend rejected sources' + Set this option to try to resolve previously lost objects + into blends of close companions. In some cases, in fact, a + blend of e.g. two close sources may not pass the correlation + check and may be simply lost. Candidate sources above the + detection threshold are just fit (no correlation check is + carried out). + + 'Final re-fitting iterations': + At the end of each iteration (see GENERAL DESCRIPTION), + all the currently known stars are re-fitted once. + At the end of the whole analysis, the re-fitting may be + further iterated to improve the astrometry and photometry. + This parameter is to set the number of final re-fitting + iterations. The default is 2. + If an input list of sources is loaded, this parameters defines + the number of fitting iterations of these sources. In this case + more than 2 iterations are usually required (e.g. 4-5). + + 'Fit only fluxes': + Set this option to fit only the fluxes of the candidate stars + when an input list of sources is loaded. NOTE: this option + is active only when an input list is loaded. + + + + CONTROLS/BUTTONS + + 'Load list': + Load an input list of stars. The list must be in an ASCII file + with three columns: X position, Y position, Flux. + The positions are in pixels, the flux is referred to the PSF. + When a list is loaded, the program will not perform a new + detection: the loaded sources will be just fit a number of time + given by the parameter 'Final re-fitting iterations'. + NOTE 1: the file name is kept in memory until the XStarFinder_Run + task is closed. Every time the 'Processing' button is pressed to + run the task, the input list is loaded again from the file. + NOTE 2: when an input list is loaded, some parameters of the GUI + are disabled: these parameters are not used when an input list + is loaded for fitting. + + 'Processing': + Analyze stellar field, applying the currently defined options. + + 'Save results': + Save list of stars on ASCII file. The output list is organized + as a 7 columns table, with the following format: + + X Y Flux Sigma_X Sigma_Y Sigma_Flux Correlation + + where + - X, Y are the coordinates of a star in pixels + - Flux is the total flux + - Sigma_X, Sigma_Y, Sigma_F are the formal error estimates + on X, Y, Flux respectively + - Correlation is the correlation coefficient of the star; + notice that the correlation coefficient of a star + found by de-blending (if applied) is set to -1 + + + 'Help': + Display this help page. + + 'Exit': + Quit XStarFinder_Run. \ No newline at end of file